linux kernel 平臺匯流排例項分析
阿新 • • 發佈:2019-01-14
linux 平臺匯流排的實現有三大塊 , platform bus , platform device , platform drvice
平臺型別結構體:
1 2 /** 3 * struct bus_type - The bus type of the device4 * 5 * @name: The name of the bus. 6 * @bus_attrs: Default attributes of the bus. 7 * @dev_attrs: Default attributes of the devices on the bus.8 * @drv_attrs: Default attributes of the device drivers on the bus. 9 * @match: Called, perhaps multiple times, whenever a new device or driver 10 * is added for this bus. It should return a nonzero value if the 11 * given device can be handled by the given driver.12 * @uevent: Called when a device is added, removed, or a few other things 13 * that generate uevents to add the environment variables. 14 * @probe: Called when a new device or driver add to this bus, and callback 15 * the specific driver's probe to initial the matched device. 16 * @remove: Called when a device removed from this bus. 17 * @shutdown: Called at shut-down time to quiesce the device. 18 * @suspend: Called when a device on this bus wants to go to sleep mode. 19 * @resume: Called to bring a device on this bus out of sleep mode. 20 * @pm: Power management operations of this bus, callback the specific 21 * device driver's pm-ops. 22 * @p: The private data of the driver core, only the driver core can 23 * touch this. 24 * 25 * A bus is a channel between the processor and one or more devices. For the 26 * purposes of the device model, all devices are connected via a bus, even if 27 * it is an internal, virtual, "platform" bus. Buses can plug into each other. 28 * A USB controller is usually a PCI device, for example. The device model 29 * represents the actual connections between buses and the devices they control. 30 * A bus is represented by the bus_type structure. It contains the name, the 31 * default attributes, the bus' methods, PM operations, and the driver core's 32 * private data. 33 */
1 struct bus_type { 2 const char *name; 3 struct bus_attribute *bus_attrs; // 匯流排屬性 4 struct device_attribute *dev_attrs; // 裝置屬性 5 struct driver_attribute *drv_attrs; // 驅動性 6 int (*match)(struct device *dev, struct device_driver *drv); //匹配平臺裝置與平臺驅動的函式 7 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); 8 int (*probe)(struct device *dev); 9 int (*remove)(struct device *dev); 10 void (*shutdown)(struct device *dev); 11 12 int (*suspend)(struct device *dev, pm_message_t state); 13 int (*resume)(struct device *dev); 14 15 const struct dev_pm_ops *pm; 16 17 struct subsys_private *p; 18 };
參考: http://blog.chinaunix.net/uid-25622207-id-2778126.html
在kernel中 , 首先是有platform bus被kernel 註冊
有以下流程:
init/main.c
1 static int __init kernel_init(void * unused) 2 { 3 ...... 4 do_basic_setup(); 5 ...... 6 } 7 8 //再到 9 static void __init do_basic_setup(void) 10 { 11 cpuset_init_smp(); 12 usermodehelper_init(); 13 init_tmpfs(); 14 driver_init(); 15 init_irq_proc(); 16 do_ctors(); 17 do_initcalls(); 18 }
再進 driver_init()
1 void __init driver_init(void) 2 { 3 /* These are the core pieces */ 4 devtmpfs_init(); 5 devices_init(); 6 buses_init(); 7 classes_init(); 8 firmware_init(); 9 hypervisor_init(); 10 11 /* These are also core pieces, but must come after the 12 * core core pieces. 13 */ 14 platform_bus_init(); //平臺匯流排初始化 15 system_bus_init(); 16 cpu_dev_init(); 17 memory_dev_init(); 18 }
再進平臺匯流排初始化
1 struct device platform_bus = { 2 .init_name = "platform", 3 }; 4 EXPORT_SYMBOL_GPL(platform_bus); 5 //... 6 7 struct bus_type platform_bus_type = { 8 .name = "platform", 9 .dev_attrs = platform_dev_attrs, 10 .match = platform_match, 11 .uevent = platform_uevent, 12 .pm = &platform_dev_pm_ops, 13 }; 14 EXPORT_SYMBOL_GPL(platform_bus_type); 15 16 //... 17 int __init platform_bus_init(void) 18 { 19 int error; 20 21 early_platform_cleanup(); 22 23 error = device_register(&platform_bus); //將平臺匯流排作為一個設備註冊 24 if (error) 25 return error; 26 error = bus_register(&platform_bus_type); 27 if (error) 28 device_unregister(&platform_bus); 29 return error; 30 }
match 函式是等下匹配platform device 和 platform driver 的函式
1 /**
2 * platform_match - bind platform device to platform driver.
3 * @dev: device.
4 * @drv: driver.
5 *
6 * Platform device IDs are assumed to be encoded like this:
7 * "<name><instance>", where <name> is a short description of the type of
8 * device, like "pci" or "floppy", and <instance> is the enumerated
9 * instance of the device, like '0' or '42'. Driver IDs are simply
10 * "<name>". So, extract the <name> from the platform_device structure,
11 * and compare it against the name of the driver. Return whether they match
12 * or not.
13 */
14
15 static int platform_match(struct device *dev, struct device_driver *drv)
16 {
17 struct platform_device *pdev = to_platform_device(dev);
18 struct platform_driver *pdrv = to_platform_driver(drv);
19
20 /* Attempt an OF style match first */
21 if (of_driver_match_device(dev, drv))
22 return 1;
23
24 /* Then try to match against the id table */
25 if (pdrv->id_table)
26 return platform_match_id(pdrv->id_table, pdev) != NULL;
27
28 /* fall-back to driver name match */
29 return (strcmp(pdev->name, drv->name) == 0);
30 }
//通過id_table 來匹配
1 static const struct platform_device_id *platform_match_id(
2 const struct platform_device_id *id,
3 struct platform_device *pdev)
4 {
5 while (id->name[0]) {
6 if (strcmp(pdev->name, id->name) == 0) {
7 pdev->id_entry = id;
8 return id;
9 }
10 id++;
11 }
12 return NULL;
13 }
1 int device_register(struct device *dev) 2 { 3 device_initialize(dev); 4 return device_add(dev); 5 }
此時platform bus 已經作為一個裝置掛到核心上
看完平臺匯流排, 下面看平臺裝置的註冊流程:
還是得把一開始的platform device 結構體放到這裡來:
1 2 struct platform_device { 3 const char * name; //平臺裝置名字 4 int id; 5 struct device dev; //裝置結構體 6 u32 num_resources; //資源個數 7 struct resource * resource; //資源 8 9 const struct platform_device_id *id_entry; 10 11 /* MFD cell pointer */ 12 struct mfd_cell *mfd_cell; 13 14 /* arch specific additions */ 15 struct pdev_archdata archdata; 16 }; 17 18 #define platform_get_device_id(pdev) ((pdev)->id_entry) 19 //通過device 找到對應的platform_device 結構體 20 #define to_platform_device(x) container_of((x), struct platform_device, dev)
其實這裡邊還有個裝置結構體 , 他是依附在平臺裝置裡面 1 /** 2 * struct device - The basic device structure 3 * @parent: The device's "parent" device, the device to which it is attached. 4 * In most cases, a parent device is some sort of bus or host 5 * controller. If parent is NULL, the device, is a top-level device, 6 * which is not usually what you want. 7 * @p: Holds the private data of the driver core portions of the device. 8 * See the comment of the struct device_private for detail. 9 * @kobj: A top-level, abstract class from which other classes are derived. 10 * @init_name: Initial name of the device. 11 * @type: The type of device. 12 * This identifies the device type and carries type-specific 13 * information. 14 * @mutex: Mutex to synchronize calls to its driver. 15 * @bus: Type of bus device is on. 16 * @driver: Which driver has allocated this 17 * @platform_data: Platform data specific to the device. 18 * Example: For devices on custom boards, as typical of embedded 19 * and SOC based hardware, Linux often uses platform_data to point 20 * to board-specific structures describing devices and how they 21 * are wired. That can include what ports are available, chip 22 * variants, which GPIO pins act in what additional roles, and so 23 * on. This shrinks the "Board Support Packages" (BSPs) and 24 * minimizes board-specific #ifdefs in drivers. 25 * @power: For device power management. 26 * See Documentation/power/devices.txt for details. 27 * @pwr_domain: Provide callbacks that are executed during system suspend, 28 * hibernation, system resume and during runtime PM transitions 29 * along with subsystem-level and driver-level callbacks. 30 * @numa_node: NUMA node this device is close to. 31 * @dma_mask: Dma mask (if dma'ble device). 32 * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all 33 * hardware supports 64-bit addresses for consistent allocations 34 * such descriptors. 35 * @dma_parms: A low level driver may set these to teach IOMMU code about 36 * segment limitations. 37 * @dma_pools: Dma pools (if dma'ble device). 38 * @dma_mem: Internal for coherent mem override. 39 * @archdata: For arch-specific additions. 40 * @of_node: Associated device tree node. 41 * @devt: For creating the sysfs "dev". 42 * @devres_lock: Spinlock to protect the resource of the device. 43 * @devres_head: The resources list of the device. 44 * @knode_class: The node used to add the device to the class list. 45 * @class: The class of the device. 46 * @groups: Optional attribute groups. 47 * @release: Callback to free the device after all references have 48 * gone away. This should be set by the allocator of the 49 * device (i.e. the bus driver that discovered the device). 50 * 51 * At the lowest level, every device in a Linux system is represented by an 52 * instance of struct device. The device structure contains the information 53 * that the device model core needs to model the system. Most subsystems, 54 * however, track additional information about the devices they host. As a 55 * result, it is rare for devices to be represented by bare device structures; 56 * instead, that structure, like kobject structures, is usually embedded within 57 * a higher-level representation of the device. 58 */ 1 struct device { 2 struct device *parent; // 裝置的父類 3 4 struct device_private *p; //裝置的私有資料 5 6 struct kobject kobj; 7 const char *init_name; /* initial name of the device */ 8 const struct device_type *type; 9 10 struct mutex mutex; /* mutex to synchronize calls to 11 * its driver. 12 */ 13 //表示該裝置是屬於哪一條匯流排 14 struct bus_type *bus; /* type of bus device is on */ 15 struct device_driver *driver; /* which driver has allocated this 16 device */ // 哪個驅動呼叫了這個裝置 17 void *platform_data; /* Platform specific data, device 18 core doesn't touch it ***********/ 19 struct dev_pm_info power; 20 struct dev_power_domain *pwr_domain; 21 22 #ifdef CONFIG_NUMA 23 int numa_node; /* NUMA node this device is close to */ 24 #endif 25 u64 *dma_mask; /* dma mask (if dma'able device) */ 26 u64 coherent_dma_mask;/* Like dma_mask, but for 27 alloc_coherent mappings as 28 not all hardware supports 29 64 bit addresses for consistent 30 allocations such descriptors. */ 31 32 struct device_dma_parameters *dma_parms; 33 34 struct list_head dma_pools; /* dma pools (if dma'ble) */ 35 36 struct dma_coherent_mem *dma_mem; /* internal for coherent mem 37 override */ 38 /* arch specific additions */ 39 struct dev_archdata archdata; 40 41 struct device_node *of_node; /* associated device tree node */ 42 43 dev_t devt; /* dev_t, creates the sysfs "dev" */ //主次裝置號的>結合體 44 45 spinlock_t devres_lock; 46 struct list_head devres_head; 47 48 struct klist_node knode_class; 49 struct class *class; 50 const struct attribute_group **groups; /* optional groups */ 51 52 void (*release)(struct device *dev); 53 };
1 //平臺設備註冊函式 2 extern int platform_device_register(struct platform_device *); 3 extern void platform_device_unregister(struct platform_device *); 4 //平臺裝置解註冊函式
這兩個函式對外發布
i2c 裝置就用到了這個 函式進行 platform device 的 register
在arch/arm/mach-mx6/board-mx6q_sabresd.c 中
1 MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board") /* Maintainer: Freescale Semiconductor, Inc. */ 2 .boot_params = MX6_PHYS_OFFSET + 0x100, 3 .fixup = fixup_mxc_board, 4 .map_io = mx6_map_io, 5 .init_irq = mx6_init_irq, 6 .init_machine = mx6_sabresd_board_init, // 板級的初始化 7 .timer = &mx6_sabresd_timer, // 時鐘的初始化 8 .reserve = mx6q_sabresd_reserve, 9 MACHINE_END
時鐘的話跟過去得到 i2c 的 CLK 的速度是400KB/s
在mx6_sabresd_board_init 中
平臺裝置內裝置的平臺數據進行賦值
1 static struct i2c_gpio_platform_data i2c_bus_gpio_data = { 2 .sda_pin = SABRESD_I2C4_SDA_GPIO, 3 .scl_pin = SABRESD_I2C4_SCL_GPIO, 4 .udelay = 10, //10Khz 5 .timeout = 500, 6 //.sda_is_open_drain = 1, //在當前板子上不能加 7 //.scl_is_open_drain = 1, //在當前板子上不能加 8 };
平臺裝置結構體的賦值
1 static struct platform_device i2c_bus_gpio_device = { 2 .name = "i2c-gpio", //這個名字是必須這樣,主要是為了和i2c-gpio驅動對應 3 //由於板子已經用掉了0,1,2號,這裡使用3 4 .id = 3, /* bus have 0,1,2, so start at 3 */ 5 .dev = { 6 .platform_data = &i2c_bus_gpio_data, 7 } 8 };
板級的初始化:
1 static void __init mx6_sabresd_board_init(void) 2 { 3 4 //。。。。。。 5 6 /** 7 * register gpio i2c bus write by zengjf 8 * 註冊i2c-gpio裝置,相當於註冊一個I2C控制器 9 */ 10 platform_device_register(&i2c_bus_gpio_device); 11 12 i2c_register_board_info(0, mxc_i2c0_board_info, 13 ARRAY_SIZE(mxc_i2c0_board_info)); 14 i2c_register_board_info(1, mxc_i2c1_board_info, 15 ARRAY_SIZE(mxc_i2c1_board_info)); 16 i2c_register_board_info(2, mxc_i2c2_board_info, 17 ARRAY_SIZE(mxc_i2c2_board_info)); 18 /** 19 * register gpio i2c device write by zengjf 20 * 在I2C控制器3上註冊I2C裝置,這裡的控制器3就是前面註冊的I2C控制器, 21 * 主要是因為前面註冊的I2C控制器的id是3 22 */ 23 i2c_register_board_info(3, gpio_i2c_devices, ARRAY_SIZE(gpio_i2c_devices)); 24 25 //。。。。。。 26 27 }
在mx6這塊板子身上 , i2c 0 , 1 , 2 是比較正常的i2c 匯流排, 但是i2c 3 是用的兩個GPIO口模擬的 i2c 的SCL和 SDA線
下面 , 跟進
1 platform_device_register(&i2c_bus_gpio_device);
/drivers/base/platform.c
1 /** 2 * platform_device_register - add a platform-level device 3 * @pdev: platform device we're adding 4 */ 5 int platform_device_register(struct platform_device *pdev) 6 { 7 device_initialize(&pdev->dev); //初始化裝置 8 return platform_device_add(pdev); //平臺裝置新增 9 } 10 EXPORT_SYMBOL_GPL(platform_device_register);
跟進 platform_device_add(pdev)
1 /** 2 * platform_device_add - add a platform device to device hierarchy 3 * @pdev: platform device we're adding 4 * 5 * This is part 2 of platform_device_register(), though may be called 6 * separately _iff_ pdev was allocated by platform_device_alloc(). 7 */ 8 int platform_device_add(struct platform_device *pdev) 9 { 10 int i, ret = 0; 11 12 if (!pdev) 13 return -EINVAL; 14 15 if (!pdev->dev.parent) 16 pdev->dev.parent = &platform_bus; //屬於平臺匯流排, 17 18 pdev->dev.bus = &platform_bus_type; // 在platform_bus_init()的時候已經註冊了平臺匯流排型別 19 20 if (pdev->id != -1) 21 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); //pdev->dev 只有一個platform_data , pdev->name == i2c-gpio , pdev->id == 3 22 else 23 dev_set_name(&pdev->dev, "%s", pdev->