1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2020, Broadcom */
3
4#include <linux/init.h>
5#include <linux/types.h>
6#include <linux/module.h>
7#include <linux/platform_device.h>
8#include <linux/interrupt.h>
9#include <linux/io.h>
10#include <linux/device.h>
11#include <linux/of.h>
12#include <linux/kernel.h>
13#include <linux/kdebug.h>
14#include <linux/gpio/consumer.h>
15
16struct out_pin {
17 u32 enable_mask;
18 u32 value_mask;
19 u32 changed_mask;
20 u32 clr_changed_mask;
21 struct gpio_desc *gpiod;
22 const char *name;
23};
24
25struct in_pin {
26 u32 enable_mask;
27 u32 value_mask;
28 struct gpio_desc *gpiod;
29 const char *name;
30 struct brcmstb_usb_pinmap_data *pdata;
31};
32
33struct brcmstb_usb_pinmap_data {
34 void __iomem *regs;
35 int in_count;
36 struct in_pin *in_pins;
37 int out_count;
38 struct out_pin *out_pins;
39};
40
41
42static void pinmap_set(void __iomem *reg, u32 mask)
43{
44 u32 val;
45
46 val = readl(addr: reg);
47 val |= mask;
48 writel(val, addr: reg);
49}
50
51static void pinmap_unset(void __iomem *reg, u32 mask)
52{
53 u32 val;
54
55 val = readl(addr: reg);
56 val &= ~mask;
57 writel(val, addr: reg);
58}
59
60static void sync_in_pin(struct in_pin *pin)
61{
62 u32 val;
63
64 val = gpiod_get_value(desc: pin->gpiod);
65 if (val)
66 pinmap_set(reg: pin->pdata->regs, mask: pin->value_mask);
67 else
68 pinmap_unset(reg: pin->pdata->regs, mask: pin->value_mask);
69}
70
71/*
72 * Interrupt from override register, propagate from override bit
73 * to GPIO.
74 */
75static irqreturn_t brcmstb_usb_pinmap_ovr_isr(int irq, void *dev_id)
76{
77 struct brcmstb_usb_pinmap_data *pdata = dev_id;
78 struct out_pin *pout;
79 u32 val;
80 u32 bit;
81 int x;
82
83 pr_debug("%s: reg: 0x%x\n", __func__, readl(pdata->regs));
84 pout = pdata->out_pins;
85 for (x = 0; x < pdata->out_count; x++) {
86 val = readl(addr: pdata->regs);
87 if (val & pout->changed_mask) {
88 pinmap_set(reg: pdata->regs, mask: pout->clr_changed_mask);
89 pinmap_unset(reg: pdata->regs, mask: pout->clr_changed_mask);
90 bit = val & pout->value_mask;
91 gpiod_set_value(desc: pout->gpiod, value: bit ? 1 : 0);
92 pr_debug("%s: %s bit changed state to %d\n",
93 __func__, pout->name, bit ? 1 : 0);
94 }
95 }
96 return IRQ_HANDLED;
97}
98
99/*
100 * Interrupt from GPIO, propagate from GPIO to override bit.
101 */
102static irqreturn_t brcmstb_usb_pinmap_gpio_isr(int irq, void *dev_id)
103{
104 struct in_pin *pin = dev_id;
105
106 pr_debug("%s: %s pin changed state\n", __func__, pin->name);
107 sync_in_pin(pin);
108 return IRQ_HANDLED;
109}
110
111
112static void get_pin_counts(struct device_node *dn, int *in_count,
113 int *out_count)
114{
115 int in;
116 int out;
117
118 *in_count = 0;
119 *out_count = 0;
120 in = of_property_count_strings(np: dn, propname: "brcm,in-functions");
121 if (in < 0)
122 return;
123 out = of_property_count_strings(np: dn, propname: "brcm,out-functions");
124 if (out < 0)
125 return;
126 *in_count = in;
127 *out_count = out;
128}
129
130static int parse_pins(struct device *dev, struct device_node *dn,
131 struct brcmstb_usb_pinmap_data *pdata)
132{
133 struct out_pin *pout;
134 struct in_pin *pin;
135 int index;
136 int res;
137 int x;
138
139 pin = pdata->in_pins;
140 for (x = 0, index = 0; x < pdata->in_count; x++) {
141 pin->gpiod = devm_gpiod_get_index(dev, con_id: "in", idx: x, flags: GPIOD_IN);
142 if (IS_ERR(ptr: pin->gpiod)) {
143 dev_err(dev, "Error getting gpio %s\n", pin->name);
144 return PTR_ERR(ptr: pin->gpiod);
145
146 }
147 res = of_property_read_string_index(np: dn, propname: "brcm,in-functions", index: x,
148 output: &pin->name);
149 if (res < 0) {
150 dev_err(dev, "Error getting brcm,in-functions for %s\n",
151 pin->name);
152 return res;
153 }
154 res = of_property_read_u32_index(np: dn, propname: "brcm,in-masks", index: index++,
155 out_value: &pin->enable_mask);
156 if (res < 0) {
157 dev_err(dev, "Error getting 1st brcm,in-masks for %s\n",
158 pin->name);
159 return res;
160 }
161 res = of_property_read_u32_index(np: dn, propname: "brcm,in-masks", index: index++,
162 out_value: &pin->value_mask);
163 if (res < 0) {
164 dev_err(dev, "Error getting 2nd brcm,in-masks for %s\n",
165 pin->name);
166 return res;
167 }
168 pin->pdata = pdata;
169 pin++;
170 }
171 pout = pdata->out_pins;
172 for (x = 0, index = 0; x < pdata->out_count; x++) {
173 pout->gpiod = devm_gpiod_get_index(dev, con_id: "out", idx: x,
174 flags: GPIOD_OUT_HIGH);
175 if (IS_ERR(ptr: pout->gpiod)) {
176 dev_err(dev, "Error getting gpio %s\n", pin->name);
177 return PTR_ERR(ptr: pout->gpiod);
178 }
179 res = of_property_read_string_index(np: dn, propname: "brcm,out-functions", index: x,
180 output: &pout->name);
181 if (res < 0) {
182 dev_err(dev, "Error getting brcm,out-functions for %s\n",
183 pout->name);
184 return res;
185 }
186 res = of_property_read_u32_index(np: dn, propname: "brcm,out-masks", index: index++,
187 out_value: &pout->enable_mask);
188 if (res < 0) {
189 dev_err(dev, "Error getting 1st brcm,out-masks for %s\n",
190 pout->name);
191 return res;
192 }
193 res = of_property_read_u32_index(np: dn, propname: "brcm,out-masks", index: index++,
194 out_value: &pout->value_mask);
195 if (res < 0) {
196 dev_err(dev, "Error getting 2nd brcm,out-masks for %s\n",
197 pout->name);
198 return res;
199 }
200 res = of_property_read_u32_index(np: dn, propname: "brcm,out-masks", index: index++,
201 out_value: &pout->changed_mask);
202 if (res < 0) {
203 dev_err(dev, "Error getting 3rd brcm,out-masks for %s\n",
204 pout->name);
205 return res;
206 }
207 res = of_property_read_u32_index(np: dn, propname: "brcm,out-masks", index: index++,
208 out_value: &pout->clr_changed_mask);
209 if (res < 0) {
210 dev_err(dev, "Error getting 4th out-masks for %s\n",
211 pout->name);
212 return res;
213 }
214 pout++;
215 }
216 return 0;
217}
218
219static void sync_all_pins(struct brcmstb_usb_pinmap_data *pdata)
220{
221 struct out_pin *pout;
222 struct in_pin *pin;
223 int val;
224 int x;
225
226 /*
227 * Enable the override, clear any changed condition and
228 * propagate the state to the GPIO for all out pins.
229 */
230 pout = pdata->out_pins;
231 for (x = 0; x < pdata->out_count; x++) {
232 pinmap_set(reg: pdata->regs, mask: pout->enable_mask);
233 pinmap_set(reg: pdata->regs, mask: pout->clr_changed_mask);
234 pinmap_unset(reg: pdata->regs, mask: pout->clr_changed_mask);
235 val = readl(addr: pdata->regs) & pout->value_mask;
236 gpiod_set_value(desc: pout->gpiod, value: val ? 1 : 0);
237 pout++;
238 }
239
240 /* sync and enable all in pins. */
241 pin = pdata->in_pins;
242 for (x = 0; x < pdata->in_count; x++) {
243 sync_in_pin(pin);
244 pinmap_set(reg: pdata->regs, mask: pin->enable_mask);
245 pin++;
246 }
247}
248
249static int __init brcmstb_usb_pinmap_probe(struct platform_device *pdev)
250{
251 struct device_node *dn = pdev->dev.of_node;
252 struct brcmstb_usb_pinmap_data *pdata;
253 struct in_pin *pin;
254 struct resource *r;
255 int out_count;
256 int in_count;
257 int err;
258 int irq;
259 int x;
260
261 get_pin_counts(dn, in_count: &in_count, out_count: &out_count);
262 if ((in_count + out_count) == 0)
263 return -EINVAL;
264
265 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
266 if (!r)
267 return -EINVAL;
268
269 pdata = devm_kzalloc(dev: &pdev->dev,
270 size: sizeof(*pdata) +
271 (sizeof(struct in_pin) * in_count) +
272 (sizeof(struct out_pin) * out_count), GFP_KERNEL);
273 if (!pdata)
274 return -ENOMEM;
275
276 pdata->in_count = in_count;
277 pdata->out_count = out_count;
278 pdata->in_pins = (struct in_pin *)(pdata + 1);
279 pdata->out_pins = (struct out_pin *)(pdata->in_pins + in_count);
280
281 pdata->regs = devm_ioremap(dev: &pdev->dev, offset: r->start, size: resource_size(res: r));
282 if (!pdata->regs)
283 return -ENOMEM;
284 platform_set_drvdata(pdev, data: pdata);
285
286 err = parse_pins(dev: &pdev->dev, dn, pdata);
287 if (err)
288 return err;
289
290 sync_all_pins(pdata);
291
292 if (out_count) {
293
294 /* Enable interrupt for out pins */
295 irq = platform_get_irq(pdev, 0);
296 if (irq < 0)
297 return irq;
298 err = devm_request_irq(dev: &pdev->dev, irq,
299 handler: brcmstb_usb_pinmap_ovr_isr,
300 IRQF_TRIGGER_RISING,
301 devname: pdev->name, dev_id: pdata);
302 if (err < 0) {
303 dev_err(&pdev->dev, "Error requesting IRQ\n");
304 return err;
305 }
306 }
307
308 for (x = 0, pin = pdata->in_pins; x < pdata->in_count; x++, pin++) {
309 irq = gpiod_to_irq(desc: pin->gpiod);
310 if (irq < 0) {
311 dev_err(&pdev->dev, "Error getting IRQ for %s pin\n",
312 pin->name);
313 return irq;
314 }
315 err = devm_request_irq(dev: &pdev->dev, irq,
316 handler: brcmstb_usb_pinmap_gpio_isr,
317 IRQF_SHARED | IRQF_TRIGGER_RISING |
318 IRQF_TRIGGER_FALLING,
319 devname: pdev->name, dev_id: pin);
320 if (err < 0) {
321 dev_err(&pdev->dev, "Error requesting IRQ for %s pin\n",
322 pin->name);
323 return err;
324 }
325 }
326
327 dev_dbg(&pdev->dev, "Driver probe succeeded\n");
328 dev_dbg(&pdev->dev, "In pin count: %d, out pin count: %d\n",
329 pdata->in_count, pdata->out_count);
330 return 0;
331}
332
333
334static const struct of_device_id brcmstb_usb_pinmap_of_match[] = {
335 { .compatible = "brcm,usb-pinmap" },
336 { },
337};
338
339static struct platform_driver brcmstb_usb_pinmap_driver = {
340 .driver = {
341 .name = "brcm-usb-pinmap",
342 .of_match_table = brcmstb_usb_pinmap_of_match,
343 },
344};
345
346static int __init brcmstb_usb_pinmap_init(void)
347{
348 return platform_driver_probe(&brcmstb_usb_pinmap_driver,
349 brcmstb_usb_pinmap_probe);
350}
351
352module_init(brcmstb_usb_pinmap_init);
353MODULE_AUTHOR("Al Cooper <alcooperx@gmail.com>");
354MODULE_DESCRIPTION("Broadcom USB Pinmap Driver");
355MODULE_LICENSE("GPL");
356

source code of linux/drivers/usb/misc/brcmstb-usb-pinmap.c