1fb0b35d3SAndrei.Stefanescu@microchip.com // SPDX-License-Identifier: GPL-2.0
2fb0b35d3SAndrei.Stefanescu@microchip.com /*
3fb0b35d3SAndrei.Stefanescu@microchip.com * SAMA5D2 PIOBU GPIO controller
4fb0b35d3SAndrei.Stefanescu@microchip.com *
5fb0b35d3SAndrei.Stefanescu@microchip.com * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
6fb0b35d3SAndrei.Stefanescu@microchip.com *
7fb0b35d3SAndrei.Stefanescu@microchip.com * Author: Andrei Stefanescu <andrei.stefanescu@microchip.com>
8fb0b35d3SAndrei.Stefanescu@microchip.com *
9fb0b35d3SAndrei.Stefanescu@microchip.com */
10fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/bits.h>
11fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/gpio/driver.h>
12fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/init.h>
13fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/kernel.h>
14fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/mfd/syscon.h>
15fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/module.h>
16fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/of.h>
17fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/platform_device.h>
18fb0b35d3SAndrei.Stefanescu@microchip.com #include <linux/regmap.h>
19fb0b35d3SAndrei.Stefanescu@microchip.com
20fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_NUM 8
21fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_REG_SIZE 4
22fb0b35d3SAndrei.Stefanescu@microchip.com
23fb0b35d3SAndrei.Stefanescu@microchip.com /*
24fb0b35d3SAndrei.Stefanescu@microchip.com * backup mode protection register for tamper detection
25fb0b35d3SAndrei.Stefanescu@microchip.com * normal mode protection register for tamper detection
26fb0b35d3SAndrei.Stefanescu@microchip.com * wakeup signal generation
27fb0b35d3SAndrei.Stefanescu@microchip.com */
28fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_BMPR 0x7C
29fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_NMPR 0x80
30fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_WKPR 0x90
31fb0b35d3SAndrei.Stefanescu@microchip.com
32fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_BASE 0x18 /* PIOBU offset from SECUMOD base register address. */
33fb0b35d3SAndrei.Stefanescu@microchip.com
34fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_DET_OFFSET 16
35fb0b35d3SAndrei.Stefanescu@microchip.com
36fb0b35d3SAndrei.Stefanescu@microchip.com /* In the datasheet this bit is called OUTPUT */
37fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_DIRECTION BIT(8)
38fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_OUT BIT(8)
39fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_IN 0
40fb0b35d3SAndrei.Stefanescu@microchip.com
41fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_SOD BIT(9)
42fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_PDS BIT(10)
43fb0b35d3SAndrei.Stefanescu@microchip.com
44fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_HIGH BIT(9)
45fb0b35d3SAndrei.Stefanescu@microchip.com #define PIOBU_LOW 0
46fb0b35d3SAndrei.Stefanescu@microchip.com
47fb0b35d3SAndrei.Stefanescu@microchip.com struct sama5d2_piobu {
48fb0b35d3SAndrei.Stefanescu@microchip.com struct gpio_chip chip;
49fb0b35d3SAndrei.Stefanescu@microchip.com struct regmap *regmap;
50fb0b35d3SAndrei.Stefanescu@microchip.com };
51fb0b35d3SAndrei.Stefanescu@microchip.com
52392a5846SLee Jones /*
53fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call
54fb0b35d3SAndrei.Stefanescu@microchip.com *
55fb0b35d3SAndrei.Stefanescu@microchip.com * Do not consider pin for tamper detection (normal and backup modes)
56fb0b35d3SAndrei.Stefanescu@microchip.com * Do not consider pin as tamper wakeup interrupt source
57fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_setup_pin(struct gpio_chip * chip,unsigned int pin)58fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
59fb0b35d3SAndrei.Stefanescu@microchip.com {
60fb0b35d3SAndrei.Stefanescu@microchip.com int ret;
61fb0b35d3SAndrei.Stefanescu@microchip.com struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
62fb0b35d3SAndrei.Stefanescu@microchip.com chip);
63fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int mask = BIT(PIOBU_DET_OFFSET + pin);
64fb0b35d3SAndrei.Stefanescu@microchip.com
65fb0b35d3SAndrei.Stefanescu@microchip.com ret = regmap_update_bits(piobu->regmap, PIOBU_BMPR, mask, 0);
66fb0b35d3SAndrei.Stefanescu@microchip.com if (ret)
67fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
68fb0b35d3SAndrei.Stefanescu@microchip.com
69fb0b35d3SAndrei.Stefanescu@microchip.com ret = regmap_update_bits(piobu->regmap, PIOBU_NMPR, mask, 0);
70fb0b35d3SAndrei.Stefanescu@microchip.com if (ret)
71fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
72fb0b35d3SAndrei.Stefanescu@microchip.com
73fb0b35d3SAndrei.Stefanescu@microchip.com return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0);
74fb0b35d3SAndrei.Stefanescu@microchip.com }
75fb0b35d3SAndrei.Stefanescu@microchip.com
76392a5846SLee Jones /*
77fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register
78fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_write_value(struct gpio_chip * chip,unsigned int pin,unsigned int mask,unsigned int value)79fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
80fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int mask, unsigned int value)
81fb0b35d3SAndrei.Stefanescu@microchip.com {
82fb0b35d3SAndrei.Stefanescu@microchip.com int reg;
83fb0b35d3SAndrei.Stefanescu@microchip.com struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
84fb0b35d3SAndrei.Stefanescu@microchip.com chip);
85fb0b35d3SAndrei.Stefanescu@microchip.com
86fb0b35d3SAndrei.Stefanescu@microchip.com reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
87fb0b35d3SAndrei.Stefanescu@microchip.com
88fb0b35d3SAndrei.Stefanescu@microchip.com return regmap_update_bits(piobu->regmap, reg, mask, value);
89fb0b35d3SAndrei.Stefanescu@microchip.com }
90fb0b35d3SAndrei.Stefanescu@microchip.com
91392a5846SLee Jones /*
92fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU
93fb0b35d3SAndrei.Stefanescu@microchip.com * register
94fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_read_value(struct gpio_chip * chip,unsigned int pin,unsigned int mask)95fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
96fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int mask)
97fb0b35d3SAndrei.Stefanescu@microchip.com {
98fb0b35d3SAndrei.Stefanescu@microchip.com struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
99fb0b35d3SAndrei.Stefanescu@microchip.com chip);
100fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int val, reg;
101fb0b35d3SAndrei.Stefanescu@microchip.com int ret;
102fb0b35d3SAndrei.Stefanescu@microchip.com
103fb0b35d3SAndrei.Stefanescu@microchip.com reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
104fb0b35d3SAndrei.Stefanescu@microchip.com ret = regmap_read(piobu->regmap, reg, &val);
105fb0b35d3SAndrei.Stefanescu@microchip.com if (ret < 0)
106fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
107fb0b35d3SAndrei.Stefanescu@microchip.com
108fb0b35d3SAndrei.Stefanescu@microchip.com return val & mask;
109fb0b35d3SAndrei.Stefanescu@microchip.com }
110fb0b35d3SAndrei.Stefanescu@microchip.com
111392a5846SLee Jones /*
112fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_get_direction() - gpiochip get_direction
113fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_get_direction(struct gpio_chip * chip,unsigned int pin)114fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
115fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int pin)
116fb0b35d3SAndrei.Stefanescu@microchip.com {
117fb0b35d3SAndrei.Stefanescu@microchip.com int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION);
118fb0b35d3SAndrei.Stefanescu@microchip.com
119fb0b35d3SAndrei.Stefanescu@microchip.com if (ret < 0)
120fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
121fb0b35d3SAndrei.Stefanescu@microchip.com
122e42615ecSMatti Vaittinen return (ret == PIOBU_IN) ? GPIO_LINE_DIRECTION_IN :
123e42615ecSMatti Vaittinen GPIO_LINE_DIRECTION_OUT;
124fb0b35d3SAndrei.Stefanescu@microchip.com }
125fb0b35d3SAndrei.Stefanescu@microchip.com
126392a5846SLee Jones /*
127fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_direction_input() - gpiochip direction_input
128fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_direction_input(struct gpio_chip * chip,unsigned int pin)129fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
130fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int pin)
131fb0b35d3SAndrei.Stefanescu@microchip.com {
132e4889362SAxel Lin return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN);
133fb0b35d3SAndrei.Stefanescu@microchip.com }
134fb0b35d3SAndrei.Stefanescu@microchip.com
135392a5846SLee Jones /*
136fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_direction_output() - gpiochip direction_output
137fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_direction_output(struct gpio_chip * chip,unsigned int pin,int value)138fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
139fb0b35d3SAndrei.Stefanescu@microchip.com unsigned int pin, int value)
140fb0b35d3SAndrei.Stefanescu@microchip.com {
141e4889362SAxel Lin unsigned int val = PIOBU_OUT;
142e4889362SAxel Lin
143e4889362SAxel Lin if (value)
144e4889362SAxel Lin val |= PIOBU_HIGH;
145e4889362SAxel Lin
146e4889362SAxel Lin return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD,
147e4889362SAxel Lin val);
148fb0b35d3SAndrei.Stefanescu@microchip.com }
149fb0b35d3SAndrei.Stefanescu@microchip.com
150392a5846SLee Jones /*
151fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_get() - gpiochip get
152fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_get(struct gpio_chip * chip,unsigned int pin)153fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
154fb0b35d3SAndrei.Stefanescu@microchip.com {
155fb0b35d3SAndrei.Stefanescu@microchip.com /* if pin is input, read value from PDS else read from SOD */
156fb0b35d3SAndrei.Stefanescu@microchip.com int ret = sama5d2_piobu_get_direction(chip, pin);
157fb0b35d3SAndrei.Stefanescu@microchip.com
158e42615ecSMatti Vaittinen if (ret == GPIO_LINE_DIRECTION_IN)
159fb0b35d3SAndrei.Stefanescu@microchip.com ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
160e42615ecSMatti Vaittinen else if (ret == GPIO_LINE_DIRECTION_OUT)
161fb0b35d3SAndrei.Stefanescu@microchip.com ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
162fb0b35d3SAndrei.Stefanescu@microchip.com
163fb0b35d3SAndrei.Stefanescu@microchip.com if (ret < 0)
164fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
165fb0b35d3SAndrei.Stefanescu@microchip.com
166fb0b35d3SAndrei.Stefanescu@microchip.com return !!ret;
167fb0b35d3SAndrei.Stefanescu@microchip.com }
168fb0b35d3SAndrei.Stefanescu@microchip.com
169392a5846SLee Jones /*
170fb0b35d3SAndrei.Stefanescu@microchip.com * sama5d2_piobu_set() - gpiochip set
171fb0b35d3SAndrei.Stefanescu@microchip.com */
sama5d2_piobu_set(struct gpio_chip * chip,unsigned int pin,int value)172fb0b35d3SAndrei.Stefanescu@microchip.com static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
173fb0b35d3SAndrei.Stefanescu@microchip.com int value)
174fb0b35d3SAndrei.Stefanescu@microchip.com {
175fb0b35d3SAndrei.Stefanescu@microchip.com if (!value)
176fb0b35d3SAndrei.Stefanescu@microchip.com value = PIOBU_LOW;
177fb0b35d3SAndrei.Stefanescu@microchip.com else
178fb0b35d3SAndrei.Stefanescu@microchip.com value = PIOBU_HIGH;
179fb0b35d3SAndrei.Stefanescu@microchip.com
180fb0b35d3SAndrei.Stefanescu@microchip.com sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);
181fb0b35d3SAndrei.Stefanescu@microchip.com }
182fb0b35d3SAndrei.Stefanescu@microchip.com
sama5d2_piobu_probe(struct platform_device * pdev)183fb0b35d3SAndrei.Stefanescu@microchip.com static int sama5d2_piobu_probe(struct platform_device *pdev)
184fb0b35d3SAndrei.Stefanescu@microchip.com {
185fb0b35d3SAndrei.Stefanescu@microchip.com struct sama5d2_piobu *piobu;
186fb0b35d3SAndrei.Stefanescu@microchip.com int ret, i;
187fb0b35d3SAndrei.Stefanescu@microchip.com
188fb0b35d3SAndrei.Stefanescu@microchip.com piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL);
189fb0b35d3SAndrei.Stefanescu@microchip.com if (!piobu)
190fb0b35d3SAndrei.Stefanescu@microchip.com return -ENOMEM;
191fb0b35d3SAndrei.Stefanescu@microchip.com
192fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.label = pdev->name;
193fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.parent = &pdev->dev;
194fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.owner = THIS_MODULE,
195fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.get_direction = sama5d2_piobu_get_direction,
196fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.direction_input = sama5d2_piobu_direction_input,
197fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.direction_output = sama5d2_piobu_direction_output,
198fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.get = sama5d2_piobu_get,
199fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.set = sama5d2_piobu_set,
200fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.base = -1,
201fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.ngpio = PIOBU_NUM,
202fb0b35d3SAndrei.Stefanescu@microchip.com piobu->chip.can_sleep = 0,
203fb0b35d3SAndrei.Stefanescu@microchip.com
204fb0b35d3SAndrei.Stefanescu@microchip.com piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node);
205fb0b35d3SAndrei.Stefanescu@microchip.com if (IS_ERR(piobu->regmap)) {
206fb0b35d3SAndrei.Stefanescu@microchip.com dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n",
207fb0b35d3SAndrei.Stefanescu@microchip.com PTR_ERR(piobu->regmap));
208fb0b35d3SAndrei.Stefanescu@microchip.com return PTR_ERR(piobu->regmap);
209fb0b35d3SAndrei.Stefanescu@microchip.com }
210fb0b35d3SAndrei.Stefanescu@microchip.com
211fb0b35d3SAndrei.Stefanescu@microchip.com ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu);
212fb0b35d3SAndrei.Stefanescu@microchip.com if (ret) {
213fb0b35d3SAndrei.Stefanescu@microchip.com dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret);
214fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
215fb0b35d3SAndrei.Stefanescu@microchip.com }
216fb0b35d3SAndrei.Stefanescu@microchip.com
217fb0b35d3SAndrei.Stefanescu@microchip.com for (i = 0; i < PIOBU_NUM; ++i) {
218fb0b35d3SAndrei.Stefanescu@microchip.com ret = sama5d2_piobu_setup_pin(&piobu->chip, i);
219fb0b35d3SAndrei.Stefanescu@microchip.com if (ret) {
220fb0b35d3SAndrei.Stefanescu@microchip.com dev_err(&pdev->dev, "Failed to setup pin: %d %d\n",
221fb0b35d3SAndrei.Stefanescu@microchip.com i, ret);
222fb0b35d3SAndrei.Stefanescu@microchip.com return ret;
223fb0b35d3SAndrei.Stefanescu@microchip.com }
224fb0b35d3SAndrei.Stefanescu@microchip.com }
225fb0b35d3SAndrei.Stefanescu@microchip.com
226fb0b35d3SAndrei.Stefanescu@microchip.com return 0;
227fb0b35d3SAndrei.Stefanescu@microchip.com }
228fb0b35d3SAndrei.Stefanescu@microchip.com
229fb0b35d3SAndrei.Stefanescu@microchip.com static const struct of_device_id sama5d2_piobu_ids[] = {
230fb0b35d3SAndrei.Stefanescu@microchip.com { .compatible = "atmel,sama5d2-secumod" },
231fb0b35d3SAndrei.Stefanescu@microchip.com {},
232fb0b35d3SAndrei.Stefanescu@microchip.com };
233fb0b35d3SAndrei.Stefanescu@microchip.com MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids);
234fb0b35d3SAndrei.Stefanescu@microchip.com
235fb0b35d3SAndrei.Stefanescu@microchip.com static struct platform_driver sama5d2_piobu_driver = {
236fb0b35d3SAndrei.Stefanescu@microchip.com .driver = {
237fb0b35d3SAndrei.Stefanescu@microchip.com .name = "sama5d2-piobu",
238*87cb1f51SKrzysztof Kozlowski .of_match_table = sama5d2_piobu_ids,
239fb0b35d3SAndrei.Stefanescu@microchip.com },
240fb0b35d3SAndrei.Stefanescu@microchip.com .probe = sama5d2_piobu_probe,
241fb0b35d3SAndrei.Stefanescu@microchip.com };
242fb0b35d3SAndrei.Stefanescu@microchip.com
243fb0b35d3SAndrei.Stefanescu@microchip.com module_platform_driver(sama5d2_piobu_driver);
244fb0b35d3SAndrei.Stefanescu@microchip.com
245fb0b35d3SAndrei.Stefanescu@microchip.com MODULE_LICENSE("GPL v2");
246fb0b35d3SAndrei.Stefanescu@microchip.com MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
247fb0b35d3SAndrei.Stefanescu@microchip.com MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");
248