如果在dts中有如下节点,
dsaf0: dsa@c7000000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "hisilicon,hns-dsaf-v2";
mode = "6port-16rss";
reg = <0x0 0xc5000000 0x0 0x890000
0x0 0xc7000000 0x0 0x600000>;
reg-names = "ppe-base", "dsaf-base";
interrupt-parent = <&mbigen_dsaf0>;
subctrl-syscon = <&dsa_subctrl>;
}
我们可以通过of_parse_phandle 就能拿到subctrl-syscon
np_temp = of_parse_phandle(np, "subctrl-syscon", 0);
这里subctrl-syscon的之定义如下:
dsa_subctrl: dsa_subctrl@c0000000 {
compatible = "hisilicon,dsa-subctrl", "syscon";
reg = <0x0 0xc0000000 0x0 0x10000>;
};
然后我们就可以通过syscon_node_to_regmap来为np_temp 做映射,这里的syscon的类型是 struct regmap *syscon;
syscon = syscon_node_to_regmap(np_temp);
这样后续就可以通过写dsa_subctrl 中定义的reg寄存器
dsaf_write_syscon(dsaf_dev->sub_ctrl, reg, val);
static inline void dsaf_write_syscon(struct regmap *base, u32 reg, u32 value)
{
regmap_write(base, reg, value);
}
那下来我们看看syscon_node_to_regmap的实现,其源代码同样在/drivers/mfd/syscon.c 中
struct regmap *syscon_node_to_regmap(struct device_node *np)
118 {
119 struct syscon *entry, *syscon = NULL;
120
121 spin_lock(&syscon_list_slock);
122 //首先在syscon_list 中查找是否已经有syscon,第一次的话,肯定是没有
123 list_for_each_entry(entry, &syscon_list, list)
124 if (entry->np == np) {
125 syscon = entry;
126 break;
127 }
128
129 spin_unlock(&syscon_list_slock);
130 //第一次syscon是空的,调用of_syscon_register
131 if (!syscon)
132 syscon = of_syscon_register(np);
133
134 if (IS_ERR(syscon))
135 return ERR_CAST(syscon);
136
137 return syscon->regmap;
138 }
45 static struct syscon *of_syscon_register(struct device_node *np)
46 {
47 struct syscon *syscon;
48 struct regmap *regmap;
49 void __iomem *base;
50 u32 reg_io_width;
51 int ret;
52 struct regmap_config syscon_config = syscon_regmap_config;
53 struct resource res;
54 //如果dts中不包含syscon,则直接退出
55 if (!of_device_is_compatible(np, "syscon"))
56 return ERR_PTR(-EINVAL);
57 //申请空间,调用kzalloc申请的空间自动清零了,免去额外调用memset清零的动作
58 syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
59 if (!syscon)
60 return ERR_PTR(-ENOMEM);
61 //得到resource 资源,也就是上面dts中定义的reg
62 if (of_address_to_resource(np, 0, &res)) {
63 ret = -ENOMEM;
64 goto err_map;
65 }
66 //对res做虚拟地址的映射
67 base = ioremap(res.start, resource_size(&res));
68 if (!base) {
69 ret = -ENOMEM;
70 goto err_map;
71 }
72 //字符的格式是big-endian/little-endian/native-endian
73 /* Parse the device's DT node for an endianness specification */
74 if (of_property_read_bool(np, "big-endian"))
75 syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
76 else if (of_property_read_bool(np, "little-endian"))
77 syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
78 else if (of_property_read_bool(np, "native-endian"))
79 syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE;
80
81 /*
82 * search for reg-io-width property in DT. If it is not provided,
83 * default to 4 bytes. regmap_init_mmio will return an error if values
84 * are invalid so there is no need to check them here.
85 */
得到寄存器的长度,默认是4个byte
86 ret = of_property_read_u32(np, "reg-io-width", ®_io_width);
87 if (ret)
88 reg_io_width = 4;
89
90 syscon_config.reg_stride = reg_io_width;
91 syscon_config.val_bits = reg_io_width * 8;
92 syscon_config.max_register = resource_size(&res) - reg_io_width;
93
94 regmap = regmap_init_mmio(NULL, base, &syscon_config);
95 if (IS_ERR(regmap)) {
96 pr_err("regmap init failed\n");
97 ret = PTR_ERR(regmap);
98 goto err_regmap;
99 }
100
101 syscon->regmap = regmap;
102 syscon->np = np;
103
104 spin_lock(&syscon_list_slock);
//将这里初始化的syscon 添加到全局变量syscon_list 中,这样下一次调用syscon_node_to_regmap 时就会在syscon_list 中找到了
105 list_add_tail(&syscon->list, &syscon_list);
106 spin_unlock(&syscon_list_slock);
107
108 return syscon;
109
110 err_regmap:
111 iounmap(base);
112 err_map:
113 kfree(syscon);
114 return ERR_PTR(ret);
115 }
继续看regmap_init_mmio
624 #define regmap_init_mmio(dev, regs, config) \
625 regmap_init_mmio_clk(dev, NULL, regs, config)
626
726 #define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \
727 __regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \
728 dev, clk_id, regs, config)
729
前面的文章分析过__regmap_lockdep_wrapper 是空函数,因此devm_regmap_init_mmio_clk 就等于__devm_regmap_init_mmio_clk,而这个函数之前已经分析过了。
dsaf0: dsa@c7000000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "hisilicon,hns-dsaf-v2";
mode = "6port-16rss";
reg = <0x0 0xc5000000 0x0 0x890000
0x0 0xc7000000 0x0 0x600000>;
reg-names = "ppe-base", "dsaf-base";
interrupt-parent = <&mbigen_dsaf0>;
subctrl-syscon = <&dsa_subctrl>;
}
我们可以通过of_parse_phandle 就能拿到subctrl-syscon
np_temp = of_parse_phandle(np, "subctrl-syscon", 0);
这里subctrl-syscon的之定义如下:
dsa_subctrl: dsa_subctrl@c0000000 {
compatible = "hisilicon,dsa-subctrl", "syscon";
reg = <0x0 0xc0000000 0x0 0x10000>;
};
然后我们就可以通过syscon_node_to_regmap来为np_temp 做映射,这里的syscon的类型是 struct regmap *syscon;
syscon = syscon_node_to_regmap(np_temp);
这样后续就可以通过写dsa_subctrl 中定义的reg寄存器
dsaf_write_syscon(dsaf_dev->sub_ctrl, reg, val);
static inline void dsaf_write_syscon(struct regmap *base, u32 reg, u32 value)
{
regmap_write(base, reg, value);
}
那下来我们看看syscon_node_to_regmap的实现,其源代码同样在/drivers/mfd/syscon.c 中
struct regmap *syscon_node_to_regmap(struct device_node *np)
118 {
119 struct syscon *entry, *syscon = NULL;
120
121 spin_lock(&syscon_list_slock);
122 //首先在syscon_list 中查找是否已经有syscon,第一次的话,肯定是没有
123 list_for_each_entry(entry, &syscon_list, list)
124 if (entry->np == np) {
125 syscon = entry;
126 break;
127 }
128
129 spin_unlock(&syscon_list_slock);
130 //第一次syscon是空的,调用of_syscon_register
131 if (!syscon)
132 syscon = of_syscon_register(np);
133
134 if (IS_ERR(syscon))
135 return ERR_CAST(syscon);
136
137 return syscon->regmap;
138 }
45 static struct syscon *of_syscon_register(struct device_node *np)
46 {
47 struct syscon *syscon;
48 struct regmap *regmap;
49 void __iomem *base;
50 u32 reg_io_width;
51 int ret;
52 struct regmap_config syscon_config = syscon_regmap_config;
53 struct resource res;
54 //如果dts中不包含syscon,则直接退出
55 if (!of_device_is_compatible(np, "syscon"))
56 return ERR_PTR(-EINVAL);
57 //申请空间,调用kzalloc申请的空间自动清零了,免去额外调用memset清零的动作
58 syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
59 if (!syscon)
60 return ERR_PTR(-ENOMEM);
61 //得到resource 资源,也就是上面dts中定义的reg
62 if (of_address_to_resource(np, 0, &res)) {
63 ret = -ENOMEM;
64 goto err_map;
65 }
66 //对res做虚拟地址的映射
67 base = ioremap(res.start, resource_size(&res));
68 if (!base) {
69 ret = -ENOMEM;
70 goto err_map;
71 }
72 //字符的格式是big-endian/little-endian/native-endian
73 /* Parse the device's DT node for an endianness specification */
74 if (of_property_read_bool(np, "big-endian"))
75 syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
76 else if (of_property_read_bool(np, "little-endian"))
77 syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
78 else if (of_property_read_bool(np, "native-endian"))
79 syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE;
80
81 /*
82 * search for reg-io-width property in DT. If it is not provided,
83 * default to 4 bytes. regmap_init_mmio will return an error if values
84 * are invalid so there is no need to check them here.
85 */
得到寄存器的长度,默认是4个byte
86 ret = of_property_read_u32(np, "reg-io-width", ®_io_width);
87 if (ret)
88 reg_io_width = 4;
89
90 syscon_config.reg_stride = reg_io_width;
91 syscon_config.val_bits = reg_io_width * 8;
92 syscon_config.max_register = resource_size(&res) - reg_io_width;
93
94 regmap = regmap_init_mmio(NULL, base, &syscon_config);
95 if (IS_ERR(regmap)) {
96 pr_err("regmap init failed\n");
97 ret = PTR_ERR(regmap);
98 goto err_regmap;
99 }
100
101 syscon->regmap = regmap;
102 syscon->np = np;
103
104 spin_lock(&syscon_list_slock);
//将这里初始化的syscon 添加到全局变量syscon_list 中,这样下一次调用syscon_node_to_regmap 时就会在syscon_list 中找到了
105 list_add_tail(&syscon->list, &syscon_list);
106 spin_unlock(&syscon_list_slock);
107
108 return syscon;
109
110 err_regmap:
111 iounmap(base);
112 err_map:
113 kfree(syscon);
114 return ERR_PTR(ret);
115 }
继续看regmap_init_mmio
624 #define regmap_init_mmio(dev, regs, config) \
625 regmap_init_mmio_clk(dev, NULL, regs, config)
626
726 #define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \
727 __regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \
728 dev, clk_id, regs, config)
729
前面的文章分析过__regmap_lockdep_wrapper 是空函数,因此devm_regmap_init_mmio_clk 就等于__devm_regmap_init_mmio_clk,而这个函数之前已经分析过了。