1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices LTC4283 GPIO driver
*
* Copyright 2025 Analog Devices Inc.
*/
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/regmap.h>
#define LTC4283_PINS_MAX 8
#define LTC4283_PGIOX_START_NR 4
#define LTC4283_INPUT_STATUS 0x02
#define LTC4283_PGIO_CONFIG 0x10
#define LTC4283_PGIO_CFG_MASK(pin) \
GENMASK(((pin) - LTC4283_PGIOX_START_NR) * 2 + 1, (((pin) - LTC4283_PGIOX_START_NR) * 2))
#define LTC4283_PGIO_CONFIG_2 0x11
#define LTC4283_ADIO_CONFIG 0x12
/* starts at bit 4 */
#define LTC4283_ADIOX_CONFIG_MASK(pin) BIT((pin) + 4)
#define LTC4283_PGIO_DIR_IN 3
#define LTC4283_PGIO_DIR_OUT 2
struct ltc4283_gpio {
struct gpio_chip gpio_chip;
struct regmap *regmap;
};
static int ltc4283_pgio_get_direction(const struct ltc4283_gpio *st, unsigned int off)
{
unsigned int val;
int ret;
ret = regmap_read(st->regmap, LTC4283_PGIO_CONFIG, &val);
if (ret)
return ret;
val = field_get(LTC4283_PGIO_CFG_MASK(off), val);
if (val == LTC4283_PGIO_DIR_IN)
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
}
static int ltc4283_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
{
struct ltc4283_gpio *st = gpiochip_get_data(gc);
unsigned int val;
int ret;
if (off >= LTC4283_PGIOX_START_NR)
return ltc4283_pgio_get_direction(st, off);
ret = regmap_read(st->regmap, LTC4283_ADIO_CONFIG, &val);
if (ret)
return ret;
if (val & LTC4283_ADIOX_CONFIG_MASK(off))
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
}
static int ltc4283_gpio_direction_set(const struct ltc4283_gpio *st,
unsigned int off, bool input)
{
if (off >= LTC4283_PGIOX_START_NR) {
unsigned int val = LTC4283_PGIO_DIR_OUT;
if (input)
val = LTC4283_PGIO_DIR_IN;
val = field_prep(LTC4283_PGIO_CFG_MASK(off), val);
return regmap_update_bits(st->regmap, LTC4283_PGIO_CONFIG,
LTC4283_PGIO_CFG_MASK(off), val);
}
return regmap_update_bits(st->regmap, LTC4283_ADIO_CONFIG,
LTC4283_ADIOX_CONFIG_MASK(off),
field_prep(LTC4283_ADIOX_CONFIG_MASK(off), input));
}
static int __ltc4283_gpio_set_value(const struct ltc4283_gpio *st,
unsigned int off, int val)
{
u32 reg = off < LTC4283_PGIOX_START_NR ? LTC4283_ADIO_CONFIG : LTC4283_PGIO_CONFIG_2;
return regmap_update_bits(st->regmap, reg, BIT(off),
field_prep(BIT(off), !!val));
}
static int ltc4283_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
{
struct ltc4283_gpio *st = gpiochip_get_data(gc);
return ltc4283_gpio_direction_set(st, off, true);
}
static int ltc4283_gpio_direction_output(struct gpio_chip *gc, unsigned int off, int val)
{
struct ltc4283_gpio *st = gpiochip_get_data(gc);
int ret;
ret = ltc4283_gpio_direction_set(st, off, false);
if (ret)
return ret;
return __ltc4283_gpio_set_value(st, off, val);
}
static int ltc4283_gpio_get_value(struct gpio_chip *gc, unsigned int off)
{
struct ltc4283_gpio *st = gpiochip_get_data(gc);
unsigned int val, reg;
int ret, dir;
dir = ltc4283_gpio_get_direction(gc, off);
if (dir < 0)
return dir;
if (dir == GPIO_LINE_DIRECTION_IN) {
ret = regmap_read(st->regmap, LTC4283_INPUT_STATUS, &val);
if (ret)
return ret;
/* ADIO1 is at bit 3. */
if (off < LTC4283_PGIOX_START_NR)
return !!(val & BIT(3 - off));
/* PGIO1 is at bit 7. */
return !!(val & BIT(7 - (off - LTC4283_PGIOX_START_NR)));
}
if (off < LTC4283_PGIOX_START_NR)
reg = LTC4283_ADIO_CONFIG;
else
reg = LTC4283_PGIO_CONFIG_2;
ret = regmap_read(st->regmap, reg, &val);
if (ret)
return ret;
return !!(val & BIT(off));
}
static int ltc4283_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val)
{
struct ltc4283_gpio *st = gpiochip_get_data(gc);
return __ltc4283_gpio_set_value(st, off, val);
}
static int ltc4283_init_valid_mask(struct gpio_chip *gc, unsigned long *valid_mask,
unsigned int ngpios)
{
unsigned long *mask = dev_get_platdata(gc->parent);
bitmap_copy(valid_mask, mask, ngpios);
return 0;
}
static int ltc4283_gpio_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct device *dev = &adev->dev;
struct ltc4283_gpio *st;
struct gpio_chip *gc;
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
st->regmap = dev_get_regmap(dev->parent, NULL);
if (!st->regmap)
return dev_err_probe(dev, -ENODEV,
"Failed to get regmap\n");
gc = &st->gpio_chip;
gc->parent = dev;
gc->get_direction = ltc4283_gpio_get_direction;
gc->direction_input = ltc4283_gpio_direction_input;
gc->direction_output = ltc4283_gpio_direction_output;
gc->get = ltc4283_gpio_get_value;
gc->set = ltc4283_gpio_set_value;
gc->init_valid_mask = ltc4283_init_valid_mask;
gc->can_sleep = true;
gc->base = -1;
gc->ngpio = LTC4283_PINS_MAX;
gc->label = adev->name;
gc->owner = THIS_MODULE;
return devm_gpiochip_add_data(dev, &st->gpio_chip, st);
}
static const struct auxiliary_device_id ltc4283_aux_id_table[] = {
{ "ltc4283.gpio" },
{ }
};
MODULE_DEVICE_TABLE(auxiliary, ltc4283_aux_id_table);
static struct auxiliary_driver ltc4283_gpio_driver = {
.probe = ltc4283_gpio_probe,
.id_table = ltc4283_aux_id_table,
};
module_auxiliary_driver(ltc4283_gpio_driver);
MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
MODULE_DESCRIPTION("GPIO LTC4283 Driver");
MODULE_LICENSE("GPL");
|