xref: /linux/drivers/gpio/gpio-cgbc.c (revision 98b5f7706f6bfd863e50317742f7d566b86fabdc)
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