gpio: Add Aspeed GPIO driver
[platform/kernel/u-boot.git] / drivers / gpio / gpio-aspeed.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2015 IBM Corp.
4  * Joel Stanley <joel@jms.id.au>
5  * Ryan Chen <ryan_chen@aspeedtech.com>
6  *
7  * Implementation extracted from the Linux kernel and adapted for u-boot.
8  */
9 #include <common.h>
10 #include <asm/io.h>
11 #include <asm/gpio.h>
12
13 #include <config.h>
14 #include <common.h>
15 #include <clk.h>
16 #include <dm.h>
17 #include <asm/io.h>
18 #include <linux/bug.h>
19 #include <linux/sizes.h>
20
21 struct aspeed_gpio_priv {
22         void *regs;
23 };
24
25 struct aspeed_gpio_bank {
26         u16             val_regs;       /* +0: Rd: read input value, Wr: set write latch
27                                          * +4: Rd/Wr: Direction (0=in, 1=out)
28                                          */
29         u16             rdata_reg;      /*     Rd: read write latch, Wr: <none>  */
30         u16             irq_regs;
31         u16             debounce_regs;
32         u16             tolerance_regs;
33         u16             cmdsrc_regs;
34         const char      names[4][3];
35 };
36
37 static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
38         {
39                 .val_regs = 0x0000,
40                 .rdata_reg = 0x00c0,
41                 .irq_regs = 0x0008,
42                 .debounce_regs = 0x0040,
43                 .tolerance_regs = 0x001c,
44                 .cmdsrc_regs = 0x0060,
45                 .names = { "A", "B", "C", "D" },
46         },
47         {
48                 .val_regs = 0x0020,
49                 .rdata_reg = 0x00c4,
50                 .irq_regs = 0x0028,
51                 .debounce_regs = 0x0048,
52                 .tolerance_regs = 0x003c,
53                 .cmdsrc_regs = 0x0068,
54                 .names = { "E", "F", "G", "H" },
55         },
56         {
57                 .val_regs = 0x0070,
58                 .rdata_reg = 0x00c8,
59                 .irq_regs = 0x0098,
60                 .debounce_regs = 0x00b0,
61                 .tolerance_regs = 0x00ac,
62                 .cmdsrc_regs = 0x0090,
63                 .names = { "I", "J", "K", "L" },
64         },
65         {
66                 .val_regs = 0x0078,
67                 .rdata_reg = 0x00cc,
68                 .irq_regs = 0x00e8,
69                 .debounce_regs = 0x0100,
70                 .tolerance_regs = 0x00fc,
71                 .cmdsrc_regs = 0x00e0,
72                 .names = { "M", "N", "O", "P" },
73         },
74         {
75                 .val_regs = 0x0080,
76                 .rdata_reg = 0x00d0,
77                 .irq_regs = 0x0118,
78                 .debounce_regs = 0x0130,
79                 .tolerance_regs = 0x012c,
80                 .cmdsrc_regs = 0x0110,
81                 .names = { "Q", "R", "S", "T" },
82         },
83         {
84                 .val_regs = 0x0088,
85                 .rdata_reg = 0x00d4,
86                 .irq_regs = 0x0148,
87                 .debounce_regs = 0x0160,
88                 .tolerance_regs = 0x015c,
89                 .cmdsrc_regs = 0x0140,
90                 .names = { "U", "V", "W", "X" },
91         },
92         {
93                 .val_regs = 0x01E0,
94                 .rdata_reg = 0x00d8,
95                 .irq_regs = 0x0178,
96                 .debounce_regs = 0x0190,
97                 .tolerance_regs = 0x018c,
98                 .cmdsrc_regs = 0x0170,
99                 .names = { "Y", "Z", "AA", "AB" },
100         },
101         {
102                 .val_regs = 0x01e8,
103                 .rdata_reg = 0x00dc,
104                 .irq_regs = 0x01a8,
105                 .debounce_regs = 0x01c0,
106                 .tolerance_regs = 0x01bc,
107                 .cmdsrc_regs = 0x01a0,
108                 .names = { "AC", "", "", "" },
109         },
110 };
111
112 enum aspeed_gpio_reg {
113         reg_val,
114         reg_rdata,
115         reg_dir,
116         reg_irq_enable,
117         reg_irq_type0,
118         reg_irq_type1,
119         reg_irq_type2,
120         reg_irq_status,
121         reg_debounce_sel1,
122         reg_debounce_sel2,
123         reg_tolerance,
124         reg_cmdsrc0,
125         reg_cmdsrc1,
126 };
127
128 #define GPIO_VAL_VALUE  0x00
129 #define GPIO_VAL_DIR    0x04
130
131 #define GPIO_IRQ_ENABLE 0x00
132 #define GPIO_IRQ_TYPE0  0x04
133 #define GPIO_IRQ_TYPE1  0x08
134 #define GPIO_IRQ_TYPE2  0x0c
135 #define GPIO_IRQ_STATUS 0x10
136
137 #define GPIO_DEBOUNCE_SEL1 0x00
138 #define GPIO_DEBOUNCE_SEL2 0x04
139
140 #define GPIO_CMDSRC_0   0x00
141 #define GPIO_CMDSRC_1   0x04
142 #define  GPIO_CMDSRC_ARM                0
143 #define  GPIO_CMDSRC_LPC                1
144 #define  GPIO_CMDSRC_COLDFIRE           2
145 #define  GPIO_CMDSRC_RESERVED           3
146
147 /* This will be resolved at compile time */
148 static inline void __iomem *bank_reg(struct aspeed_gpio_priv *gpio,
149                                      const struct aspeed_gpio_bank *bank,
150                                      const enum aspeed_gpio_reg reg)
151 {
152         switch (reg) {
153         case reg_val:
154                 return gpio->regs + bank->val_regs + GPIO_VAL_VALUE;
155         case reg_rdata:
156                 return gpio->regs + bank->rdata_reg;
157         case reg_dir:
158                 return gpio->regs + bank->val_regs + GPIO_VAL_DIR;
159         case reg_irq_enable:
160                 return gpio->regs + bank->irq_regs + GPIO_IRQ_ENABLE;
161         case reg_irq_type0:
162                 return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE0;
163         case reg_irq_type1:
164                 return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE1;
165         case reg_irq_type2:
166                 return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE2;
167         case reg_irq_status:
168                 return gpio->regs + bank->irq_regs + GPIO_IRQ_STATUS;
169         case reg_debounce_sel1:
170                 return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL1;
171         case reg_debounce_sel2:
172                 return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
173         case reg_tolerance:
174                 return gpio->regs + bank->tolerance_regs;
175         case reg_cmdsrc0:
176                 return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_0;
177         case reg_cmdsrc1:
178                 return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_1;
179         }
180         BUG();
181 }
182
183 #define GPIO_BANK(x)    ((x) >> 5)
184 #define GPIO_OFFSET(x)  ((x) & 0x1f)
185 #define GPIO_BIT(x)     BIT(GPIO_OFFSET(x))
186
187 static const struct aspeed_gpio_bank *to_bank(unsigned int offset)
188 {
189         unsigned int bank = GPIO_BANK(offset);
190
191         WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks));
192         return &aspeed_gpio_banks[bank];
193 }
194
195 static int
196 aspeed_gpio_direction_input(struct udevice *dev, unsigned int offset)
197 {
198         struct aspeed_gpio_priv *priv = dev_get_priv(dev);
199         const struct aspeed_gpio_bank *bank = to_bank(offset);
200         u32 dir = readl(bank_reg(priv, bank, reg_dir));
201
202         dir &= ~GPIO_BIT(offset);
203         writel(dir, bank_reg(priv, bank, reg_dir));
204
205         return 0;
206 }
207
208 static int aspeed_gpio_direction_output(struct udevice *dev, unsigned int offset,
209                                         int value)
210 {
211         struct aspeed_gpio_priv *priv = dev_get_priv(dev);
212         const struct aspeed_gpio_bank *bank = to_bank(offset);
213         u32 dir = readl(bank_reg(priv, bank, reg_dir));
214         u32 output = readl(bank_reg(priv, bank, reg_val));
215
216         dir |= GPIO_BIT(offset);
217         writel(dir, bank_reg(priv, bank, reg_dir));
218
219         if (value)
220                 output |= GPIO_BIT(offset);
221         else
222                 output &= ~GPIO_BIT(offset);
223
224         writel(output, bank_reg(priv, bank, reg_val));
225
226         return 0;
227 }
228
229 static int aspeed_gpio_get_value(struct udevice *dev, unsigned int offset)
230 {
231         struct aspeed_gpio_priv *priv = dev_get_priv(dev);
232         const struct aspeed_gpio_bank *bank = to_bank(offset);
233
234         return !!(readl(bank_reg(priv, bank, reg_val)) & GPIO_BIT(offset));
235 }
236
237 static int
238 aspeed_gpio_set_value(struct udevice *dev, unsigned int offset, int value)
239 {
240         struct aspeed_gpio_priv *priv = dev_get_priv(dev);
241         const struct aspeed_gpio_bank *bank = to_bank(offset);
242         u32 data = readl(bank_reg(priv, bank, reg_val));
243
244         if (value)
245                 data |= GPIO_BIT(offset);
246         else
247                 data &= ~GPIO_BIT(offset);
248
249         writel(data, bank_reg(priv, bank, reg_val));
250
251         return 0;
252 }
253
254 static int aspeed_gpio_get_function(struct udevice *dev, unsigned int offset)
255 {
256         struct aspeed_gpio_priv *priv = dev_get_priv(dev);
257         const struct aspeed_gpio_bank *bank = to_bank(offset);
258
259         if (readl(bank_reg(priv, bank, reg_dir)) & GPIO_BIT(offset))
260                 return GPIOF_OUTPUT;
261
262         return GPIOF_INPUT;
263 }
264
265 static const struct dm_gpio_ops aspeed_gpio_ops = {
266         .direction_input        = aspeed_gpio_direction_input,
267         .direction_output       = aspeed_gpio_direction_output,
268         .get_value              = aspeed_gpio_get_value,
269         .set_value              = aspeed_gpio_set_value,
270         .get_function           = aspeed_gpio_get_function,
271 };
272
273 static int aspeed_gpio_probe(struct udevice *dev)
274 {
275         struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
276         struct aspeed_gpio_priv *priv = dev_get_priv(dev);
277
278         uc_priv->bank_name = dev->name;
279         ofnode_read_u32(dev_ofnode(dev), "ngpios", &uc_priv->gpio_count);
280         priv->regs = devfdt_get_addr_ptr(dev);
281
282         return 0;
283 }
284
285 static const struct udevice_id aspeed_gpio_ids[] = {
286         { .compatible = "aspeed,ast2400-gpio",  },
287         { .compatible = "aspeed,ast2500-gpio",  },
288         { .compatible = "aspeed,ast2600-gpio",  },
289         { }
290 };
291
292 U_BOOT_DRIVER(gpio_aspeed) = {
293         .name   = "gpio-aspeed",
294         .id     = UCLASS_GPIO,
295         .of_match = aspeed_gpio_ids,
296         .ops    = &aspeed_gpio_ops,
297         .probe  = aspeed_gpio_probe,
298         .priv_auto = sizeof(struct aspeed_gpio_priv),
299 };