]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
44d5c371 FB |
2 | /* |
3 | * Copyright (C) EETS GmbH, 2017, Felix Brack <[email protected]> | |
44d5c371 FB |
4 | */ |
5 | ||
6 | #include <common.h> | |
9d922450 | 7 | #include <dm.h> |
336d4615 | 8 | #include <dm/device_compat.h> |
44d5c371 | 9 | #include <dm/pinctrl.h> |
b08c8c48 | 10 | #include <linux/libfdt.h> |
44d5c371 FB |
11 | #include <asm/io.h> |
12 | ||
4ace4fa6 DB |
13 | /** |
14 | * struct single_pdata - platform data | |
15 | * @base: first configuration register | |
16 | * @offset: index of last configuration register | |
17 | * @mask: configuration-value mask bits | |
18 | * @width: configuration register bit width | |
19 | * @bits_per_mux: true if one register controls more than one pin | |
20 | */ | |
44d5c371 | 21 | struct single_pdata { |
4ace4fa6 DB |
22 | fdt_addr_t base; |
23 | int offset; | |
24 | u32 mask; | |
971c64a4 | 25 | u32 width; |
159a887e | 26 | bool bits_per_mux; |
44d5c371 FB |
27 | }; |
28 | ||
4ace4fa6 DB |
29 | /** |
30 | * struct single_fdt_pin_cfg - pin configuration | |
31 | * | |
32 | * This structure is used for the pin configuration parameters in case | |
33 | * the register controls only one pin. | |
34 | * | |
35 | * @reg: configuration register offset | |
36 | * @val: configuration register value | |
37 | */ | |
44d5c371 | 38 | struct single_fdt_pin_cfg { |
4ace4fa6 DB |
39 | fdt32_t reg; |
40 | fdt32_t val; | |
44d5c371 FB |
41 | }; |
42 | ||
4ace4fa6 DB |
43 | /** |
44 | * struct single_fdt_bits_cfg - pin configuration | |
45 | * | |
46 | * This structure is used for the pin configuration parameters in case | |
47 | * the register controls more than one pin. | |
48 | * | |
49 | * @reg: configuration register offset | |
50 | * @val: configuration register value | |
51 | * @mask: configuration register mask | |
52 | */ | |
159a887e | 53 | struct single_fdt_bits_cfg { |
4ace4fa6 DB |
54 | fdt32_t reg; |
55 | fdt32_t val; | |
56 | fdt32_t mask; | |
159a887e AF |
57 | }; |
58 | ||
180531fc DB |
59 | static unsigned int single_read(struct udevice *dev, fdt_addr_t reg) |
60 | { | |
61 | struct single_pdata *pdata = dev_get_plat(dev); | |
62 | ||
63 | switch (pdata->width) { | |
64 | case 8: | |
65 | return readb(reg); | |
66 | case 16: | |
67 | return readw(reg); | |
68 | default: /* 32 bits */ | |
69 | return readl(reg); | |
70 | } | |
71 | ||
72 | return readb(reg); | |
73 | } | |
74 | ||
75 | static void single_write(struct udevice *dev, unsigned int val, fdt_addr_t reg) | |
76 | { | |
77 | struct single_pdata *pdata = dev_get_plat(dev); | |
78 | ||
79 | switch (pdata->width) { | |
80 | case 8: | |
81 | writeb(val, reg); | |
82 | break; | |
83 | case 16: | |
84 | writew(val, reg); | |
85 | break; | |
86 | default: /* 32 bits */ | |
87 | writel(val, reg); | |
88 | } | |
89 | } | |
90 | ||
44d5c371 FB |
91 | /** |
92 | * single_configure_pins() - Configure pins based on FDT data | |
93 | * | |
94 | * @dev: Pointer to single pin configuration device which is the parent of | |
95 | * the pins node holding the pin configuration data. | |
96 | * @pins: Pointer to the first element of an array of register/value pairs | |
97 | * of type 'struct single_fdt_pin_cfg'. Each such pair describes the | |
98 | * the pin to be configured and the value to be used for configuration. | |
99 | * This pointer points to a 'pinctrl-single,pins' property in the | |
100 | * device-tree. | |
101 | * @size: Size of the 'pins' array in bytes. | |
102 | * The number of register/value pairs in the 'pins' array therefore | |
103 | * equals to 'size / sizeof(struct single_fdt_pin_cfg)'. | |
104 | */ | |
105 | static int single_configure_pins(struct udevice *dev, | |
106 | const struct single_fdt_pin_cfg *pins, | |
107 | int size) | |
108 | { | |
0fd3d911 | 109 | struct single_pdata *pdata = dev_get_plat(dev); |
67192946 DB |
110 | int n, count = size / sizeof(struct single_fdt_pin_cfg); |
111 | phys_addr_t reg; | |
9b884e79 | 112 | u32 offset, val; |
44d5c371 | 113 | |
d85b93e8 DB |
114 | /* If function mask is null, needn't enable it. */ |
115 | if (!pdata->mask) | |
116 | return 0; | |
117 | ||
46f51dc9 | 118 | for (n = 0; n < count; n++, pins++) { |
9b884e79 DB |
119 | offset = fdt32_to_cpu(pins->reg); |
120 | if (offset < 0 || offset > pdata->offset) { | |
121 | dev_dbg(dev, " invalid register offset 0x%x\n", | |
122 | offset); | |
44d5c371 FB |
123 | continue; |
124 | } | |
9b884e79 DB |
125 | |
126 | reg = pdata->base + offset; | |
46f51dc9 | 127 | val = fdt32_to_cpu(pins->val) & pdata->mask; |
180531fc DB |
128 | single_write(dev, (single_read(dev, reg) & ~pdata->mask) | val, |
129 | reg); | |
fcf6a2b3 | 130 | dev_dbg(dev, " reg/val %pa/0x%08x\n", ®, val); |
180531fc | 131 | |
44d5c371 FB |
132 | } |
133 | return 0; | |
134 | } | |
135 | ||
159a887e AF |
136 | static int single_configure_bits(struct udevice *dev, |
137 | const struct single_fdt_bits_cfg *pins, | |
138 | int size) | |
139 | { | |
0fd3d911 | 140 | struct single_pdata *pdata = dev_get_plat(dev); |
67192946 DB |
141 | int n, count = size / sizeof(struct single_fdt_bits_cfg); |
142 | phys_addr_t reg; | |
9b884e79 | 143 | u32 offset, val, mask; |
159a887e AF |
144 | |
145 | for (n = 0; n < count; n++, pins++) { | |
9b884e79 DB |
146 | offset = fdt32_to_cpu(pins->reg); |
147 | if (offset < 0 || offset > pdata->offset) { | |
148 | dev_dbg(dev, " invalid register offset 0x%x\n", | |
149 | offset); | |
159a887e AF |
150 | continue; |
151 | } | |
9b884e79 DB |
152 | |
153 | reg = pdata->base + offset; | |
159a887e AF |
154 | |
155 | mask = fdt32_to_cpu(pins->mask); | |
156 | val = fdt32_to_cpu(pins->val) & mask; | |
180531fc | 157 | single_write(dev, (single_read(dev, reg) & ~mask) | val, reg); |
fcf6a2b3 | 158 | dev_dbg(dev, " reg/val %pa/0x%08x\n", ®, val); |
159a887e AF |
159 | } |
160 | return 0; | |
161 | } | |
44d5c371 FB |
162 | static int single_set_state(struct udevice *dev, |
163 | struct udevice *config) | |
164 | { | |
44d5c371 | 165 | const struct single_fdt_pin_cfg *prop; |
159a887e | 166 | const struct single_fdt_bits_cfg *prop_bits; |
44d5c371 FB |
167 | int len; |
168 | ||
dbfd9e0e | 169 | prop = dev_read_prop(config, "pinctrl-single,pins", &len); |
159a887e | 170 | |
44d5c371 FB |
171 | if (prop) { |
172 | dev_dbg(dev, "configuring pins for %s\n", config->name); | |
173 | if (len % sizeof(struct single_fdt_pin_cfg)) { | |
174 | dev_dbg(dev, " invalid pin configuration in fdt\n"); | |
175 | return -FDT_ERR_BADSTRUCTURE; | |
176 | } | |
177 | single_configure_pins(dev, prop, len); | |
159a887e | 178 | return 0; |
44d5c371 FB |
179 | } |
180 | ||
159a887e | 181 | /* pinctrl-single,pins not found so check for pinctrl-single,bits */ |
dbfd9e0e | 182 | prop_bits = dev_read_prop(config, "pinctrl-single,bits", &len); |
159a887e AF |
183 | if (prop_bits) { |
184 | dev_dbg(dev, "configuring pins for %s\n", config->name); | |
185 | if (len % sizeof(struct single_fdt_bits_cfg)) { | |
186 | dev_dbg(dev, " invalid bits configuration in fdt\n"); | |
187 | return -FDT_ERR_BADSTRUCTURE; | |
188 | } | |
189 | single_configure_bits(dev, prop_bits, len); | |
190 | return 0; | |
191 | } | |
192 | ||
193 | /* Neither 'pinctrl-single,pins' nor 'pinctrl-single,bits' were found */ | |
44d5c371 FB |
194 | return len; |
195 | } | |
196 | ||
d1998a9f | 197 | static int single_of_to_plat(struct udevice *dev) |
44d5c371 FB |
198 | { |
199 | fdt_addr_t addr; | |
9fd8a430 | 200 | fdt_size_t size; |
0fd3d911 | 201 | struct single_pdata *pdata = dev_get_plat(dev); |
971c64a4 | 202 | int ret; |
44d5c371 | 203 | |
971c64a4 DB |
204 | ret = dev_read_u32(dev, "pinctrl-single,register-width", &pdata->width); |
205 | if (ret) { | |
206 | dev_err(dev, "missing register width\n"); | |
207 | return ret; | |
208 | } | |
44d5c371 | 209 | |
180531fc DB |
210 | switch (pdata->width) { |
211 | case 8: | |
212 | case 16: | |
213 | case 32: | |
214 | break; | |
215 | default: | |
216 | dev_err(dev, "wrong register width\n"); | |
217 | return -EINVAL; | |
218 | } | |
219 | ||
9fd8a430 DB |
220 | addr = dev_read_addr_size(dev, "reg", &size); |
221 | if (addr == FDT_ADDR_T_NONE) { | |
222 | dev_err(dev, "failed to get base register size\n"); | |
223 | return -EINVAL; | |
224 | } | |
225 | ||
226 | pdata->offset = size - pdata->width / BITS_PER_BYTE; | |
44d5c371 | 227 | |
719cab6d | 228 | addr = dev_read_addr(dev); |
44d5c371 FB |
229 | if (addr == FDT_ADDR_T_NONE) { |
230 | dev_dbg(dev, "no valid base register address\n"); | |
231 | return -EINVAL; | |
232 | } | |
233 | pdata->base = addr; | |
234 | ||
d85b93e8 DB |
235 | ret = dev_read_u32(dev, "pinctrl-single,function-mask", &pdata->mask); |
236 | if (ret) { | |
237 | pdata->mask = 0; | |
238 | dev_warn(dev, "missing function register mask\n"); | |
239 | } | |
240 | ||
719cab6d | 241 | pdata->bits_per_mux = dev_read_bool(dev, "pinctrl-single,bit-per-mux"); |
159a887e | 242 | |
44d5c371 FB |
243 | return 0; |
244 | } | |
245 | ||
246 | const struct pinctrl_ops single_pinctrl_ops = { | |
247 | .set_state = single_set_state, | |
248 | }; | |
249 | ||
250 | static const struct udevice_id single_pinctrl_match[] = { | |
251 | { .compatible = "pinctrl-single" }, | |
252 | { /* sentinel */ } | |
253 | }; | |
254 | ||
255 | U_BOOT_DRIVER(single_pinctrl) = { | |
256 | .name = "single-pinctrl", | |
257 | .id = UCLASS_PINCTRL, | |
258 | .of_match = single_pinctrl_match, | |
259 | .ops = &single_pinctrl_ops, | |
caa4daa2 | 260 | .plat_auto = sizeof(struct single_pdata), |
d1998a9f | 261 | .of_to_plat = single_of_to_plat, |
44d5c371 | 262 | }; |