1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Conexant Digicolor General Purpose Pin Mapping |
4 | * |
5 | * Author: Baruch Siach <baruch@tkos.co.il> |
6 | * |
7 | * Copyright (C) 2015 Paradox Innovation Ltd. |
8 | * |
9 | * TODO: |
10 | * - GPIO interrupt support |
11 | * - Pin pad configuration (pull up/down, strength) |
12 | */ |
13 | |
14 | #include <linux/gpio/driver.h> |
15 | #include <linux/init.h> |
16 | #include <linux/io.h> |
17 | #include <linux/mod_devicetable.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/spinlock.h> |
20 | |
21 | #include <linux/pinctrl/machine.h> |
22 | #include <linux/pinctrl/pinconf.h> |
23 | #include <linux/pinctrl/pinconf-generic.h> |
24 | #include <linux/pinctrl/pinctrl.h> |
25 | #include <linux/pinctrl/pinmux.h> |
26 | |
27 | #include "pinctrl-utils.h" |
28 | |
29 | #define DRIVER_NAME "pinctrl-digicolor" |
30 | |
31 | #define GP_CLIENTSEL(clct) ((clct)*8 + 0x20) |
32 | #define GP_DRIVE0(clct) (GP_CLIENTSEL(clct) + 2) |
33 | #define GP_OUTPUT0(clct) (GP_CLIENTSEL(clct) + 3) |
34 | #define GP_INPUT(clct) (GP_CLIENTSEL(clct) + 6) |
35 | |
36 | #define PIN_COLLECTIONS ('R' - 'A' + 1) |
37 | #define PINS_PER_COLLECTION 8 |
38 | #define PINS_COUNT (PIN_COLLECTIONS * PINS_PER_COLLECTION) |
39 | |
40 | struct dc_pinmap { |
41 | void __iomem *regs; |
42 | struct device *dev; |
43 | struct pinctrl_dev *pctl; |
44 | |
45 | struct pinctrl_desc *desc; |
46 | const char *pin_names[PINS_COUNT]; |
47 | |
48 | struct gpio_chip chip; |
49 | spinlock_t lock; |
50 | }; |
51 | |
52 | static int dc_get_groups_count(struct pinctrl_dev *pctldev) |
53 | { |
54 | return PINS_COUNT; |
55 | } |
56 | |
57 | static const char *dc_get_group_name(struct pinctrl_dev *pctldev, |
58 | unsigned selector) |
59 | { |
60 | struct dc_pinmap *pmap = pinctrl_dev_get_drvdata(pctldev); |
61 | |
62 | /* Exactly one group per pin */ |
63 | return pmap->desc->pins[selector].name; |
64 | } |
65 | |
66 | static int dc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, |
67 | const unsigned **pins, |
68 | unsigned *num_pins) |
69 | { |
70 | struct dc_pinmap *pmap = pinctrl_dev_get_drvdata(pctldev); |
71 | |
72 | *pins = &pmap->desc->pins[selector].number; |
73 | *num_pins = 1; |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | static const struct pinctrl_ops dc_pinctrl_ops = { |
79 | .get_groups_count = dc_get_groups_count, |
80 | .get_group_name = dc_get_group_name, |
81 | .get_group_pins = dc_get_group_pins, |
82 | .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, |
83 | .dt_free_map = pinctrl_utils_free_map, |
84 | }; |
85 | |
86 | static const char *const dc_functions[] = { |
87 | "gpio" , |
88 | "client_a" , |
89 | "client_b" , |
90 | "client_c" , |
91 | }; |
92 | |
93 | static int dc_get_functions_count(struct pinctrl_dev *pctldev) |
94 | { |
95 | return ARRAY_SIZE(dc_functions); |
96 | } |
97 | |
98 | static const char *dc_get_fname(struct pinctrl_dev *pctldev, unsigned selector) |
99 | { |
100 | return dc_functions[selector]; |
101 | } |
102 | |
103 | static int dc_get_groups(struct pinctrl_dev *pctldev, unsigned selector, |
104 | const char * const **groups, |
105 | unsigned * const num_groups) |
106 | { |
107 | struct dc_pinmap *pmap = pinctrl_dev_get_drvdata(pctldev); |
108 | |
109 | *groups = pmap->pin_names; |
110 | *num_groups = PINS_COUNT; |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | static void dc_client_sel(int pin_num, int *reg, int *bit) |
116 | { |
117 | *bit = (pin_num % PINS_PER_COLLECTION) * 2; |
118 | *reg = GP_CLIENTSEL(pin_num/PINS_PER_COLLECTION); |
119 | |
120 | if (*bit >= PINS_PER_COLLECTION) { |
121 | *bit -= PINS_PER_COLLECTION; |
122 | *reg += 1; |
123 | } |
124 | } |
125 | |
126 | static int dc_set_mux(struct pinctrl_dev *pctldev, unsigned selector, |
127 | unsigned group) |
128 | { |
129 | struct dc_pinmap *pmap = pinctrl_dev_get_drvdata(pctldev); |
130 | int bit_off, reg_off; |
131 | u8 reg; |
132 | |
133 | dc_client_sel(pin_num: group, reg: ®_off, bit: &bit_off); |
134 | |
135 | reg = readb_relaxed(pmap->regs + reg_off); |
136 | reg &= ~(3 << bit_off); |
137 | reg |= (selector << bit_off); |
138 | writeb_relaxed(reg, pmap->regs + reg_off); |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static int dc_pmx_request_gpio(struct pinctrl_dev *pcdev, |
144 | struct pinctrl_gpio_range *range, |
145 | unsigned offset) |
146 | { |
147 | struct dc_pinmap *pmap = pinctrl_dev_get_drvdata(pctldev: pcdev); |
148 | int bit_off, reg_off; |
149 | u8 reg; |
150 | |
151 | dc_client_sel(pin_num: offset, reg: ®_off, bit: &bit_off); |
152 | |
153 | reg = readb_relaxed(pmap->regs + reg_off); |
154 | if ((reg & (3 << bit_off)) != 0) |
155 | return -EBUSY; |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | static const struct pinmux_ops dc_pmxops = { |
161 | .get_functions_count = dc_get_functions_count, |
162 | .get_function_name = dc_get_fname, |
163 | .get_function_groups = dc_get_groups, |
164 | .set_mux = dc_set_mux, |
165 | .gpio_request_enable = dc_pmx_request_gpio, |
166 | }; |
167 | |
168 | static int dc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) |
169 | { |
170 | struct dc_pinmap *pmap = gpiochip_get_data(gc: chip); |
171 | int reg_off = GP_DRIVE0(gpio/PINS_PER_COLLECTION); |
172 | int bit_off = gpio % PINS_PER_COLLECTION; |
173 | u8 drive; |
174 | unsigned long flags; |
175 | |
176 | spin_lock_irqsave(&pmap->lock, flags); |
177 | drive = readb_relaxed(pmap->regs + reg_off); |
178 | drive &= ~BIT(bit_off); |
179 | writeb_relaxed(drive, pmap->regs + reg_off); |
180 | spin_unlock_irqrestore(lock: &pmap->lock, flags); |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | static void dc_gpio_set(struct gpio_chip *chip, unsigned gpio, int value); |
186 | |
187 | static int dc_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, |
188 | int value) |
189 | { |
190 | struct dc_pinmap *pmap = gpiochip_get_data(gc: chip); |
191 | int reg_off = GP_DRIVE0(gpio/PINS_PER_COLLECTION); |
192 | int bit_off = gpio % PINS_PER_COLLECTION; |
193 | u8 drive; |
194 | unsigned long flags; |
195 | |
196 | dc_gpio_set(chip, gpio, value); |
197 | |
198 | spin_lock_irqsave(&pmap->lock, flags); |
199 | drive = readb_relaxed(pmap->regs + reg_off); |
200 | drive |= BIT(bit_off); |
201 | writeb_relaxed(drive, pmap->regs + reg_off); |
202 | spin_unlock_irqrestore(lock: &pmap->lock, flags); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static int dc_gpio_get(struct gpio_chip *chip, unsigned gpio) |
208 | { |
209 | struct dc_pinmap *pmap = gpiochip_get_data(gc: chip); |
210 | int reg_off = GP_INPUT(gpio/PINS_PER_COLLECTION); |
211 | int bit_off = gpio % PINS_PER_COLLECTION; |
212 | u8 input; |
213 | |
214 | input = readb_relaxed(pmap->regs + reg_off); |
215 | |
216 | return !!(input & BIT(bit_off)); |
217 | } |
218 | |
219 | static void dc_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) |
220 | { |
221 | struct dc_pinmap *pmap = gpiochip_get_data(gc: chip); |
222 | int reg_off = GP_OUTPUT0(gpio/PINS_PER_COLLECTION); |
223 | int bit_off = gpio % PINS_PER_COLLECTION; |
224 | u8 output; |
225 | unsigned long flags; |
226 | |
227 | spin_lock_irqsave(&pmap->lock, flags); |
228 | output = readb_relaxed(pmap->regs + reg_off); |
229 | if (value) |
230 | output |= BIT(bit_off); |
231 | else |
232 | output &= ~BIT(bit_off); |
233 | writeb_relaxed(output, pmap->regs + reg_off); |
234 | spin_unlock_irqrestore(lock: &pmap->lock, flags); |
235 | } |
236 | |
237 | static int dc_gpiochip_add(struct dc_pinmap *pmap) |
238 | { |
239 | struct gpio_chip *chip = &pmap->chip; |
240 | int ret; |
241 | |
242 | chip->label = DRIVER_NAME; |
243 | chip->parent = pmap->dev; |
244 | chip->request = gpiochip_generic_request; |
245 | chip->free = gpiochip_generic_free; |
246 | chip->direction_input = dc_gpio_direction_input; |
247 | chip->direction_output = dc_gpio_direction_output; |
248 | chip->get = dc_gpio_get; |
249 | chip->set = dc_gpio_set; |
250 | chip->base = -1; |
251 | chip->ngpio = PINS_COUNT; |
252 | |
253 | spin_lock_init(&pmap->lock); |
254 | |
255 | ret = gpiochip_add_data(chip, pmap); |
256 | if (ret < 0) |
257 | return ret; |
258 | |
259 | ret = gpiochip_add_pin_range(gc: chip, pinctl_name: dev_name(dev: pmap->dev), gpio_offset: 0, pin_offset: 0, |
260 | PINS_COUNT); |
261 | if (ret < 0) { |
262 | gpiochip_remove(gc: chip); |
263 | return ret; |
264 | } |
265 | |
266 | return 0; |
267 | } |
268 | |
269 | static int dc_pinctrl_probe(struct platform_device *pdev) |
270 | { |
271 | struct dc_pinmap *pmap; |
272 | struct pinctrl_pin_desc *pins; |
273 | struct pinctrl_desc *pctl_desc; |
274 | char *pin_names; |
275 | int name_len = strlen("GP_xx" ) + 1; |
276 | int i, j; |
277 | |
278 | pmap = devm_kzalloc(dev: &pdev->dev, size: sizeof(*pmap), GFP_KERNEL); |
279 | if (!pmap) |
280 | return -ENOMEM; |
281 | |
282 | pmap->regs = devm_platform_ioremap_resource(pdev, index: 0); |
283 | if (IS_ERR(ptr: pmap->regs)) |
284 | return PTR_ERR(ptr: pmap->regs); |
285 | |
286 | pins = devm_kcalloc(dev: &pdev->dev, PINS_COUNT, size: sizeof(*pins), |
287 | GFP_KERNEL); |
288 | if (!pins) |
289 | return -ENOMEM; |
290 | pin_names = devm_kcalloc(dev: &pdev->dev, PINS_COUNT, size: name_len, |
291 | GFP_KERNEL); |
292 | if (!pin_names) |
293 | return -ENOMEM; |
294 | |
295 | for (i = 0; i < PIN_COLLECTIONS; i++) { |
296 | for (j = 0; j < PINS_PER_COLLECTION; j++) { |
297 | int pin_id = i*PINS_PER_COLLECTION + j; |
298 | char *name = &pin_names[pin_id * name_len]; |
299 | |
300 | snprintf(buf: name, size: name_len, fmt: "GP_%c%c" , 'A'+i, '0'+j); |
301 | |
302 | pins[pin_id].number = pin_id; |
303 | pins[pin_id].name = name; |
304 | pmap->pin_names[pin_id] = name; |
305 | } |
306 | } |
307 | |
308 | pctl_desc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*pctl_desc), GFP_KERNEL); |
309 | if (!pctl_desc) |
310 | return -ENOMEM; |
311 | |
312 | pctl_desc->name = DRIVER_NAME, |
313 | pctl_desc->owner = THIS_MODULE, |
314 | pctl_desc->pctlops = &dc_pinctrl_ops, |
315 | pctl_desc->pmxops = &dc_pmxops, |
316 | pctl_desc->npins = PINS_COUNT; |
317 | pctl_desc->pins = pins; |
318 | pmap->desc = pctl_desc; |
319 | |
320 | pmap->dev = &pdev->dev; |
321 | |
322 | pmap->pctl = devm_pinctrl_register(dev: &pdev->dev, pctldesc: pctl_desc, driver_data: pmap); |
323 | if (IS_ERR(ptr: pmap->pctl)) { |
324 | dev_err(&pdev->dev, "pinctrl driver registration failed\n" ); |
325 | return PTR_ERR(ptr: pmap->pctl); |
326 | } |
327 | |
328 | return dc_gpiochip_add(pmap); |
329 | } |
330 | |
331 | static const struct of_device_id dc_pinctrl_ids[] = { |
332 | { .compatible = "cnxt,cx92755-pinctrl" }, |
333 | { /* sentinel */ } |
334 | }; |
335 | |
336 | static struct platform_driver dc_pinctrl_driver = { |
337 | .driver = { |
338 | .name = DRIVER_NAME, |
339 | .of_match_table = dc_pinctrl_ids, |
340 | .suppress_bind_attrs = true, |
341 | }, |
342 | .probe = dc_pinctrl_probe, |
343 | }; |
344 | builtin_platform_driver(dc_pinctrl_driver); |
345 | |