前言
以前基于 3.18 追的的 s3c24xx 系列的流程,因为对这个芯片熟。。。
本文解析的设备树:
///
// // 串口模块
// // s3c24xx.dtsi
// uart0: serial@50000000 {
// compatible = "samsung,s3c2410-uart";
// reg = <0x50000000 0x4000>; // ULCON0 0x50000000
// interrupts = <1 28 0 4>, <1 28 1 4>;
// status = "disabled";
// };
//
// // s3c2416.dtsi
// serial@50000000 { // ULCON0 0x50000000
// compatible = "samsung,s3c2440-uart";
// clock-names = "uart", "clk_uart_baud2",
// "clk_uart_baud3";
// clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,
// <&clocks SCLK_UART>;
// };
dts 注册平台设备
// Setup.c (z:\work\e266l_cmcc\kernel-3.18\arch\arm64\kernel)
arch_initcall_sync(arm64_device_init);
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
// 遍历根节点的每个一次子节点
for_each_child_of_node(root, child)
rc = of_platform_bus_create(child, matches, lookup, parent, true);
// 【核心】如果不是ARM Primecell Peripherals,那么我们就需要向 platform bus 上增加一个 platform device 了。
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
///
// of_device_alloc 除了分配 struct platform_device 的内存,还分配了该 platform device 需要
// 的 resource 的内存。当然,这就需要解析该 device node 的 interrupt 资源以及 memory address
// 资源
/
///
// // 串口模块
// // s3c24xx.dtsi
// uart0: serial@50000000 {
// compatible = "samsung,s3c2410-uart";
// reg = <0x50000000 0x4000>; // ULCON0 0x50000000
// interrupts = <1 28 0 4>, <1 28 1 4>;
// status = "disabled";
// };
//
// // s3c2416.dtsi
// serial@50000000 { // ULCON0 0x50000000
// compatible = "samsung,s3c2440-uart";
// clock-names = "uart", "clk_uart_baud2",
// "clk_uart_baud3";
// clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,
// <&clocks SCLK_UART>;
// };
dev = of_device_alloc(np, bus_id, parent);
///
// 分配 platform_device 设备
dev = platform_device_alloc("", -1);
///
// 计算 io/irq 资源数量
// of_address_to_resource(): 解析 reg 字段,返回的资源保存在 temp_res 中
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
// 解析中断 = 0?
num_irq = of_irq_count(np);
# 填充平台设备的资源表:中断、内存
if (num_irq || num_reg)
// 进行 HW 硬件中断号与 IRQ 中断号映射
of_irq_to_resource_table(np, res, num_irq)
。。。
domain->ops->xlate(domain, irq_data->np, irq_data->args,irq_data->args_count, &hwirq, &type)
// Irq-s3c24xx.c (kernel-3.18\drivers\irqchip)
###############################################################################################
# xlate 函数就是将指定的设备(node参数)上若干个(intsize参数)中断属性(intspec参数)翻译成HW
# interrupt ID(out_hwirq参数)和trigger类型(out_type)
// 例:
// uart0{
// ...
// interrupts = <1 28 0 4>, <1 28 1 4>;
// 解析方式:
// 1: 次级中断控制器 SUBSRCPND
// 28: 位于主控制器 SRCPND[28]
// 0/1: 位于 SUBSRCPND[0-1]
// }
// 我们已经通过 compatible 属性找到了适合的 interrupt controller,那么如何解析 reg 属性呢?我们知道,对于
// s3c2416 的 interrupt controller 而言,其 #interrupt-cells 的属性值是 4,定义为。每个域的解释如下:
// (1)ctrl_num 表示使用哪一种类型的 interrupt controller,其值的解释如下:
// - 0 ... main controller
// - 1 ... sub controller
// - 2 ... second main controller
// (2)parent_irq。对于 sub controller,parent_irq 标识了其在 main controller 的 bit position。
// (3)ctrl_irq 标识了在 controller 中的 bit 位置。
// (4)type 标识了该中断的 trigger type,例如:上升沿触发还是电平触发。
s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,const u32 *intspec, unsigned int intsize,irq_hw_number_t *out_hwirq, unsigned int *out_type)
//
// Array holding pointers to the global controller structs
// [0] ... main_intc
// [1] ... sub_intc
// [2] ... main_intc2 on s3c2416
//
// static struct s3c_irq_intc *s3c_intc[3];
intc = s3c_intc[intspec[0]];
// 将 DTS 中的配置转换为硬件中断号
*out_hwirq = intspec[0] * 32 + intspec[2];
// 转换中断电平:
// IRQ_TYPE_NONE = 0x00000000,
// IRQ_TYPE_EDGE_RISING = 0x00000001,
// IRQ_TYPE_EDGE_FALLING = 0x00000002,
// IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
// IRQ_TYPE_LEVEL_HIGH = 0x00000004,
// IRQ_TYPE_LEVEL_LOW = 0x00000008,
// IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
。。。
// 注册到域中,中断域可参考 DTS 在 s3c2416 中有
// 创建HW interrupt ID和IRQ number的映射关系
virq = irq_create_mapping(domain, hwirq);
# 调用irq domain的map callback函数
ret = domain->ops->map(domain, virq, hwirq);
// Irq-s3c24xx.c (kernel-3.18\drivers\irqchip)
s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,irq_hw_number_t hw)
irq_set_chip_and_handler(virq, &s3c_irq_level_chip,handle_edge_irq);
irq_set_chip_and_handler_name(irq, chip, handle, NULL);
irq_set_chip(irq, chip);
desc->irq_data.chip = chip;
__irq_set_handler(irq, handle, 0, name);
desc->handle_irq = handle;
# DMA 操作
of_dma_configure(&dev->dev);
使用连续的 DMA 分配需求, 设置平台的 DMA 操作函数 */
if (of_dma_is_coherent(dev->of_node)) {
// Dma-mapping.h (kernel-3.18\arch\arm\include\asm)
set_arch_dma_coherent_ops(dev);
// struct dma_map_ops arm_coherent_dma_ops = {
// .alloc = arm_coherent_dma_alloc,
// .free = arm_coherent_dma_free,
// .mmap = arm_dma_mmap,
// .get_sgtable = arm_dma_get_sgtable,
// .map_page = arm_coherent_dma_map_page,
// .map_sg = arm_dma_map_sg,
// .set_dma_mask = arm_dma_set_mask,
// };
set_dma_ops(dev, &arm_coherent_dma_ops);
平台资源获取
// 获得 IRQ 中断号,此时已经经过了 DTS ==》 HW ID => IRQ 的绑定了
platform_get_irq(struct platform_device *dev, unsigned int num)
// 查找映射
ret = of_irq_get(dev->dev.of_node, num);
rc = of_irq_parse_one(dev, index, &oirq);
if (rc)
return rc;
// 如果没有则手动映射下
domain = irq_find_host(oirq.np);
return irq_create_of_mapping(&oirq);
// 获得资源,设置中断电平
r = platform_get_resource(dev, IORESOURCE_IRQ, num);
if (r && r->flags & IORESOURCE_BITS)
irqd_set_trigger_type(irq_get_irq_data(r->start),r->flags & IORESOURCE_BITS);
platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;
}
platform_bus 总线注册
//Main.c (z:\work\e266l_cmcc\kernel-3.18\init)
start_kernel(void)
rest_init(void)
kernel_thread(kernel_init, NULL, CLONE_FS);
kernel_init(void *unused)
kernel_init_freeable(void)
do_basic_setup(void)
driver_init()
/
// 创建 sys 下的目录节点
devices_init();
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
dev_kobj = kobject_create_and_add("dev", NULL);
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
buses_init();
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
///
// 初始化平台总线
platform_bus_init();
/
// 仅仅是注册了一个总线的设备,因为此时还
// 没有总线,所以很快返回了
// struct device platform_bus = {
// .init_name = "platform",
// };
error = device_register(&platform_bus);
device_initialize(dev);
return device_add(dev);
//
// 注册总线,主要是创建了一系统 /sys 下目录
// struct bus_type platform_bus_type = {
// .name = "platform",
// .dev_groups = platform_dev_groups,
// .match = platform_match,
// .uevent = platform_uevent,
// .pm = &platform_dev_pm_ops,
// };
error = bus_register(&platform_bus_type);
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
retval = bus_create_file(bus, &bus_attr_uevent);
priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
retval = add_probe_files(bus);
retval = bus_create_file(bus, &bus_attr_drivers_probe);
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
# platform device 注册流程
// Platform.c (z:\work\e266l_cmcc\kernel-3.18\drivers\base)
platform_device_register(struct platform_device *pdev)
ret = platform_device_add(pdev);
pdev->dev.bus = &platform_bus_type;
// 从资源树中分配资源
for (i = 0; i < pdev->num_resources; i++)
struct resource *p, *r = &pdev->resource[i];
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
insert_resource(p, r)
ret = device_add(&pdev->dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
///
// 如果设备已经绑定了驱动
if (dev->driver)
klist_node_attached(&dev->p->knode_driver)
ret = device_bind_driver(dev);
//
// 如果当前设备没有绑定驱动
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
//
// Dd.c (z:\work\e266l_cmcc\kernel-3.18\drivers\base)
__device_attach(struct device_driver *drv, void *data)
/
// 检查能不能匹配上:
// 1. 检查 dts 匹配
// 2. 根据 id_table 匹配
// 3. 根据名称匹配
if (!driver_match_device(drv, dev))return 0;
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
//
// 调用总线匹配函数
platform_match(struct device *dev, struct device_driver *drv)
// 匹配 dts 中是否有相应项
if (of_driver_match_device(dev, drv))return 1;
return of_match_device(drv->of_match_table, dev) != NULL;
return of_match_node(matches, dev->of_node);
match = __of_match_node(matches, node);
for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++)
score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name);
if (compat && compat[0])
prop = __of_find_property(device, "compatible", NULL);
//
// 根据 id_table 匹配
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
// 根据名称进行匹配
return (strcmp(pdev->name, drv->name) == 0);
/
// 如果有,首先调用总线的 probe(), 否则调用匹配的驱动的 probe()
return driver_probe_device(drv, dev);
ret = really_probe(dev, drv);
ret = pinctrl_bind_pins(dev);
//
// 调用总线的 probe()
if (dev->bus->probe)
ret = dev->bus->probe(dev);
//
// 调用驱动的 probe()
else if (drv->probe)
ret = drv->probe(dev);
}
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
platform drvier 注册流程
platform_driver_register(drv)
__platform_driver_register(drv, THIS_MODULE)
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
ret = bus_add_driver(drv);
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
//
// Dd.c (z:\work\e266l_cmcc\kernel-3.18\drivers\base)
__driver_attach(struct device *dev, void *data)
/
// 检查能不能匹配上:
// 1. 检查 dts 匹配
// 2. 根据 id_table 匹配
// 3. 根据名称匹配
if (!driver_match_device(drv, dev))return 0;
/
// 如果有,首先调用总线的 probe(), 否则调用匹配的驱动的 probe()
if (!dev->driver)
driver_probe_device(drv, dev);
ret = really_probe(dev, drv);
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);