blob: 6d73287c0a242437adbd7629331627be042ee7bb [file] [log] [blame]
Jim Liu0d500532024-07-29 16:51:03 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2024 Nuvoton Technology Corp.
4 */
5
6#include <dm.h>
7#include <asm/gpio.h>
8#include <linux/io.h>
9
10#define MAX_NR_HW_SGPIO 64
11#define NPCM_CLK_MHZ 8000000
12
13#define NPCM_IOXCFG1 0x2A
14
15#define NPCM_IOXCTS 0x28
16#define NPCM_IOXCTS_IOXIF_EN BIT(7)
17#define NPCM_IOXCTS_RD_MODE GENMASK(2, 1)
18#define NPCM_IOXCTS_RD_MODE_PERIODIC BIT(2)
19
20#define NPCM_IOXCFG2 0x2B
21#define NPCM_IOXCFG2_PORT GENMASK(3, 0)
22
23#define GPIO_BANK(x) ((x) / 8)
24#define GPIO_BIT(x) ((x) % 8)
25
26struct npcm_sgpio_priv {
27 void __iomem *base;
28 u32 nin_sgpio;
29 u32 nout_sgpio;
30 u32 in_port;
31 u32 out_port;
32};
33
34struct npcm_sgpio_bank {
35 u8 rdata_reg;
36 u8 wdata_reg;
37 u8 event_config;
38 u8 event_status;
39};
40
41enum npcm_sgpio_reg {
42 READ_DATA,
43 WRITE_DATA,
44 EVENT_CFG,
45 EVENT_STS,
46};
47
48static const struct npcm_sgpio_bank npcm_sgpio_banks[] = {
49 {
50 .wdata_reg = 0x00,
51 .rdata_reg = 0x08,
52 .event_config = 0x10,
53 .event_status = 0x20,
54 },
55 {
56 .wdata_reg = 0x01,
57 .rdata_reg = 0x09,
58 .event_config = 0x12,
59 .event_status = 0x21,
60 },
61 {
62 .wdata_reg = 0x02,
63 .rdata_reg = 0x0a,
64 .event_config = 0x14,
65 .event_status = 0x22,
66 },
67 {
68 .wdata_reg = 0x03,
69 .rdata_reg = 0x0b,
70 .event_config = 0x16,
71 .event_status = 0x23,
72 },
73 {
74 .wdata_reg = 0x04,
75 .rdata_reg = 0x0c,
76 .event_config = 0x18,
77 .event_status = 0x24,
78 },
79 {
80 .wdata_reg = 0x05,
81 .rdata_reg = 0x0d,
82 .event_config = 0x1a,
83 .event_status = 0x25,
84 },
85 {
86 .wdata_reg = 0x06,
87 .rdata_reg = 0x0e,
88 .event_config = 0x1c,
89 .event_status = 0x26,
90 },
91 {
92 .wdata_reg = 0x07,
93 .rdata_reg = 0x0f,
94 .event_config = 0x1e,
95 .event_status = 0x27,
96 },
97};
98
99static void __iomem *bank_reg(struct npcm_sgpio_priv *gpio,
100 const struct npcm_sgpio_bank *bank,
101 const enum npcm_sgpio_reg reg)
102{
103 switch (reg) {
104 case READ_DATA:
105 return gpio->base + bank->rdata_reg;
106 case WRITE_DATA:
107 return gpio->base + bank->wdata_reg;
108 case EVENT_CFG:
109 return gpio->base + bank->event_config;
110 case EVENT_STS:
111 return gpio->base + bank->event_status;
112 default:
113 /* actually if code runs to here, it's an error case */
114 printf("Getting here is an error condition\n");
115 return NULL;
116 }
117}
118
119static const struct npcm_sgpio_bank *offset_to_bank(unsigned int offset)
120{
121 unsigned int bank = GPIO_BANK(offset);
122
123 return &npcm_sgpio_banks[bank];
124}
125
126static int npcm_sgpio_direction_input(struct udevice *dev, unsigned int offset)
127{
128 struct npcm_sgpio_priv *priv = dev_get_priv(dev);
129
130 if (offset < priv->nout_sgpio) {
131 printf("Error: Offset %d is a output pin\n", offset);
132 return -EINVAL;
133 }
134
135 return 0;
136}
137
138static int npcm_sgpio_direction_output(struct udevice *dev, unsigned int offset,
139 int value)
140{
141 struct npcm_sgpio_priv *priv = dev_get_priv(dev);
142 const struct npcm_sgpio_bank *bank = offset_to_bank(offset);
143 void __iomem *addr;
144 u8 reg = 0;
145
146 if (offset >= priv->nout_sgpio) {
147 printf("Error: Offset %d is a input pin\n", offset);
148 return -EINVAL;
149 }
150
151 addr = bank_reg(priv, bank, WRITE_DATA);
152 reg = ioread8(addr);
153
154 if (value)
155 reg |= BIT(GPIO_BIT(offset));
156 else
157 reg &= ~BIT(GPIO_BIT(offset));
158
159 iowrite8(reg, addr);
160
161 return 0;
162}
163
164static int npcm_sgpio_get_value(struct udevice *dev, unsigned int offset)
165{
166 struct npcm_sgpio_priv *priv = dev_get_priv(dev);
167 const struct npcm_sgpio_bank *bank;
168 void __iomem *addr;
169 u8 reg;
170
171 if (offset < priv->nout_sgpio) {
172 bank = offset_to_bank(offset);
173 addr = bank_reg(priv, bank, WRITE_DATA);
174 } else {
175 offset -= priv->nout_sgpio;
176 bank = offset_to_bank(offset);
177 addr = bank_reg(priv, bank, READ_DATA);
178 }
179
180 reg = ioread8(addr);
181
182 return !!(reg & BIT(GPIO_BIT(offset)));
183}
184
185static int npcm_sgpio_set_value(struct udevice *dev, unsigned int offset,
186 int value)
187{
188 return npcm_sgpio_direction_output(dev, offset, value);
189}
190
191static int npcm_sgpio_get_function(struct udevice *dev, unsigned int offset)
192{
193 struct npcm_sgpio_priv *priv = dev_get_priv(dev);
194
195 if (offset < priv->nout_sgpio)
196 return GPIOF_OUTPUT;
197
198 return GPIOF_INPUT;
199}
200
201static void npcm_sgpio_setup_enable(struct npcm_sgpio_priv *gpio, bool enable)
202{
203 u8 reg;
204
205 reg = ioread8(gpio->base + NPCM_IOXCTS);
206 reg = (reg & ~NPCM_IOXCTS_RD_MODE) | NPCM_IOXCTS_RD_MODE_PERIODIC;
207
208 if (enable)
209 reg |= NPCM_IOXCTS_IOXIF_EN;
210 else
211 reg &= ~NPCM_IOXCTS_IOXIF_EN;
212
213 iowrite8(reg, gpio->base + NPCM_IOXCTS);
214}
215
216static int npcm_sgpio_init_port(struct udevice *dev)
217{
218 struct npcm_sgpio_priv *priv = dev_get_priv(dev);
219 u8 in_port, out_port, set_port, reg, set_clk;
220
221 npcm_sgpio_setup_enable(priv, false);
222
223 in_port = GPIO_BANK(priv->nin_sgpio);
224 if (GPIO_BIT(priv->nin_sgpio) > 0)
225 in_port += 1;
226
227 out_port = GPIO_BANK(priv->nout_sgpio);
228 if (GPIO_BIT(priv->nout_sgpio) > 0)
229 out_port += 1;
230
231 priv->in_port = in_port;
232 priv->out_port = out_port;
233
234 set_port = (out_port & NPCM_IOXCFG2_PORT) << 4 | (in_port & NPCM_IOXCFG2_PORT);
235 set_clk = 0x07;
236
237 iowrite8(set_port, priv->base + NPCM_IOXCFG2);
238 iowrite8(set_clk, priv->base + NPCM_IOXCFG1);
239
240 reg = ioread8(priv->base + NPCM_IOXCFG2);
241
242 return reg == set_port ? 0 : -EINVAL;
243}
244
245static const struct dm_gpio_ops npcm_sgpio_ops = {
246 .direction_input = npcm_sgpio_direction_input,
247 .direction_output = npcm_sgpio_direction_output,
248 .get_value = npcm_sgpio_get_value,
249 .set_value = npcm_sgpio_set_value,
250 .get_function = npcm_sgpio_get_function,
251};
252
253static int npcm_sgpio_probe(struct udevice *dev)
254{
255 struct npcm_sgpio_priv *priv = dev_get_priv(dev);
256 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
257 int rc;
258
259 priv->base = dev_read_addr_ptr(dev);
260 ofnode_read_u32(dev_ofnode(dev), "nuvoton,input-ngpios", &priv->nin_sgpio);
261 ofnode_read_u32(dev_ofnode(dev), "nuvoton,output-ngpios", &priv->nout_sgpio);
262
263 if (priv->nin_sgpio > MAX_NR_HW_SGPIO || priv->nout_sgpio > MAX_NR_HW_SGPIO)
264 return -EINVAL;
265
266 rc = npcm_sgpio_init_port(dev);
267 if (rc < 0)
268 return rc;
269
270 uc_priv->gpio_count = priv->nin_sgpio + priv->nout_sgpio;
271 uc_priv->bank_name = dev->name;
272
273 npcm_sgpio_setup_enable(priv, true);
274
275 return 0;
276}
277
278static const struct udevice_id npcm_sgpio_match[] = {
279 { .compatible = "nuvoton,npcm845-sgpio" },
280 { .compatible = "nuvoton,npcm750-sgpio" },
281 { }
282};
283
284U_BOOT_DRIVER(npcm_sgpio) = {
285 .name = "npcm_sgpio",
286 .id = UCLASS_GPIO,
287 .of_match = npcm_sgpio_match,
288 .probe = npcm_sgpio_probe,
289 .priv_auto = sizeof(struct npcm_sgpio_priv),
290 .ops = &npcm_sgpio_ops,
291};