1*4342bf63SThomas Richard // SPDX-License-Identifier: GPL-2.0-or-later
2*4342bf63SThomas Richard /*
3*4342bf63SThomas Richard * Congatec Board Controller GPIO driver
4*4342bf63SThomas Richard *
5*4342bf63SThomas Richard * Copyright (C) 2024 Bootlin
6*4342bf63SThomas Richard * Author: Thomas Richard <thomas.richard@bootlin.com>
7*4342bf63SThomas Richard */
8*4342bf63SThomas Richard
9*4342bf63SThomas Richard #include <linux/gpio/driver.h>
10*4342bf63SThomas Richard #include <linux/mfd/cgbc.h>
11*4342bf63SThomas Richard #include <linux/module.h>
12*4342bf63SThomas Richard #include <linux/mutex.h>
13*4342bf63SThomas Richard #include <linux/platform_device.h>
14*4342bf63SThomas Richard
15*4342bf63SThomas Richard #define CGBC_GPIO_NGPIO 14
16*4342bf63SThomas Richard
17*4342bf63SThomas Richard #define CGBC_GPIO_CMD_GET 0x64
18*4342bf63SThomas Richard #define CGBC_GPIO_CMD_SET 0x65
19*4342bf63SThomas Richard #define CGBC_GPIO_CMD_DIR_GET 0x66
20*4342bf63SThomas Richard #define CGBC_GPIO_CMD_DIR_SET 0x67
21*4342bf63SThomas Richard
22*4342bf63SThomas Richard struct cgbc_gpio_data {
23*4342bf63SThomas Richard struct gpio_chip chip;
24*4342bf63SThomas Richard struct cgbc_device_data *cgbc;
25*4342bf63SThomas Richard struct mutex lock;
26*4342bf63SThomas Richard };
27*4342bf63SThomas Richard
cgbc_gpio_cmd(struct cgbc_device_data * cgbc,u8 cmd0,u8 cmd1,u8 cmd2,u8 * value)28*4342bf63SThomas Richard static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc,
29*4342bf63SThomas Richard u8 cmd0, u8 cmd1, u8 cmd2, u8 *value)
30*4342bf63SThomas Richard {
31*4342bf63SThomas Richard u8 cmd[3] = {cmd0, cmd1, cmd2};
32*4342bf63SThomas Richard
33*4342bf63SThomas Richard return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL);
34*4342bf63SThomas Richard }
35*4342bf63SThomas Richard
cgbc_gpio_get(struct gpio_chip * chip,unsigned int offset)36*4342bf63SThomas Richard static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset)
37*4342bf63SThomas Richard {
38*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
39*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc;
40*4342bf63SThomas Richard int ret;
41*4342bf63SThomas Richard u8 val;
42*4342bf63SThomas Richard
43*4342bf63SThomas Richard scoped_guard(mutex, &gpio->lock)
44*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val);
45*4342bf63SThomas Richard
46*4342bf63SThomas Richard offset %= 8;
47*4342bf63SThomas Richard
48*4342bf63SThomas Richard if (ret)
49*4342bf63SThomas Richard return ret;
50*4342bf63SThomas Richard else
51*4342bf63SThomas Richard return (int)(val & (u8)BIT(offset));
52*4342bf63SThomas Richard }
53*4342bf63SThomas Richard
__cgbc_gpio_set(struct gpio_chip * chip,unsigned int offset,int value)54*4342bf63SThomas Richard static void __cgbc_gpio_set(struct gpio_chip *chip,
55*4342bf63SThomas Richard unsigned int offset, int value)
56*4342bf63SThomas Richard {
57*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
58*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc;
59*4342bf63SThomas Richard u8 val;
60*4342bf63SThomas Richard int ret;
61*4342bf63SThomas Richard
62*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val);
63*4342bf63SThomas Richard if (ret)
64*4342bf63SThomas Richard return;
65*4342bf63SThomas Richard
66*4342bf63SThomas Richard if (value)
67*4342bf63SThomas Richard val |= BIT(offset % 8);
68*4342bf63SThomas Richard else
69*4342bf63SThomas Richard val &= ~(BIT(offset % 8));
70*4342bf63SThomas Richard
71*4342bf63SThomas Richard cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val);
72*4342bf63SThomas Richard }
73*4342bf63SThomas Richard
cgbc_gpio_set(struct gpio_chip * chip,unsigned int offset,int value)74*4342bf63SThomas Richard static void cgbc_gpio_set(struct gpio_chip *chip,
75*4342bf63SThomas Richard unsigned int offset, int value)
76*4342bf63SThomas Richard {
77*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
78*4342bf63SThomas Richard
79*4342bf63SThomas Richard scoped_guard(mutex, &gpio->lock)
80*4342bf63SThomas Richard __cgbc_gpio_set(chip, offset, value);
81*4342bf63SThomas Richard }
82*4342bf63SThomas Richard
cgbc_gpio_direction_set(struct gpio_chip * chip,unsigned int offset,int direction)83*4342bf63SThomas Richard static int cgbc_gpio_direction_set(struct gpio_chip *chip,
84*4342bf63SThomas Richard unsigned int offset, int direction)
85*4342bf63SThomas Richard {
86*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
87*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc;
88*4342bf63SThomas Richard int ret;
89*4342bf63SThomas Richard u8 val;
90*4342bf63SThomas Richard
91*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val);
92*4342bf63SThomas Richard if (ret)
93*4342bf63SThomas Richard goto end;
94*4342bf63SThomas Richard
95*4342bf63SThomas Richard if (direction == GPIO_LINE_DIRECTION_IN)
96*4342bf63SThomas Richard val &= ~(BIT(offset % 8));
97*4342bf63SThomas Richard else
98*4342bf63SThomas Richard val |= BIT(offset % 8);
99*4342bf63SThomas Richard
100*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val);
101*4342bf63SThomas Richard
102*4342bf63SThomas Richard end:
103*4342bf63SThomas Richard return ret;
104*4342bf63SThomas Richard }
105*4342bf63SThomas Richard
cgbc_gpio_direction_input(struct gpio_chip * chip,unsigned int offset)106*4342bf63SThomas Richard static int cgbc_gpio_direction_input(struct gpio_chip *chip,
107*4342bf63SThomas Richard unsigned int offset)
108*4342bf63SThomas Richard {
109*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
110*4342bf63SThomas Richard
111*4342bf63SThomas Richard guard(mutex)(&gpio->lock);
112*4342bf63SThomas Richard return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN);
113*4342bf63SThomas Richard }
114*4342bf63SThomas Richard
cgbc_gpio_direction_output(struct gpio_chip * chip,unsigned int offset,int value)115*4342bf63SThomas Richard static int cgbc_gpio_direction_output(struct gpio_chip *chip,
116*4342bf63SThomas Richard unsigned int offset, int value)
117*4342bf63SThomas Richard {
118*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
119*4342bf63SThomas Richard
120*4342bf63SThomas Richard guard(mutex)(&gpio->lock);
121*4342bf63SThomas Richard
122*4342bf63SThomas Richard __cgbc_gpio_set(chip, offset, value);
123*4342bf63SThomas Richard return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT);
124*4342bf63SThomas Richard }
125*4342bf63SThomas Richard
cgbc_gpio_get_direction(struct gpio_chip * chip,unsigned int offset)126*4342bf63SThomas Richard static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
127*4342bf63SThomas Richard {
128*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
129*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc;
130*4342bf63SThomas Richard int ret;
131*4342bf63SThomas Richard u8 val;
132*4342bf63SThomas Richard
133*4342bf63SThomas Richard scoped_guard(mutex, &gpio->lock)
134*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val);
135*4342bf63SThomas Richard
136*4342bf63SThomas Richard if (ret)
137*4342bf63SThomas Richard return ret;
138*4342bf63SThomas Richard
139*4342bf63SThomas Richard if (val & BIT(offset % 8))
140*4342bf63SThomas Richard return GPIO_LINE_DIRECTION_OUT;
141*4342bf63SThomas Richard else
142*4342bf63SThomas Richard return GPIO_LINE_DIRECTION_IN;
143*4342bf63SThomas Richard }
144*4342bf63SThomas Richard
cgbc_gpio_probe(struct platform_device * pdev)145*4342bf63SThomas Richard static int cgbc_gpio_probe(struct platform_device *pdev)
146*4342bf63SThomas Richard {
147*4342bf63SThomas Richard struct device *dev = &pdev->dev;
148*4342bf63SThomas Richard struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent);
149*4342bf63SThomas Richard struct cgbc_gpio_data *gpio;
150*4342bf63SThomas Richard struct gpio_chip *chip;
151*4342bf63SThomas Richard int ret;
152*4342bf63SThomas Richard
153*4342bf63SThomas Richard gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
154*4342bf63SThomas Richard if (!gpio)
155*4342bf63SThomas Richard return -ENOMEM;
156*4342bf63SThomas Richard
157*4342bf63SThomas Richard gpio->cgbc = cgbc;
158*4342bf63SThomas Richard
159*4342bf63SThomas Richard platform_set_drvdata(pdev, gpio);
160*4342bf63SThomas Richard
161*4342bf63SThomas Richard chip = &gpio->chip;
162*4342bf63SThomas Richard chip->label = dev_name(&pdev->dev);
163*4342bf63SThomas Richard chip->owner = THIS_MODULE;
164*4342bf63SThomas Richard chip->parent = dev;
165*4342bf63SThomas Richard chip->base = -1;
166*4342bf63SThomas Richard chip->direction_input = cgbc_gpio_direction_input;
167*4342bf63SThomas Richard chip->direction_output = cgbc_gpio_direction_output;
168*4342bf63SThomas Richard chip->get_direction = cgbc_gpio_get_direction;
169*4342bf63SThomas Richard chip->get = cgbc_gpio_get;
170*4342bf63SThomas Richard chip->set = cgbc_gpio_set;
171*4342bf63SThomas Richard chip->ngpio = CGBC_GPIO_NGPIO;
172*4342bf63SThomas Richard
173*4342bf63SThomas Richard ret = devm_mutex_init(dev, &gpio->lock);
174*4342bf63SThomas Richard if (ret)
175*4342bf63SThomas Richard return ret;
176*4342bf63SThomas Richard
177*4342bf63SThomas Richard ret = devm_gpiochip_add_data(dev, chip, gpio);
178*4342bf63SThomas Richard if (ret)
179*4342bf63SThomas Richard return dev_err_probe(dev, ret, "Could not register GPIO chip\n");
180*4342bf63SThomas Richard
181*4342bf63SThomas Richard return 0;
182*4342bf63SThomas Richard }
183*4342bf63SThomas Richard
184*4342bf63SThomas Richard static struct platform_driver cgbc_gpio_driver = {
185*4342bf63SThomas Richard .driver = {
186*4342bf63SThomas Richard .name = "cgbc-gpio",
187*4342bf63SThomas Richard },
188*4342bf63SThomas Richard .probe = cgbc_gpio_probe,
189*4342bf63SThomas Richard };
190*4342bf63SThomas Richard
191*4342bf63SThomas Richard module_platform_driver(cgbc_gpio_driver);
192*4342bf63SThomas Richard
193*4342bf63SThomas Richard MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver");
194*4342bf63SThomas Richard MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
195*4342bf63SThomas Richard MODULE_LICENSE("GPL");
196*4342bf63SThomas Richard MODULE_ALIAS("platform:cgbc-gpio");
197