blob: d081e16989713b7dfbd0d4fdadea8dab6e243583 [file] [log] [blame]
Nathan Barrett-Morrison5315ff82025-02-26 12:30:26 -05001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * GPIO Chip driver for Analog Devices
4 * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
5 *
6 * (C) Copyright 2022 - Analog Devices, Inc.
7 *
8 * Written and/or maintained by Timesys Corporation
9 *
10 * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
11 * Contact: Greg Malysa <greg.malysa@timesys.com>
12 *
13 * Based on Michael Hennerich's Linux driver:
14 * Michael Hennerich <michael.hennerich@analog.com>
15 *
16 */
17
18#include <dm.h>
19#include <i2c.h>
20#include <asm-generic/gpio.h>
21
22#define ADP5588_MAXGPIO 18
23#define ADP5588_BANK(offs) ((offs) >> 3)
24#define ADP5588_BIT(offs) (1u << ((offs) & 0x7))
25
26#define DEV_ID 0x00 /* Device ID */
27#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */
28#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */
29#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */
30#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */
31#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */
32#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */
33#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */
34#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */
35#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */
36#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */
37#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */
38#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */
39#define GPIO_DIR1 0x23 /* GPIO Data Direction */
40#define GPIO_DIR2 0x24 /* GPIO Data Direction */
41#define GPIO_DIR3 0x25 /* GPIO Data Direction */
42#define GPIO_PULL1 0x2C /* GPIO Pull Disable */
43#define GPIO_PULL2 0x2D /* GPIO Pull Disable */
44#define GPIO_PULL3 0x2E /* GPIO Pull Disable */
45#define ID_MASK 0x0F
46
47struct adp5588_gpio {
48 u8 dat_out[3];
49 u8 dir[3];
50};
51
52static int adp5588_gpio_read(struct udevice *dev, u8 reg)
53{
54 int ret;
55 u8 val;
56
57 ret = dm_i2c_read(dev, reg, &val, 1);
58
59 if (ret < 0) {
60 pr_err("%s: read error\n", __func__);
61 return ret;
62 }
63
64 return val;
65}
66
67static int adp5588_gpio_write(struct udevice *dev, u8 reg, u8 val)
68{
69 int ret;
70
71 ret = dm_i2c_write(dev, reg, &val, 1);
72 if (ret < 0) {
73 pr_err("%s: write error\n", __func__);
74 return ret;
75 }
76
77 return 0;
78}
79
80static int adp5588_get_value(struct udevice *dev, u32 offset)
81{
82 struct adp5588_gpio *plat = dev_get_plat(dev);
83 unsigned int bank = ADP5588_BANK(offset);
84 unsigned int bit = ADP5588_BIT(offset);
85 int val;
86
87 if (plat->dir[bank] & bit)
88 val = plat->dat_out[bank];
89 else
90 val = adp5588_gpio_read(dev, GPIO_DAT_STAT1 + bank);
91
92 return !!(val & bit);
93}
94
95static int adp5588_set_value(struct udevice *dev, u32 offset,
96 int32_t value)
97{
98 unsigned int bank, bit;
99 int ret;
100 struct adp5588_gpio *plat = dev_get_plat(dev);
101
102 bank = ADP5588_BANK(offset);
103 bit = ADP5588_BIT(offset);
104
105 if (value)
106 plat->dat_out[bank] |= bit;
107 else
108 plat->dat_out[bank] &= ~bit;
109
110 ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
111 plat->dat_out[bank]);
112
113 return ret;
114}
115
116static int adp5588_direction_input(struct udevice *dev, u32 offset)
117{
118 int ret;
119 unsigned int bank;
120 struct adp5588_gpio *plat = dev_get_plat(dev);
121
122 bank = ADP5588_BANK(offset);
123
124 plat->dir[bank] &= ~ADP5588_BIT(offset);
125 ret = adp5588_gpio_write(dev, GPIO_DIR1 + bank, plat->dir[bank]);
126
127 return ret;
128}
129
130static int adp5588_direction_output(struct udevice *dev,
131 u32 offset, int value)
132{
133 int ret;
134 unsigned int bank, bit;
135 struct adp5588_gpio *plat = dev_get_plat(dev);
136
137 bank = ADP5588_BANK(offset);
138 bit = ADP5588_BIT(offset);
139
140 plat->dir[bank] |= bit;
141
142 if (value)
143 plat->dat_out[bank] |= bit;
144 else
145 plat->dat_out[bank] &= ~bit;
146
147 ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
148 plat->dat_out[bank]);
149 ret |= adp5588_gpio_write(dev, GPIO_DIR1 + bank,
150 plat->dir[bank]);
151
152 return ret;
153}
154
155static int adp5588_ofdata_platdata(struct udevice *dev)
156{
157 struct adp5588_gpio *plat = dev_get_plat(dev);
158 struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
159 int node = dev_of_offset(dev);
160 int ret, i, revid;
161
162 priv->gpio_count = ADP5588_MAXGPIO;
163 priv->bank_name = fdt_get_name(gd->fdt_blob, node, NULL);
164
165 ret = adp5588_gpio_read(dev, DEV_ID);
166 if (ret < 0)
167 return ret;
168
169 revid = ret & ID_MASK;
170
171 printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid);
172
173 for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
174 plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i);
175 plat->dir[i] = adp5588_gpio_read(dev, GPIO_DIR1 + i);
176 ret |= adp5588_gpio_write(dev, KP_GPIO1 + i, 0);
177 ret |= adp5588_gpio_write(dev, GPIO_PULL1 + i, 0);
178 ret |= adp5588_gpio_write(dev, GPIO_INT_EN1 + i, 0);
179 if (ret) {
180 pr_err("%s: Initialization error\n", __func__);
181 return ret;
182 }
183 }
184
185 return 0;
186}
187
188static const struct dm_gpio_ops adp5588_ops = {
189 .direction_input = adp5588_direction_input,
190 .direction_output = adp5588_direction_output,
191 .get_value = adp5588_get_value,
192 .set_value = adp5588_set_value,
193};
194
195static const struct udevice_id adp5588_of_match_list[] = {
196 { .compatible = "adi,adp5588"},
197 { /* sentinel */ }
198};
199
200U_BOOT_DRIVER(gpio_adp5588) = {
201 .name = "gpio_adp5588",
202 .id = UCLASS_GPIO,
203 .ops = &adp5588_ops,
204 .of_match = adp5588_of_match_list,
205 .of_to_plat = adp5588_ofdata_platdata,
206 .plat_auto = sizeof(struct adp5588_gpio),
207 .flags = DM_FLAG_PRE_RELOC,
208};