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 
16 struct 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 
25 struct 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 
33 struct 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 
pinmap_set(void __iomem * reg,u32 mask)42 static void pinmap_set(void __iomem *reg, u32 mask)
43 {
44 	u32 val;
45 
46 	val = readl(reg);
47 	val |= mask;
48 	writel(val, reg);
49 }
50 
pinmap_unset(void __iomem * reg,u32 mask)51 static void pinmap_unset(void __iomem *reg, u32 mask)
52 {
53 	u32 val;
54 
55 	val = readl(reg);
56 	val &= ~mask;
57 	writel(val, reg);
58 }
59 
sync_in_pin(struct in_pin * pin)60 static void sync_in_pin(struct in_pin *pin)
61 {
62 	u32 val;
63 
64 	val = gpiod_get_value(pin->gpiod);
65 	if (val)
66 		pinmap_set(pin->pdata->regs, pin->value_mask);
67 	else
68 		pinmap_unset(pin->pdata->regs, pin->value_mask);
69 }
70 
71 /*
72  * Interrupt from override register, propagate from override bit
73  * to GPIO.
74  */
brcmstb_usb_pinmap_ovr_isr(int irq,void * dev_id)75 static 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(pdata->regs);
87 		if (val & pout->changed_mask) {
88 			pinmap_set(pdata->regs, pout->clr_changed_mask);
89 			pinmap_unset(pdata->regs, pout->clr_changed_mask);
90 			bit = val & pout->value_mask;
91 			gpiod_set_value(pout->gpiod, 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  */
brcmstb_usb_pinmap_gpio_isr(int irq,void * dev_id)102 static 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 
get_pin_counts(struct device_node * dn,int * in_count,int * out_count)112 static 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(dn, "brcm,in-functions");
121 	if (in < 0)
122 		return;
123 	out = of_property_count_strings(dn, "brcm,out-functions");
124 	if (out < 0)
125 		return;
126 	*in_count = in;
127 	*out_count = out;
128 }
129 
parse_pins(struct device * dev,struct device_node * dn,struct brcmstb_usb_pinmap_data * pdata)130 static 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, "in", x, GPIOD_IN);
142 		if (IS_ERR(pin->gpiod)) {
143 			dev_err(dev, "Error getting gpio %s\n", pin->name);
144 			return PTR_ERR(pin->gpiod);
145 
146 		}
147 		res = of_property_read_string_index(dn, "brcm,in-functions", x,
148 						    &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(dn, "brcm,in-masks", index++,
155 						 &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(dn, "brcm,in-masks", index++,
162 						 &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, "out", x,
174 						   GPIOD_OUT_HIGH);
175 		if (IS_ERR(pout->gpiod)) {
176 			dev_err(dev, "Error getting gpio %s\n", pin->name);
177 			return PTR_ERR(pout->gpiod);
178 		}
179 		res = of_property_read_string_index(dn, "brcm,out-functions", x,
180 						    &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(dn, "brcm,out-masks", index++,
187 						 &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(dn, "brcm,out-masks", index++,
194 						 &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(dn, "brcm,out-masks", index++,
201 						 &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(dn, "brcm,out-masks", index++,
208 						 &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 
sync_all_pins(struct brcmstb_usb_pinmap_data * pdata)219 static 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(pdata->regs, pout->enable_mask);
233 		pinmap_set(pdata->regs, pout->clr_changed_mask);
234 		pinmap_unset(pdata->regs, pout->clr_changed_mask);
235 		val = readl(pdata->regs) & pout->value_mask;
236 		gpiod_set_value(pout->gpiod, 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(pdata->regs, pin->enable_mask);
245 		pin++;
246 	}
247 }
248 
brcmstb_usb_pinmap_probe(struct platform_device * pdev)249 static 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, &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(&pdev->dev,
270 			     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(&pdev->dev, r->start, resource_size(r));
282 	if (!pdata->regs)
283 		return -ENOMEM;
284 	platform_set_drvdata(pdev, pdata);
285 
286 	err = parse_pins(&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(&pdev->dev, irq,
299 				       brcmstb_usb_pinmap_ovr_isr,
300 				       IRQF_TRIGGER_RISING,
301 				       pdev->name, 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(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(&pdev->dev, irq,
316 				       brcmstb_usb_pinmap_gpio_isr,
317 				       IRQF_SHARED | IRQF_TRIGGER_RISING |
318 				       IRQF_TRIGGER_FALLING,
319 				       pdev->name, 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 
334 static const struct of_device_id brcmstb_usb_pinmap_of_match[] = {
335 	{ .compatible = "brcm,usb-pinmap" },
336 	{ },
337 };
338 MODULE_DEVICE_TABLE(of, brcmstb_usb_pinmap_of_match);
339 
340 static struct platform_driver brcmstb_usb_pinmap_driver = {
341 	.driver = {
342 		.name	= "brcm-usb-pinmap",
343 		.of_match_table = brcmstb_usb_pinmap_of_match,
344 	},
345 };
346 
brcmstb_usb_pinmap_init(void)347 static int __init brcmstb_usb_pinmap_init(void)
348 {
349 	return platform_driver_probe(&brcmstb_usb_pinmap_driver,
350 				     brcmstb_usb_pinmap_probe);
351 }
352 
353 module_init(brcmstb_usb_pinmap_init);
354 MODULE_AUTHOR("Al Cooper <alcooperx@gmail.com>");
355 MODULE_DESCRIPTION("Broadcom USB Pinmap Driver");
356 MODULE_LICENSE("GPL");
357