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