blob: 3227a8d5b574eb074987870c6487b231fba0d980 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Peng Fanc5f49862017-02-22 16:21:45 +08002/*
3 * Copyright 2016 Freescale Semiconductor, Inc.
4 *
5 * RGPIO2P driver for the Freescale i.MX7ULP.
Peng Fanc5f49862017-02-22 16:21:45 +08006 */
7
8#include <common.h>
9#include <dm.h>
10#include <errno.h>
11#include <fdtdec.h>
12#include <asm/gpio.h>
13#include <asm/io.h>
Simon Glass95588622020-12-22 19:30:28 -070014#include <dm/device-internal.h>
Peng Fanc5f49862017-02-22 16:21:45 +080015#include <malloc.h>
16
Peng Fanc5f49862017-02-22 16:21:45 +080017enum imx_rgpio2p_direction {
18 IMX_RGPIO2P_DIRECTION_IN,
19 IMX_RGPIO2P_DIRECTION_OUT,
20};
21
22#define GPIO_PER_BANK 32
23
Peng Fanbcdc6162024-04-12 22:24:51 +080024struct imx_rgpio2p_soc_data {
25 bool have_dual_base;
26};
27
28#define IMX8ULP_GPIO_BASE_OFF 0x40
29
Peng Fanc5f49862017-02-22 16:21:45 +080030struct imx_rgpio2p_data {
31 struct gpio_regs *regs;
32};
33
34struct imx_rgpio2p_plat {
35 int bank_index;
36 struct gpio_regs *regs;
37};
38
39static int imx_rgpio2p_is_output(struct gpio_regs *regs, int offset)
40{
41 u32 val;
42
43 val = readl(&regs->gpio_pddr);
44
45 return val & (1 << offset) ? 1 : 0;
46}
47
Christoph Fritzd76d9c82022-04-05 12:29:30 +020048static int imx_rgpio2p_bank_get_direction(struct gpio_regs *regs, int offset)
49{
50 if ((readl(&regs->gpio_pddr) >> offset) & 0x01)
51 return IMX_RGPIO2P_DIRECTION_OUT;
52
53 return IMX_RGPIO2P_DIRECTION_IN;
54}
55
Peng Fanc5f49862017-02-22 16:21:45 +080056static void imx_rgpio2p_bank_direction(struct gpio_regs *regs, int offset,
57 enum imx_rgpio2p_direction direction)
58{
59 u32 l;
60
61 l = readl(&regs->gpio_pddr);
62
63 switch (direction) {
64 case IMX_RGPIO2P_DIRECTION_OUT:
65 l |= 1 << offset;
66 break;
67 case IMX_RGPIO2P_DIRECTION_IN:
68 l &= ~(1 << offset);
69 }
70 writel(l, &regs->gpio_pddr);
71}
72
73static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset,
74 int value)
75{
76 if (value)
77 writel((1 << offset), &regs->gpio_psor);
78 else
79 writel((1 << offset), &regs->gpio_pcor);
80}
81
82static int imx_rgpio2p_bank_get_value(struct gpio_regs *regs, int offset)
83{
Christoph Fritzd76d9c82022-04-05 12:29:30 +020084 if (imx_rgpio2p_bank_get_direction(regs, offset) ==
85 IMX_RGPIO2P_DIRECTION_IN)
86 return (readl(&regs->gpio_pdir) >> offset) & 0x01;
87
88 return (readl(&regs->gpio_pdor) >> offset) & 0x01;
Peng Fanc5f49862017-02-22 16:21:45 +080089}
90
91static int imx_rgpio2p_direction_input(struct udevice *dev, unsigned offset)
92{
93 struct imx_rgpio2p_data *bank = dev_get_priv(dev);
94
95 /* Configure GPIO direction as input. */
96 imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_IN);
97
98 return 0;
99}
100
101static int imx_rgpio2p_direction_output(struct udevice *dev, unsigned offset,
102 int value)
103{
104 struct imx_rgpio2p_data *bank = dev_get_priv(dev);
105
106 /* Configure GPIO output value. */
107 imx_rgpio2p_bank_set_value(bank->regs, offset, value);
108
109 /* Configure GPIO direction as output. */
110 imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_OUT);
111
112 return 0;
113}
114
115static int imx_rgpio2p_get_value(struct udevice *dev, unsigned offset)
116{
117 struct imx_rgpio2p_data *bank = dev_get_priv(dev);
118
119 return imx_rgpio2p_bank_get_value(bank->regs, offset);
120}
121
122static int imx_rgpio2p_set_value(struct udevice *dev, unsigned offset,
123 int value)
124{
125 struct imx_rgpio2p_data *bank = dev_get_priv(dev);
126
127 imx_rgpio2p_bank_set_value(bank->regs, offset, value);
128
129 return 0;
130}
131
132static int imx_rgpio2p_get_function(struct udevice *dev, unsigned offset)
133{
134 struct imx_rgpio2p_data *bank = dev_get_priv(dev);
135
136 /* GPIOF_FUNC is not implemented yet */
137 if (imx_rgpio2p_is_output(bank->regs, offset))
138 return GPIOF_OUTPUT;
139 else
140 return GPIOF_INPUT;
141}
142
143static const struct dm_gpio_ops imx_rgpio2p_ops = {
144 .direction_input = imx_rgpio2p_direction_input,
145 .direction_output = imx_rgpio2p_direction_output,
146 .get_value = imx_rgpio2p_get_value,
147 .set_value = imx_rgpio2p_set_value,
148 .get_function = imx_rgpio2p_get_function,
149};
150
151static int imx_rgpio2p_probe(struct udevice *dev)
152{
153 struct imx_rgpio2p_data *bank = dev_get_priv(dev);
Simon Glassfa20e932020-12-03 16:55:20 -0700154 struct imx_rgpio2p_plat *plat = dev_get_plat(dev);
Peng Fanc5f49862017-02-22 16:21:45 +0800155 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
156 int banknum;
157 char name[18], *str;
158
159 banknum = plat->bank_index;
160 sprintf(name, "GPIO%d_", banknum + 1);
161 str = strdup(name);
162 if (!str)
163 return -ENOMEM;
164 uc_priv->bank_name = str;
165 uc_priv->gpio_count = GPIO_PER_BANK;
166 bank->regs = plat->regs;
167
168 return 0;
169}
170
171static int imx_rgpio2p_bind(struct udevice *dev)
172{
Simon Glass95588622020-12-22 19:30:28 -0700173 struct imx_rgpio2p_plat *plat = dev_get_plat(dev);
Peng Fanbcdc6162024-04-12 22:24:51 +0800174 struct imx_rgpio2p_soc_data *data =
175 (struct imx_rgpio2p_soc_data *)dev_get_driver_data(dev);
176 bool dual_base = data->have_dual_base;
Peng Fanc5f49862017-02-22 16:21:45 +0800177 fdt_addr_t addr;
178
179 /*
Simon Glass71fa5b42020-12-03 16:55:18 -0700180 * If plat already exsits, directly return.
181 * Actually only when DT is not supported, plat
Simon Glass1d8364a2020-12-28 20:34:54 -0700182 * is statically initialized in U_BOOT_DRVINFOS.Here
Peng Fanc5f49862017-02-22 16:21:45 +0800183 * will return.
184 */
185 if (plat)
186 return 0;
187
Peng Fanbcdc6162024-04-12 22:24:51 +0800188 /*
189 * Handle legacy compatible combinations which used two reg values
190 * for the i.MX8ULP and i.MX93.
191 */
192 if (device_is_compatible(dev, "fsl,imx7ulp-gpio") &&
193 (device_is_compatible(dev, "fsl,imx93-gpio") ||
194 (device_is_compatible(dev, "fsl,imx8ulp-gpio"))))
195 dual_base = true;
196
197 if (dual_base) {
198 addr = devfdt_get_addr_index(dev, 1);
199 if (addr == FDT_ADDR_T_NONE)
200 return -EINVAL;
201 } else {
202 addr = devfdt_get_addr_index(dev, 0);
203 if (addr == FDT_ADDR_T_NONE)
204 return -EINVAL;
205
206 addr += IMX8ULP_GPIO_BASE_OFF;
207 }
Peng Fanc5f49862017-02-22 16:21:45 +0800208
209 /*
210 * TODO:
211 * When every board is converted to driver model and DT is supported,
212 * this can be done by auto-alloc feature, but not using calloc
Simon Glass71fa5b42020-12-03 16:55:18 -0700213 * to alloc memory for plat.
Simon Glass87f4cb72017-09-17 16:54:52 -0600214 *
215 * For example imx_rgpio2p_plat uses platform data rather than device
216 * tree.
217 *
218 * NOTE: DO NOT COPY this code if you are using device tree.
Peng Fanc5f49862017-02-22 16:21:45 +0800219 */
220 plat = calloc(1, sizeof(*plat));
221 if (!plat)
222 return -ENOMEM;
223
224 plat->regs = (struct gpio_regs *)addr;
Simon Glassb4db3722020-12-16 21:20:24 -0700225 plat->bank_index = dev_seq(dev);
Simon Glass95588622020-12-22 19:30:28 -0700226 dev_set_plat(dev, plat);
Peng Fanc5f49862017-02-22 16:21:45 +0800227
228 return 0;
229}
230
Peng Fanbcdc6162024-04-12 22:24:51 +0800231static struct imx_rgpio2p_soc_data imx7ulp_data = {
232 .have_dual_base = true,
233};
234
235static struct imx_rgpio2p_soc_data imx8ulp_data = {
236 .have_dual_base = false,
237};
Peng Fanc5f49862017-02-22 16:21:45 +0800238
239static const struct udevice_id imx_rgpio2p_ids[] = {
Peng Fanbcdc6162024-04-12 22:24:51 +0800240 { .compatible = "fsl,imx7ulp-gpio", .data = (ulong)&imx7ulp_data },
241 { .compatible = "fsl,imx8ulp-gpio", .data = (ulong)&imx8ulp_data },
Peng Fanc5f49862017-02-22 16:21:45 +0800242 { }
243};
244
245U_BOOT_DRIVER(imx_rgpio2p) = {
246 .name = "imx_rgpio2p",
247 .id = UCLASS_GPIO,
248 .ops = &imx_rgpio2p_ops,
249 .probe = imx_rgpio2p_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700250 .priv_auto = sizeof(struct imx_rgpio2p_plat),
Peng Fanc5f49862017-02-22 16:21:45 +0800251 .of_match = imx_rgpio2p_ids,
252 .bind = imx_rgpio2p_bind,
253};
254
255#if !CONFIG_IS_ENABLED(OF_CONTROL)
256static const struct imx_rgpio2p_plat imx_plat[] = {
257 { 0, (struct gpio_regs *)RGPIO2P_GPIO1_BASE_ADDR },
258 { 1, (struct gpio_regs *)RGPIO2P_GPIO2_BASE_ADDR },
259 { 2, (struct gpio_regs *)RGPIO2P_GPIO3_BASE_ADDR },
260 { 3, (struct gpio_regs *)RGPIO2P_GPIO4_BASE_ADDR },
261 { 4, (struct gpio_regs *)RGPIO2P_GPIO5_BASE_ADDR },
262 { 5, (struct gpio_regs *)RGPIO2P_GPIO6_BASE_ADDR },
263};
264
Simon Glass1d8364a2020-12-28 20:34:54 -0700265U_BOOT_DRVINFOS(imx_rgpio2ps) = {
Peng Fanc5f49862017-02-22 16:21:45 +0800266 { "imx_rgpio2p", &imx_plat[0] },
267 { "imx_rgpio2p", &imx_plat[1] },
268 { "imx_rgpio2p", &imx_plat[2] },
269 { "imx_rgpio2p", &imx_plat[3] },
270 { "imx_rgpio2p", &imx_plat[4] },
271 { "imx_rgpio2p", &imx_plat[5] },
272};
273#endif