]> Git Repo - J-linux.git/blob - drivers/gpio/gpio-cgbc.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / gpio / gpio-cgbc.c
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 <[email protected]>
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 <[email protected]>");
195 MODULE_LICENSE("GPL");
196 MODULE_ALIAS("platform:cgbc-gpio");
This page took 0.036683 seconds and 4 git commands to generate.