1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2018 Rafał Miłecki <rafal@milecki.pl> |
4 | */ |
5 | |
6 | #include <linux/err.h> |
7 | #include <linux/io.h> |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> |
10 | #include <linux/pinctrl/pinconf-generic.h> |
11 | #include <linux/pinctrl/pinctrl.h> |
12 | #include <linux/pinctrl/pinmux.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/property.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include "../core.h" |
18 | #include "../pinmux.h" |
19 | |
20 | #define FLAG_BCM4708 BIT(1) |
21 | #define FLAG_BCM4709 BIT(2) |
22 | #define FLAG_BCM53012 BIT(3) |
23 | |
24 | struct ns_pinctrl { |
25 | struct device *dev; |
26 | unsigned int chipset_flag; |
27 | struct pinctrl_dev *pctldev; |
28 | void __iomem *base; |
29 | |
30 | struct pinctrl_desc pctldesc; |
31 | }; |
32 | |
33 | /* |
34 | * Pins |
35 | */ |
36 | |
37 | static const struct pinctrl_pin_desc ns_pinctrl_pins[] = { |
38 | { 0, "spi_clk" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
39 | { 1, "spi_ss" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
40 | { 2, "spi_mosi" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
41 | { 3, "spi_miso" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
42 | { 4, "i2c_scl" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
43 | { 5, "i2c_sda" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
44 | { 6, "mdc" , (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, |
45 | { 7, "mdio" , (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, |
46 | { 8, "pwm0" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
47 | { 9, "pwm1" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
48 | { 10, "pwm2" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
49 | { 11, "pwm3" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
50 | { 12, "uart1_rx" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
51 | { 13, "uart1_tx" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
52 | { 14, "uart1_cts" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
53 | { 15, "uart1_rts" , (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, |
54 | { 16, "uart2_rx" , (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, |
55 | { 17, "uart2_tx" , (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, |
56 | /* TODO { ??, "xtal_out", (void *)(FLAG_BCM4709) }, */ |
57 | { 22, "sdio_pwr" , (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, |
58 | { 23, "sdio_en_1p8v" , (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, |
59 | }; |
60 | |
61 | /* |
62 | * Groups |
63 | */ |
64 | |
65 | struct ns_pinctrl_group { |
66 | const char *name; |
67 | unsigned int *pins; |
68 | const unsigned int num_pins; |
69 | unsigned int chipsets; |
70 | }; |
71 | |
72 | static unsigned int spi_pins[] = { 0, 1, 2, 3 }; |
73 | static unsigned int i2c_pins[] = { 4, 5 }; |
74 | static unsigned int mdio_pins[] = { 6, 7 }; |
75 | static unsigned int pwm0_pins[] = { 8 }; |
76 | static unsigned int pwm1_pins[] = { 9 }; |
77 | static unsigned int pwm2_pins[] = { 10 }; |
78 | static unsigned int pwm3_pins[] = { 11 }; |
79 | static unsigned int uart1_pins[] = { 12, 13, 14, 15 }; |
80 | static unsigned int uart2_pins[] = { 16, 17 }; |
81 | static unsigned int sdio_pwr_pins[] = { 22 }; |
82 | static unsigned int sdio_1p8v_pins[] = { 23 }; |
83 | |
84 | #define NS_GROUP(_name, _pins, _chipsets) \ |
85 | { \ |
86 | .name = _name, \ |
87 | .pins = _pins, \ |
88 | .num_pins = ARRAY_SIZE(_pins), \ |
89 | .chipsets = _chipsets, \ |
90 | } |
91 | |
92 | static const struct ns_pinctrl_group ns_pinctrl_groups[] = { |
93 | NS_GROUP("spi_grp" , spi_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
94 | NS_GROUP("i2c_grp" , i2c_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
95 | NS_GROUP("mdio_grp" , mdio_pins, FLAG_BCM4709 | FLAG_BCM53012), |
96 | NS_GROUP("pwm0_grp" , pwm0_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
97 | NS_GROUP("pwm1_grp" , pwm1_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
98 | NS_GROUP("pwm2_grp" , pwm2_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
99 | NS_GROUP("pwm3_grp" , pwm3_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
100 | NS_GROUP("uart1_grp" , uart1_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
101 | NS_GROUP("uart2_grp" , uart2_pins, FLAG_BCM4709 | FLAG_BCM53012), |
102 | NS_GROUP("sdio_pwr_grp" , sdio_pwr_pins, FLAG_BCM4709 | FLAG_BCM53012), |
103 | NS_GROUP("sdio_1p8v_grp" , sdio_1p8v_pins, FLAG_BCM4709 | FLAG_BCM53012), |
104 | }; |
105 | |
106 | /* |
107 | * Functions |
108 | */ |
109 | |
110 | struct ns_pinctrl_function { |
111 | const char *name; |
112 | const char * const *groups; |
113 | const unsigned int num_groups; |
114 | unsigned int chipsets; |
115 | }; |
116 | |
117 | static const char * const spi_groups[] = { "spi_grp" }; |
118 | static const char * const i2c_groups[] = { "i2c_grp" }; |
119 | static const char * const mdio_groups[] = { "mdio_grp" }; |
120 | static const char * const pwm_groups[] = { "pwm0_grp" , "pwm1_grp" , "pwm2_grp" , |
121 | "pwm3_grp" }; |
122 | static const char * const uart1_groups[] = { "uart1_grp" }; |
123 | static const char * const uart2_groups[] = { "uart2_grp" }; |
124 | static const char * const sdio_groups[] = { "sdio_pwr_grp" , "sdio_1p8v_grp" }; |
125 | |
126 | #define NS_FUNCTION(_name, _groups, _chipsets) \ |
127 | { \ |
128 | .name = _name, \ |
129 | .groups = _groups, \ |
130 | .num_groups = ARRAY_SIZE(_groups), \ |
131 | .chipsets = _chipsets, \ |
132 | } |
133 | |
134 | static const struct ns_pinctrl_function ns_pinctrl_functions[] = { |
135 | NS_FUNCTION("spi" , spi_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
136 | NS_FUNCTION("i2c" , i2c_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
137 | NS_FUNCTION("mdio" , mdio_groups, FLAG_BCM4709 | FLAG_BCM53012), |
138 | NS_FUNCTION("pwm" , pwm_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
139 | NS_FUNCTION("uart1" , uart1_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), |
140 | NS_FUNCTION("uart2" , uart2_groups, FLAG_BCM4709 | FLAG_BCM53012), |
141 | NS_FUNCTION("sdio" , sdio_groups, FLAG_BCM4709 | FLAG_BCM53012), |
142 | }; |
143 | |
144 | /* |
145 | * Groups code |
146 | */ |
147 | |
148 | static const struct pinctrl_ops ns_pinctrl_ops = { |
149 | .get_groups_count = pinctrl_generic_get_group_count, |
150 | .get_group_name = pinctrl_generic_get_group_name, |
151 | .get_group_pins = pinctrl_generic_get_group_pins, |
152 | .dt_node_to_map = pinconf_generic_dt_node_to_map_group, |
153 | .dt_free_map = pinconf_generic_dt_free_map, |
154 | }; |
155 | |
156 | /* |
157 | * Functions code |
158 | */ |
159 | |
160 | static int ns_pinctrl_set_mux(struct pinctrl_dev *pctrl_dev, |
161 | unsigned int func_select, |
162 | unsigned int group_selector) |
163 | { |
164 | struct ns_pinctrl *ns_pinctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
165 | struct group_desc *group; |
166 | u32 unset = 0; |
167 | u32 tmp; |
168 | int i; |
169 | |
170 | group = pinctrl_generic_get_group(pctldev: pctrl_dev, group_selector); |
171 | if (!group) |
172 | return -EINVAL; |
173 | |
174 | for (i = 0; i < group->grp.npins; i++) |
175 | unset |= BIT(group->grp.pins[i]); |
176 | |
177 | tmp = readl(addr: ns_pinctrl->base); |
178 | tmp &= ~unset; |
179 | writel(val: tmp, addr: ns_pinctrl->base); |
180 | |
181 | return 0; |
182 | } |
183 | |
184 | static const struct pinmux_ops ns_pinctrl_pmxops = { |
185 | .get_functions_count = pinmux_generic_get_function_count, |
186 | .get_function_name = pinmux_generic_get_function_name, |
187 | .get_function_groups = pinmux_generic_get_function_groups, |
188 | .set_mux = ns_pinctrl_set_mux, |
189 | }; |
190 | |
191 | /* |
192 | * Controller code |
193 | */ |
194 | |
195 | static struct pinctrl_desc ns_pinctrl_desc = { |
196 | .name = "pinctrl-ns" , |
197 | .pctlops = &ns_pinctrl_ops, |
198 | .pmxops = &ns_pinctrl_pmxops, |
199 | }; |
200 | |
201 | static const struct of_device_id ns_pinctrl_of_match_table[] = { |
202 | { .compatible = "brcm,bcm4708-pinmux" , .data = (void *)FLAG_BCM4708, }, |
203 | { .compatible = "brcm,bcm4709-pinmux" , .data = (void *)FLAG_BCM4709, }, |
204 | { .compatible = "brcm,bcm53012-pinmux" , .data = (void *)FLAG_BCM53012, }, |
205 | { } |
206 | }; |
207 | |
208 | static int ns_pinctrl_probe(struct platform_device *pdev) |
209 | { |
210 | struct device *dev = &pdev->dev; |
211 | struct ns_pinctrl *ns_pinctrl; |
212 | struct pinctrl_desc *pctldesc; |
213 | struct pinctrl_pin_desc *pin; |
214 | struct resource *res; |
215 | int i; |
216 | |
217 | ns_pinctrl = devm_kzalloc(dev, size: sizeof(*ns_pinctrl), GFP_KERNEL); |
218 | if (!ns_pinctrl) |
219 | return -ENOMEM; |
220 | pctldesc = &ns_pinctrl->pctldesc; |
221 | platform_set_drvdata(pdev, data: ns_pinctrl); |
222 | |
223 | /* Set basic properties */ |
224 | |
225 | ns_pinctrl->dev = dev; |
226 | |
227 | ns_pinctrl->chipset_flag = (uintptr_t)device_get_match_data(dev); |
228 | |
229 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
230 | "cru_gpio_control" ); |
231 | ns_pinctrl->base = devm_ioremap_resource(dev, res); |
232 | if (IS_ERR(ptr: ns_pinctrl->base)) |
233 | return PTR_ERR(ptr: ns_pinctrl->base); |
234 | |
235 | memcpy(pctldesc, &ns_pinctrl_desc, sizeof(*pctldesc)); |
236 | |
237 | /* Set pinctrl properties */ |
238 | |
239 | pctldesc->pins = devm_kcalloc(dev, ARRAY_SIZE(ns_pinctrl_pins), |
240 | size: sizeof(struct pinctrl_pin_desc), |
241 | GFP_KERNEL); |
242 | if (!pctldesc->pins) |
243 | return -ENOMEM; |
244 | for (i = 0, pin = (struct pinctrl_pin_desc *)&pctldesc->pins[0]; |
245 | i < ARRAY_SIZE(ns_pinctrl_pins); i++) { |
246 | const struct pinctrl_pin_desc *src = &ns_pinctrl_pins[i]; |
247 | unsigned int chipsets = (uintptr_t)src->drv_data; |
248 | |
249 | if (chipsets & ns_pinctrl->chipset_flag) { |
250 | memcpy(pin++, src, sizeof(*src)); |
251 | pctldesc->npins++; |
252 | } |
253 | } |
254 | |
255 | /* Register */ |
256 | |
257 | ns_pinctrl->pctldev = devm_pinctrl_register(dev, pctldesc, driver_data: ns_pinctrl); |
258 | if (IS_ERR(ptr: ns_pinctrl->pctldev)) { |
259 | dev_err(dev, "Failed to register pinctrl\n" ); |
260 | return PTR_ERR(ptr: ns_pinctrl->pctldev); |
261 | } |
262 | |
263 | for (i = 0; i < ARRAY_SIZE(ns_pinctrl_groups); i++) { |
264 | const struct ns_pinctrl_group *group = &ns_pinctrl_groups[i]; |
265 | |
266 | if (!(group->chipsets & ns_pinctrl->chipset_flag)) |
267 | continue; |
268 | |
269 | pinctrl_generic_add_group(pctldev: ns_pinctrl->pctldev, name: group->name, |
270 | pins: group->pins, num_pins: group->num_pins, NULL); |
271 | } |
272 | |
273 | for (i = 0; i < ARRAY_SIZE(ns_pinctrl_functions); i++) { |
274 | const struct ns_pinctrl_function *function = &ns_pinctrl_functions[i]; |
275 | |
276 | if (!(function->chipsets & ns_pinctrl->chipset_flag)) |
277 | continue; |
278 | |
279 | pinmux_generic_add_function(pctldev: ns_pinctrl->pctldev, name: function->name, |
280 | groups: function->groups, |
281 | num_groups: function->num_groups, NULL); |
282 | } |
283 | |
284 | return 0; |
285 | } |
286 | |
287 | static struct platform_driver ns_pinctrl_driver = { |
288 | .probe = ns_pinctrl_probe, |
289 | .driver = { |
290 | .name = "ns-pinmux" , |
291 | .of_match_table = ns_pinctrl_of_match_table, |
292 | }, |
293 | }; |
294 | |
295 | module_platform_driver(ns_pinctrl_driver); |
296 | |
297 | MODULE_AUTHOR("Rafał Miłecki" ); |
298 | MODULE_DEVICE_TABLE(of, ns_pinctrl_of_match_table); |
299 | |