diff options
author | Sudip Mukherjee <sudip.mukherjee@codethink.co.uk> | 2017-01-19 17:23:20 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2017-01-26 05:11:40 -0500 |
commit | 6596e59e63cd4c0c0b787ce8c1e8bdd1d957b16e (patch) | |
tree | ed8e3c85cdeab061f0360bbf51454ea1ee60cd7f | |
parent | 99c8ac957e6b7c670b78ad7b8667051de8624d37 (diff) |
gpio: exar: add gpio for exar cards
Exar XR17V352/354/358 chips have 16 multi-purpose inputs/outputs which
can be controlled using gpio interface.
Add the gpio specific code.
Signed-off-by: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-exar.c | 200 |
3 files changed, 208 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 701809b1a105..2d7739222cdb 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -185,6 +185,13 @@ config GPIO_ETRAXFS | |||
185 | help | 185 | help |
186 | Say yes here to support the GPIO controller on Axis ETRAX FS SoCs. | 186 | Say yes here to support the GPIO controller on Axis ETRAX FS SoCs. |
187 | 187 | ||
188 | config GPIO_EXAR | ||
189 | tristate "Support for GPIO pins on XR17V352/354/358" | ||
190 | depends on SERIAL_8250_EXAR | ||
191 | help | ||
192 | Selecting this option will enable handling of GPIO pins present | ||
193 | on Exar XR17V352/354/358 chips. | ||
194 | |||
188 | config GPIO_GE_FPGA | 195 | config GPIO_GE_FPGA |
189 | bool "GE FPGA based GPIO" | 196 | bool "GE FPGA based GPIO" |
190 | depends on GE_FPGA | 197 | depends on GE_FPGA |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index de45957b9b62..ee8b0dcac623 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o | |||
46 | obj-$(CONFIG_GPIO_EM) += gpio-em.o | 46 | obj-$(CONFIG_GPIO_EM) += gpio-em.o |
47 | obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o | 47 | obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o |
48 | obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o | 48 | obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o |
49 | obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o | ||
49 | obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o | 50 | obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o |
50 | obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o | 51 | obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o |
51 | obj-$(CONFIG_GPIO_GEMINI) += gpio-gemini.o | 52 | obj-$(CONFIG_GPIO_GEMINI) += gpio-gemini.o |
diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c new file mode 100644 index 000000000000..05c8946d6446 --- /dev/null +++ b/drivers/gpio/gpio-exar.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * GPIO driver for Exar XR17V35X chip | ||
3 | * | ||
4 | * Copyright (C) 2015 Sudip Mukherjee <sudip.mukherjee@codethink.co.uk> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/bitops.h> | ||
11 | #include <linux/device.h> | ||
12 | #include <linux/gpio/driver.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | |||
19 | #define EXAR_OFFSET_MPIOLVL_LO 0x90 | ||
20 | #define EXAR_OFFSET_MPIOSEL_LO 0x93 | ||
21 | #define EXAR_OFFSET_MPIOLVL_HI 0x96 | ||
22 | #define EXAR_OFFSET_MPIOSEL_HI 0x99 | ||
23 | |||
24 | #define DRIVER_NAME "gpio_exar" | ||
25 | |||
26 | static DEFINE_IDA(ida_index); | ||
27 | |||
28 | struct exar_gpio_chip { | ||
29 | struct gpio_chip gpio_chip; | ||
30 | struct mutex lock; | ||
31 | int index; | ||
32 | void __iomem *regs; | ||
33 | char name[20]; | ||
34 | }; | ||
35 | |||
36 | static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, | ||
37 | unsigned int offset) | ||
38 | { | ||
39 | struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); | ||
40 | int temp; | ||
41 | |||
42 | mutex_lock(&exar_gpio->lock); | ||
43 | temp = readb(exar_gpio->regs + reg); | ||
44 | temp &= ~BIT(offset); | ||
45 | if (val) | ||
46 | temp |= BIT(offset); | ||
47 | writeb(temp, exar_gpio->regs + reg); | ||
48 | mutex_unlock(&exar_gpio->lock); | ||
49 | } | ||
50 | |||
51 | static int exar_set_direction(struct gpio_chip *chip, int direction, | ||
52 | unsigned int offset) | ||
53 | { | ||
54 | unsigned int bank = offset / 8; | ||
55 | unsigned int addr; | ||
56 | |||
57 | addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; | ||
58 | exar_update(chip, addr, direction, offset % 8); | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, | ||
63 | int value) | ||
64 | { | ||
65 | return exar_set_direction(chip, 0, offset); | ||
66 | } | ||
67 | |||
68 | static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) | ||
69 | { | ||
70 | return exar_set_direction(chip, 1, offset); | ||
71 | } | ||
72 | |||
73 | static int exar_get(struct gpio_chip *chip, unsigned int reg) | ||
74 | { | ||
75 | struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); | ||
76 | int value; | ||
77 | |||
78 | mutex_lock(&exar_gpio->lock); | ||
79 | value = readb(exar_gpio->regs + reg); | ||
80 | mutex_unlock(&exar_gpio->lock); | ||
81 | |||
82 | return !!value; | ||
83 | } | ||
84 | |||
85 | static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) | ||
86 | { | ||
87 | unsigned int bank = offset / 8; | ||
88 | unsigned int addr; | ||
89 | int val; | ||
90 | |||
91 | addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; | ||
92 | val = exar_get(chip, addr) >> (offset % 8); | ||
93 | |||
94 | return !!val; | ||
95 | } | ||
96 | |||
97 | static int exar_get_value(struct gpio_chip *chip, unsigned int offset) | ||
98 | { | ||
99 | unsigned int bank = offset / 8; | ||
100 | unsigned int addr; | ||
101 | int val; | ||
102 | |||
103 | addr = bank ? EXAR_OFFSET_MPIOLVL_LO : EXAR_OFFSET_MPIOLVL_HI; | ||
104 | val = exar_get(chip, addr) >> (offset % 8); | ||
105 | |||
106 | return !!val; | ||
107 | } | ||
108 | |||
109 | static void exar_set_value(struct gpio_chip *chip, unsigned int offset, | ||
110 | int value) | ||
111 | { | ||
112 | unsigned int bank = offset / 8; | ||
113 | unsigned int addr; | ||
114 | |||
115 | addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; | ||
116 | exar_update(chip, addr, value, offset % 8); | ||
117 | } | ||
118 | |||
119 | static int gpio_exar_probe(struct platform_device *pdev) | ||
120 | { | ||
121 | struct pci_dev *pcidev = platform_get_drvdata(pdev); | ||
122 | struct exar_gpio_chip *exar_gpio; | ||
123 | void __iomem *p; | ||
124 | int index, ret; | ||
125 | |||
126 | if (pcidev->vendor != PCI_VENDOR_ID_EXAR) | ||
127 | return -ENODEV; | ||
128 | |||
129 | /* | ||
130 | * Map the pci device to get the register addresses. | ||
131 | * We will need to read and write those registers to control | ||
132 | * the GPIO pins. | ||
133 | * Using managed functions will save us from unmaping on exit. | ||
134 | * As the device is enabled using managed functions by the | ||
135 | * UART driver we can also use managed functions here. | ||
136 | */ | ||
137 | p = pcim_iomap(pcidev, 0, 0); | ||
138 | if (!p) | ||
139 | return -ENOMEM; | ||
140 | |||
141 | exar_gpio = devm_kzalloc(&pcidev->dev, sizeof(*exar_gpio), GFP_KERNEL); | ||
142 | if (!exar_gpio) | ||
143 | return -ENOMEM; | ||
144 | |||
145 | mutex_init(&exar_gpio->lock); | ||
146 | |||
147 | index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL); | ||
148 | |||
149 | sprintf(exar_gpio->name, "exar_gpio%d", index); | ||
150 | exar_gpio->gpio_chip.label = exar_gpio->name; | ||
151 | exar_gpio->gpio_chip.parent = &pcidev->dev; | ||
152 | exar_gpio->gpio_chip.direction_output = exar_direction_output; | ||
153 | exar_gpio->gpio_chip.direction_input = exar_direction_input; | ||
154 | exar_gpio->gpio_chip.get_direction = exar_get_direction; | ||
155 | exar_gpio->gpio_chip.get = exar_get_value; | ||
156 | exar_gpio->gpio_chip.set = exar_set_value; | ||
157 | exar_gpio->gpio_chip.base = -1; | ||
158 | exar_gpio->gpio_chip.ngpio = 16; | ||
159 | exar_gpio->regs = p; | ||
160 | exar_gpio->index = index; | ||
161 | |||
162 | ret = devm_gpiochip_add_data(&pcidev->dev, | ||
163 | &exar_gpio->gpio_chip, exar_gpio); | ||
164 | if (ret) | ||
165 | goto err_destroy; | ||
166 | |||
167 | platform_set_drvdata(pdev, exar_gpio); | ||
168 | |||
169 | return 0; | ||
170 | |||
171 | err_destroy: | ||
172 | ida_simple_remove(&ida_index, index); | ||
173 | mutex_destroy(&exar_gpio->lock); | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static int gpio_exar_remove(struct platform_device *pdev) | ||
178 | { | ||
179 | struct exar_gpio_chip *exar_gpio = platform_get_drvdata(pdev); | ||
180 | |||
181 | ida_simple_remove(&ida_index, exar_gpio->index); | ||
182 | mutex_destroy(&exar_gpio->lock); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static struct platform_driver gpio_exar_driver = { | ||
188 | .probe = gpio_exar_probe, | ||
189 | .remove = gpio_exar_remove, | ||
190 | .driver = { | ||
191 | .name = DRIVER_NAME, | ||
192 | }, | ||
193 | }; | ||
194 | |||
195 | module_platform_driver(gpio_exar_driver); | ||
196 | |||
197 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
198 | MODULE_DESCRIPTION("Exar GPIO driver"); | ||
199 | MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); | ||
200 | MODULE_LICENSE("GPL"); | ||