History

gpio: msc313: MStar MSC313 GPIO driver

This adds a driver that supports the GPIO block found in MStar/SigmaStar ARMv7 SoCs. The controller seems to have enough register for 128 lines but where they are wired up differs between chips and no currently known chip uses anywhere near 128 lines so there needs to be some per-chip data to collect together what lines actually have physical pins attached and map the right names to them. The core peripherals seem to use the same lines on the currently known chips but the lines used for the sensor interface, lcd controller etc pins seem to be totally different between the infinity and mercury chips The code tries to collect all of the re-usable names, offsets etc together so that it's easy to build the extra per-chip data for other chips in the future. So far this only supports the MSC313 and MSC313E chips. Support for the SSC8336N (mercury5) is trivial to add once all of the lines have been mapped out.

Daniel Palmer committed 5 years ago

Commit 93224ed1 parent 493c7e04 files · +473 −0Browse files

Unified · Split

4 files
  1. MAINTAINERS +1 −0
  2. drivers/gpio/Kconfig +11 −0
  3. drivers/gpio/Makefile +1 −0
  4. drivers/gpio/gpio-msc313.c +460 −0
MAINTAINERS+1−0
@@ -2134,6 +2134,7 @@ F: Documentation/devicetree/bindings/arm/mstar/* · 2133 unmodified lines
2134F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml2134F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml
2135F: arch/arm/boot/dts/mstar-*2135F: arch/arm/boot/dts/mstar-*
2136F: arch/arm/mach-mstar/2136F: arch/arm/mach-mstar/
2137F: drivers/gpio/gpio-msc313.c
2137F: include/dt-bindings/gpio/msc313-gpio.h2138F: include/dt-bindings/gpio/msc313-gpio.h
2138 2139
2139ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT2140ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT
drivers/gpio/Kconfig+11−0
@@ -738,6 +738,17 @@ config GPIO_AMD_FCH · 737 unmodified lines
738 Note: This driver doesn't registers itself automatically, as it738 Note: This driver doesn't registers itself automatically, as it
739 needs to be provided with platform specific configuration.739 needs to be provided with platform specific configuration.
740 (See eg. CONFIG_PCENGINES_APU2.)740 (See eg. CONFIG_PCENGINES_APU2.)
741
742config GPIO_MSC313
743 bool "MStar MSC313 GPIO support"
744 depends on ARCH_MSTARV7
745 default ARCH_MSTARV7
746 select GPIOLIB_IRQCHIP
747 select IRQ_DOMAIN_HIERARCHY
748 help
749 Say Y here to support the main GPIO block on MStar/SigmaStar
750 ARMv7 based SoCs.
751
741endmenu752endmenu
742 753
743menu "Port-mapped I/O GPIO drivers"754menu "Port-mapped I/O GPIO drivers"
drivers/gpio/Makefile+1−0
@@ -101,6 +101,7 @@ obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o · 100 unmodified lines
101obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o101obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
102obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o102obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
103obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o103obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
104obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o
104obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o105obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o
105obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o106obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
106obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o107obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
drivers/gpio/gpio-msc313.c+460−0
@@ -0,0 +1,460 @@
1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (C) 2020 Daniel Palmer<daniel@thingy.jp> */
3
4#include <linux/bitops.h>
5#include <linux/kernel.h>
6#include <linux/types.h>
7#include <linux/io.h>
8#include <linux/of.h>
9#include <linux/of_device.h>
10#include <linux/of_irq.h>
11#include <linux/gpio/driver.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14
15#include <dt-bindings/gpio/msc313-gpio.h>
16#include <dt-bindings/interrupt-controller/arm-gic.h>
17
18#define DRIVER_NAME "gpio-msc313"
19
20#define MSC313_GPIO_IN BIT(0)
21#define MSC313_GPIO_OUT BIT(4)
22#define MSC313_GPIO_OEN BIT(5)
23
24/*
25 * These bits need to be saved to correctly restore the
26 * gpio state when resuming from suspend to memory.
27 */
28#define MSC313_GPIO_BITSTOSAVE (MSC313_GPIO_OUT | MSC313_GPIO_OEN)
29
30/* pad names for fuart, same for all SoCs so far */
31#define MSC313_PINNAME_FUART_RX "fuart_rx"
32#define MSC313_PINNAME_FUART_TX "fuart_tx"
33#define MSC313_PINNAME_FUART_CTS "fuart_cts"
34#define MSC313_PINNAME_FUART_RTS "fuart_rts"
35
36/* pad names for sr, mercury5 is different */
37#define MSC313_PINNAME_SR_IO2 "sr_io2"
38#define MSC313_PINNAME_SR_IO3 "sr_io3"
39#define MSC313_PINNAME_SR_IO4 "sr_io4"
40#define MSC313_PINNAME_SR_IO5 "sr_io5"
41#define MSC313_PINNAME_SR_IO6 "sr_io6"
42#define MSC313_PINNAME_SR_IO7 "sr_io7"
43#define MSC313_PINNAME_SR_IO8 "sr_io8"
44#define MSC313_PINNAME_SR_IO9 "sr_io9"
45#define MSC313_PINNAME_SR_IO10 "sr_io10"
46#define MSC313_PINNAME_SR_IO11 "sr_io11"
47#define MSC313_PINNAME_SR_IO12 "sr_io12"
48#define MSC313_PINNAME_SR_IO13 "sr_io13"
49#define MSC313_PINNAME_SR_IO14 "sr_io14"
50#define MSC313_PINNAME_SR_IO15 "sr_io15"
51#define MSC313_PINNAME_SR_IO16 "sr_io16"
52#define MSC313_PINNAME_SR_IO17 "sr_io17"
53
54/* pad names for sd, same for all SoCs so far */
55#define MSC313_PINNAME_SD_CLK "sd_clk"
56#define MSC313_PINNAME_SD_CMD "sd_cmd"
57#define MSC313_PINNAME_SD_D0 "sd_d0"
58#define MSC313_PINNAME_SD_D1 "sd_d1"
59#define MSC313_PINNAME_SD_D2 "sd_d2"
60#define MSC313_PINNAME_SD_D3 "sd_d3"
61
62/* pad names for i2c1, same for all SoCs so for */
63#define MSC313_PINNAME_I2C1_SCL "i2c1_scl"
64#define MSC313_PINNAME_I2C1_SCA "i2c1_sda"
65
66/* pad names for spi0, same for all SoCs so far */
67#define MSC313_PINNAME_SPI0_CZ "spi0_cz"
68#define MSC313_PINNAME_SPI0_CK "spi0_ck"
69#define MSC313_PINNAME_SPI0_DI "spi0_di"
70#define MSC313_PINNAME_SPI0_DO "spi0_do"
71
72#define FUART_NAMES \
73 MSC313_PINNAME_FUART_RX, \
74 MSC313_PINNAME_FUART_TX, \
75 MSC313_PINNAME_FUART_CTS, \
76 MSC313_PINNAME_FUART_RTS
77
78#define OFF_FUART_RX 0x50
79#define OFF_FUART_TX 0x54
80#define OFF_FUART_CTS 0x58
81#define OFF_FUART_RTS 0x5c
82
83#define FUART_OFFSETS \
84 OFF_FUART_RX, \
85 OFF_FUART_TX, \
86 OFF_FUART_CTS, \
87 OFF_FUART_RTS
88
89#define SR_NAMES \
90 MSC313_PINNAME_SR_IO2, \
91 MSC313_PINNAME_SR_IO3, \
92 MSC313_PINNAME_SR_IO4, \
93 MSC313_PINNAME_SR_IO5, \
94 MSC313_PINNAME_SR_IO6, \
95 MSC313_PINNAME_SR_IO7, \
96 MSC313_PINNAME_SR_IO8, \
97 MSC313_PINNAME_SR_IO9, \
98 MSC313_PINNAME_SR_IO10, \
99 MSC313_PINNAME_SR_IO11, \
100 MSC313_PINNAME_SR_IO12, \
101 MSC313_PINNAME_SR_IO13, \
102 MSC313_PINNAME_SR_IO14, \
103 MSC313_PINNAME_SR_IO15, \
104 MSC313_PINNAME_SR_IO16, \
105 MSC313_PINNAME_SR_IO17
106
107#define OFF_SR_IO2 0x88
108#define OFF_SR_IO3 0x8c
109#define OFF_SR_IO4 0x90
110#define OFF_SR_IO5 0x94
111#define OFF_SR_IO6 0x98
112#define OFF_SR_IO7 0x9c
113#define OFF_SR_IO8 0xa0
114#define OFF_SR_IO9 0xa4
115#define OFF_SR_IO10 0xa8
116#define OFF_SR_IO11 0xac
117#define OFF_SR_IO12 0xb0
118#define OFF_SR_IO13 0xb4
119#define OFF_SR_IO14 0xb8
120#define OFF_SR_IO15 0xbc
121#define OFF_SR_IO16 0xc0
122#define OFF_SR_IO17 0xc4
123
124#define SR_OFFSETS \
125 OFF_SR_IO2, \
126 OFF_SR_IO3, \
127 OFF_SR_IO4, \
128 OFF_SR_IO5, \
129 OFF_SR_IO6, \
130 OFF_SR_IO7, \
131 OFF_SR_IO8, \
132 OFF_SR_IO9, \
133 OFF_SR_IO10, \
134 OFF_SR_IO11, \
135 OFF_SR_IO12, \
136 OFF_SR_IO13, \
137 OFF_SR_IO14, \
138 OFF_SR_IO15, \
139 OFF_SR_IO16, \
140 OFF_SR_IO17
141
142#define SD_NAMES \
143 MSC313_PINNAME_SD_CLK, \
144 MSC313_PINNAME_SD_CMD, \
145 MSC313_PINNAME_SD_D0, \
146 MSC313_PINNAME_SD_D1, \
147 MSC313_PINNAME_SD_D2, \
148 MSC313_PINNAME_SD_D3
149
150#define OFF_SD_CLK 0x140
151#define OFF_SD_CMD 0x144
152#define OFF_SD_D0 0x148
153#define OFF_SD_D1 0x14c
154#define OFF_SD_D2 0x150
155#define OFF_SD_D3 0x154
156
157#define SD_OFFSETS \
158 OFF_SD_CLK, \
159 OFF_SD_CMD, \
160 OFF_SD_D0, \
161 OFF_SD_D1, \
162 OFF_SD_D2, \
163 OFF_SD_D3
164
165#define I2C1_NAMES \
166 MSC313_PINNAME_I2C1_SCL, \
167 MSC313_PINNAME_I2C1_SCA
168
169#define OFF_I2C1_SCL 0x188
170#define OFF_I2C1_SCA 0x18c
171
172#define I2C1_OFFSETS \
173 OFF_I2C1_SCL, \
174 OFF_I2C1_SCA
175
176#define SPI0_NAMES \
177 MSC313_PINNAME_SPI0_CZ, \
178 MSC313_PINNAME_SPI0_CK, \
179 MSC313_PINNAME_SPI0_DI, \
180 MSC313_PINNAME_SPI0_DO
181
182#define OFF_SPI0_CZ 0x1c0
183#define OFF_SPI0_CK 0x1c4
184#define OFF_SPI0_DI 0x1c8
185#define OFF_SPI0_DO 0x1cc
186
187#define SPI0_OFFSETS \
188 OFF_SPI0_CZ, \
189 OFF_SPI0_CK, \
190 OFF_SPI0_DI, \
191 OFF_SPI0_DO
192
193struct msc313_gpio_data {
194 const char * const *names;
195 const unsigned int *offsets;
196 const unsigned int num;
197};
198
199#define MSC313_GPIO_CHIPDATA(_chip) \
200static const struct msc313_gpio_data _chip##_data = { \
201 .names = _chip##_names, \
202 .offsets = _chip##_offsets, \
203 .num = ARRAY_SIZE(_chip##_offsets), \
204}
205
206#ifdef CONFIG_MACH_INFINITY
207static const char * const msc313_names[] = {
208 FUART_NAMES,
209 SR_NAMES,
210 SD_NAMES,
211 I2C1_NAMES,
212 SPI0_NAMES,
213};
214
215static const unsigned int msc313_offsets[] = {
216 FUART_OFFSETS,
217 SR_OFFSETS,
218 SD_OFFSETS,
219 I2C1_OFFSETS,
220 SPI0_OFFSETS,
221};
222
223MSC313_GPIO_CHIPDATA(msc313);
224#endif
225
226struct msc313_gpio {
227 void __iomem *base;
228 const struct msc313_gpio_data *gpio_data;
229 u8 *saved;
230};
231
232static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
233{
234 struct msc313_gpio *gpio = gpiochip_get_data(chip);
235 u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]);
236
237 if (value)
238 gpioreg |= MSC313_GPIO_OUT;
239 else
240 gpioreg &= ~MSC313_GPIO_OUT;
241
242 writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]);
243}
244
245static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset)
246{
247 struct msc313_gpio *gpio = gpiochip_get_data(chip);
248
249 return readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]) & MSC313_GPIO_IN;
250}
251
252static int msc313_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
253{
254 struct msc313_gpio *gpio = gpiochip_get_data(chip);
255 u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]);
256
257 gpioreg |= MSC313_GPIO_OEN;
258 writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]);
259
260 return 0;
261}
262
263static int msc313_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value)
264{
265 struct msc313_gpio *gpio = gpiochip_get_data(chip);
266 u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]);
267
268 gpioreg &= ~MSC313_GPIO_OEN;
269 if (value)
270 gpioreg |= MSC313_GPIO_OUT;
271 else
272 gpioreg &= ~MSC313_GPIO_OUT;
273 writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]);
274
275 return 0;
276}
277
278/*
279 * The interrupt handling happens in the parent interrupt controller,
280 * we don't do anything here.
281 */
282static struct irq_chip msc313_gpio_irqchip = {
283 .name = "GPIO",
284 .irq_eoi = irq_chip_eoi_parent,
285 .irq_mask = irq_chip_mask_parent,
286 .irq_unmask = irq_chip_unmask_parent,
287 .irq_set_type = irq_chip_set_type_parent,
288 .irq_set_affinity = irq_chip_set_affinity_parent,
289};
290
291/*
292 * The parent interrupt controller needs the GIC interrupt type set to GIC_SPI
293 * so we need to provide the fwspec. Essentially gpiochip_populate_parent_fwspec_twocell
294 * that puts GIC_SPI into the first cell.
295 */
296static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
297 unsigned int parent_hwirq,
298 unsigned int parent_type)
299{
300 struct irq_fwspec *fwspec;
301
302 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
303 if (!fwspec)
304 return NULL;
305
306 fwspec->fwnode = gc->irq.parent_domain->fwnode;
307 fwspec->param_count = 3;
308 fwspec->param[0] = GIC_SPI;
309 fwspec->param[1] = parent_hwirq;
310 fwspec->param[2] = parent_type;
311
312 return fwspec;
313}
314
315static int msc313e_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
316 unsigned int child,
317 unsigned int child_type,
318 unsigned int *parent,
319 unsigned int *parent_type)
320{
321 struct msc313_gpio *priv = gpiochip_get_data(chip);
322 unsigned int offset = priv->gpio_data->offsets[child];
323
324 /*
325 * only the spi0 pins have interrupts on the parent
326 * on all of the known chips and so far they are all
327 * mapped to the same place
328 */
329 if (offset >= OFF_SPI0_CZ && offset <= OFF_SPI0_DO) {
330 *parent_type = child_type;
331 *parent = ((offset - OFF_SPI0_CZ) >> 2) + 28;
332 return 0;
333 }
334
335 return -EINVAL;
336}
337
338static int msc313_gpio_probe(struct platform_device *pdev)
339{
340 const struct msc313_gpio_data *match_data;
341 struct msc313_gpio *gpio;
342 struct gpio_chip *gpiochip;
343 struct gpio_irq_chip *gpioirqchip;
344 struct irq_domain *parent_domain;
345 struct device_node *parent_node;
346 struct device *dev = &pdev->dev;
347 int ret;
348
349 match_data = of_device_get_match_data(dev);
350 if (!match_data)
351 return -EINVAL;
352
353 parent_node = of_irq_find_parent(dev->of_node);
354 if (!parent_node)
355 return -ENODEV;
356
357 parent_domain = irq_find_host(parent_node);
358 if (!parent_domain)
359 return -ENODEV;
360
361 gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
362 if (!gpio)
363 return -ENOMEM;
364
365 gpio->gpio_data = match_data;
366
367 gpio->saved = devm_kcalloc(dev, gpio->gpio_data->num, sizeof(*gpio->saved), GFP_KERNEL);
368 if (!gpio->saved)
369 return -ENOMEM;
370
371 gpio->base = devm_platform_ioremap_resource(pdev, 0);
372 if (IS_ERR(gpio->base))
373 return PTR_ERR(gpio->base);
374
375 platform_set_drvdata(pdev, gpio);
376
377 gpiochip = devm_kzalloc(dev, sizeof(*gpiochip), GFP_KERNEL);
378 if (!gpiochip)
379 return -ENOMEM;
380
381 gpiochip->label = DRIVER_NAME;
382 gpiochip->parent = dev;
383 gpiochip->request = gpiochip_generic_request;
384 gpiochip->free = gpiochip_generic_free;
385 gpiochip->direction_input = msc313_gpio_direction_input;
386 gpiochip->direction_output = msc313_gpio_direction_output;
387 gpiochip->get = msc313_gpio_get;
388 gpiochip->set = msc313_gpio_set;
389 gpiochip->base = -1;
390 gpiochip->ngpio = gpio->gpio_data->num;
391 gpiochip->names = gpio->gpio_data->names;
392
393 gpioirqchip = &gpiochip->irq;
394 gpioirqchip->chip = &msc313_gpio_irqchip;
395 gpioirqchip->fwnode = of_node_to_fwnode(dev->of_node);
396 gpioirqchip->parent_domain = parent_domain;
397 gpioirqchip->child_to_parent_hwirq = msc313e_gpio_child_to_parent_hwirq;
398 gpioirqchip->populate_parent_alloc_arg = msc313_gpio_populate_parent_fwspec;
399 gpioirqchip->handler = handle_bad_irq;
400 gpioirqchip->default_type = IRQ_TYPE_NONE;
401
402 ret = devm_gpiochip_add_data(dev, gpiochip, gpio);
403 return ret;
404}
405
406static int msc313_gpio_remove(struct platform_device *pdev)
407{
408 return 0;
409}
410
411static const struct of_device_id msc313_gpio_of_match[] = {
412#ifdef CONFIG_MACH_INFINITY
413 {
414 .compatible = "mstar,msc313-gpio",
415 .data = &msc313_data,
416 },
417#endif
418 { }
419};
420
421/*
422 * The GPIO controller loses the state of the registers when the
423 * SoC goes into suspend to memory mode so we need to save some
424 * of the register bits before suspending and put it back when resuming
425 */
426static int __maybe_unused msc313_gpio_suspend(struct device *dev)
427{
428 struct msc313_gpio *gpio = dev_get_drvdata(dev);
429 int i;
430
431 for (i = 0; i < gpio->gpio_data->num; i++)
432 gpio->saved[i] = readb_relaxed(gpio->base + gpio->gpio_data->offsets[i]) & MSC313_GPIO_BITSTOSAVE;
433
434 return 0;
435}
436
437static int __maybe_unused msc313_gpio_resume(struct device *dev)
438{
439 struct msc313_gpio *gpio = dev_get_drvdata(dev);
440 int i;
441
442 for (i = 0; i < gpio->gpio_data->num; i++)
443 writeb_relaxed(gpio->saved[i], gpio->base + gpio->gpio_data->offsets[i]);
444
445 return 0;
446}
447
448static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume);
449
450static struct platform_driver msc313_gpio_driver = {
451 .driver = {
452 .name = DRIVER_NAME,
453 .of_match_table = msc313_gpio_of_match,
454 .pm = &msc313_gpio_ops,
455 },
456 .probe = msc313_gpio_probe,
457 .remove = msc313_gpio_remove,
458};
459
460builtin_platform_driver(msc313_gpio_driver);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t