summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/gpio/Kconfig11
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-sama5d2-piobu.c253
4 files changed, 271 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 65828b79ae49..79979ca0edd4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9768,6 +9768,12 @@ M: Nicolas Ferre <nicolas.ferre@microchip.com>
9768S: Supported 9768S: Supported
9769F: drivers/power/reset/at91-sama5d2_shdwc.c 9769F: drivers/power/reset/at91-sama5d2_shdwc.c
9770 9770
9771MICROCHIP SAMA5D2-COMPATIBLE PIOBU GPIO
9772M: Andrei Stefanescu <andrei.stefanescu@microchip.com>
9773L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
9774L: linux-gpio@vger.kernel.org
9775F: drivers/gpio/gpio-sama5d2-piobu.c
9776
9771MICROCHIP SPI DRIVER 9777MICROCHIP SPI DRIVER
9772M: Nicolas Ferre <nicolas.ferre@microchip.com> 9778M: Nicolas Ferre <nicolas.ferre@microchip.com>
9773S: Supported 9779S: Supported
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 06e7f9d5b84a..f47994708809 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -430,6 +430,17 @@ config GPIO_REG
430 A 32-bit single register GPIO fixed in/out implementation. This 430 A 32-bit single register GPIO fixed in/out implementation. This
431 can be used to represent any register as a set of GPIO signals. 431 can be used to represent any register as a set of GPIO signals.
432 432
433config GPIO_SAMA5D2_PIOBU
434 tristate "SAMA5D2 PIOBU GPIO support"
435 depends on MFD_SYSCON
436 select GPIO_SYSCON
437 help
438 Say yes here to use the PIOBU pins as GPIOs.
439
440 PIOBU pins on the SAMA5D2 can be used as GPIOs.
441 The difference from regular GPIOs is that they
442 maintain their value during backup/self-refresh.
443
433config GPIO_SIOX 444config GPIO_SIOX
434 tristate "SIOX GPIO support" 445 tristate "SIOX GPIO support"
435 depends on SIOX 446 depends on SIOX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 671c4477c951..f18d34590722 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -108,6 +108,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
108obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o 108obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
109obj-$(CONFIG_GPIO_REG) += gpio-reg.o 109obj-$(CONFIG_GPIO_REG) += gpio-reg.o
110obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o 110obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
111obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
111obj-$(CONFIG_GPIO_SCH) += gpio-sch.o 112obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
112obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o 113obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
113obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o 114obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c
new file mode 100644
index 000000000000..03a000659fa1
--- /dev/null
+++ b/drivers/gpio/gpio-sama5d2-piobu.c
@@ -0,0 +1,253 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * SAMA5D2 PIOBU GPIO controller
4 *
5 * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
6 *
7 * Author: Andrei Stefanescu <andrei.stefanescu@microchip.com>
8 *
9 */
10#include <linux/bits.h>
11#include <linux/gpio/driver.h>
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/mfd/syscon.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/platform_device.h>
18#include <linux/regmap.h>
19
20#define PIOBU_NUM 8
21#define PIOBU_REG_SIZE 4
22
23/*
24 * backup mode protection register for tamper detection
25 * normal mode protection register for tamper detection
26 * wakeup signal generation
27 */
28#define PIOBU_BMPR 0x7C
29#define PIOBU_NMPR 0x80
30#define PIOBU_WKPR 0x90
31
32#define PIOBU_BASE 0x18 /* PIOBU offset from SECUMOD base register address. */
33
34#define PIOBU_DET_OFFSET 16
35
36/* In the datasheet this bit is called OUTPUT */
37#define PIOBU_DIRECTION BIT(8)
38#define PIOBU_OUT BIT(8)
39#define PIOBU_IN 0
40
41#define PIOBU_SOD BIT(9)
42#define PIOBU_PDS BIT(10)
43
44#define PIOBU_HIGH BIT(9)
45#define PIOBU_LOW 0
46
47struct sama5d2_piobu {
48 struct gpio_chip chip;
49 struct regmap *regmap;
50};
51
52/**
53 * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call
54 *
55 * Do not consider pin for tamper detection (normal and backup modes)
56 * Do not consider pin as tamper wakeup interrupt source
57 */
58static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
59{
60 int ret;
61 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
62 chip);
63 unsigned int mask = BIT(PIOBU_DET_OFFSET + pin);
64
65 ret = regmap_update_bits(piobu->regmap, PIOBU_BMPR, mask, 0);
66 if (ret)
67 return ret;
68
69 ret = regmap_update_bits(piobu->regmap, PIOBU_NMPR, mask, 0);
70 if (ret)
71 return ret;
72
73 return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0);
74}
75
76/**
77 * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register
78 */
79static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
80 unsigned int mask, unsigned int value)
81{
82 int reg;
83 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
84 chip);
85
86 reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
87
88 return regmap_update_bits(piobu->regmap, reg, mask, value);
89}
90
91/**
92 * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU
93 * register
94 */
95static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
96 unsigned int mask)
97{
98 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
99 chip);
100 unsigned int val, reg;
101 int ret;
102
103 reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
104 ret = regmap_read(piobu->regmap, reg, &val);
105 if (ret < 0)
106 return ret;
107
108 return val & mask;
109}
110
111/**
112 * sama5d2_piobu_set_direction() - mark pin as input or output
113 */
114static int sama5d2_piobu_set_direction(struct gpio_chip *chip,
115 unsigned int direction,
116 unsigned int pin)
117{
118 return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction);
119}
120
121/**
122 * sama5d2_piobu_get_direction() - gpiochip get_direction
123 */
124static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
125 unsigned int pin)
126{
127 int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION);
128
129 if (ret < 0)
130 return ret;
131
132 return (ret == PIOBU_IN) ? 1 : 0;
133}
134
135/**
136 * sama5d2_piobu_direction_input() - gpiochip direction_input
137 */
138static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
139 unsigned int pin)
140{
141 return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin);
142}
143
144/**
145 * sama5d2_piobu_direction_output() - gpiochip direction_output
146 */
147static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
148 unsigned int pin, int value)
149{
150 return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin);
151}
152
153/**
154 * sama5d2_piobu_get() - gpiochip get
155 */
156static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
157{
158 /* if pin is input, read value from PDS else read from SOD */
159 int ret = sama5d2_piobu_get_direction(chip, pin);
160
161 if (ret == 1)
162 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
163 else if (!ret)
164 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
165
166 if (ret < 0)
167 return ret;
168
169 return !!ret;
170}
171
172/**
173 * sama5d2_piobu_set() - gpiochip set
174 */
175static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
176 int value)
177{
178 if (!value)
179 value = PIOBU_LOW;
180 else
181 value = PIOBU_HIGH;
182
183 sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);
184}
185
186static int sama5d2_piobu_probe(struct platform_device *pdev)
187{
188 struct sama5d2_piobu *piobu;
189 int ret, i;
190
191 piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL);
192 if (!piobu)
193 return -ENOMEM;
194
195 platform_set_drvdata(pdev, piobu);
196 piobu->chip.label = pdev->name;
197 piobu->chip.parent = &pdev->dev;
198 piobu->chip.of_node = pdev->dev.of_node;
199 piobu->chip.owner = THIS_MODULE,
200 piobu->chip.get_direction = sama5d2_piobu_get_direction,
201 piobu->chip.direction_input = sama5d2_piobu_direction_input,
202 piobu->chip.direction_output = sama5d2_piobu_direction_output,
203 piobu->chip.get = sama5d2_piobu_get,
204 piobu->chip.set = sama5d2_piobu_set,
205 piobu->chip.base = -1,
206 piobu->chip.ngpio = PIOBU_NUM,
207 piobu->chip.can_sleep = 0,
208
209 piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node);
210 if (IS_ERR(piobu->regmap)) {
211 dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n",
212 PTR_ERR(piobu->regmap));
213 return PTR_ERR(piobu->regmap);
214 }
215
216 ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu);
217 if (ret) {
218 dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret);
219 return ret;
220 }
221
222 for (i = 0; i < PIOBU_NUM; ++i) {
223 ret = sama5d2_piobu_setup_pin(&piobu->chip, i);
224 if (ret) {
225 dev_err(&pdev->dev, "Failed to setup pin: %d %d\n",
226 i, ret);
227 return ret;
228 }
229 }
230
231 return 0;
232}
233
234static const struct of_device_id sama5d2_piobu_ids[] = {
235 { .compatible = "atmel,sama5d2-secumod" },
236 {},
237};
238MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids);
239
240static struct platform_driver sama5d2_piobu_driver = {
241 .driver = {
242 .name = "sama5d2-piobu",
243 .of_match_table = of_match_ptr(sama5d2_piobu_ids)
244 },
245 .probe = sama5d2_piobu_probe,
246};
247
248module_platform_driver(sama5d2_piobu_driver);
249
250MODULE_VERSION("1.0");
251MODULE_LICENSE("GPL v2");
252MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
253MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");