1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2025 Microchip Technology Inc.
4 * Eoin Dickson <eoin.dickson@microchip.com>
5 */
6
7 #include <dm.h>
8 #include <asm-generic/gpio.h>
9 #include <asm/io.h>
10 #include <errno.h>
11 #include <asm/gpio.h>
12 #include <linux/bitops.h>
13
14 #define MPFS_INP_REG 0x84
15 #define COREGPIO_INP_REG 0x90
16 #define MPFS_OUTP_REG 0x88
17 #define COREGPIO_OUTP_REG 0xA0
18 #define MPFS_GPIO_CTRL(i) (0x4 * (i))
19 #define MPFS_MAX_NUM_GPIO 32
20 #define MPFS_GPIO_EN_OUT_BUF BIT(2)
21 #define MPFS_GPIO_EN_IN BIT(1)
22 #define MPFS_GPIO_EN_OUT BIT(0)
23
24 struct mpfs_gpio_reg_offsets {
25 u8 inp;
26 u8 outp;
27 };
28
29 struct mchp_gpio_plat {
30 void *base;
31 const struct mpfs_gpio_reg_offsets *regs;
32 };
33
mchp_update_gpio_reg(void * bptr,u32 offset,bool value)34 static void mchp_update_gpio_reg(void *bptr, u32 offset, bool value)
35 {
36 void __iomem *ptr = (void __iomem *)bptr;
37
38 u32 old = readl(ptr);
39
40 if (value)
41 writel(old | offset, ptr);
42 else
43 writel(old & ~offset, ptr);
44 }
45
mchp_gpio_direction_input(struct udevice * dev,u32 offset)46 static int mchp_gpio_direction_input(struct udevice *dev, u32 offset)
47 {
48 struct mchp_gpio_plat *plat = dev_get_plat(dev);
49 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
50
51 if (offset > uc_priv->gpio_count)
52 return -EINVAL;
53
54 mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_IN, true);
55 mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT, false);
56 mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT_BUF, false);
57
58 return 0;
59 }
60
mchp_gpio_direction_output(struct udevice * dev,u32 offset,int value)61 static int mchp_gpio_direction_output(struct udevice *dev, u32 offset, int value)
62 {
63 struct mchp_gpio_plat *plat = dev_get_plat(dev);
64 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
65
66 if (offset > uc_priv->gpio_count)
67 return -EINVAL;
68
69 mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_IN, false);
70 mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT, true);
71 mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT_BUF, true);
72
73 mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value);
74
75 return 0;
76 }
77
mchp_gpio_get_value(struct udevice * dev,u32 offset)78 static bool mchp_gpio_get_value(struct udevice *dev, u32 offset)
79 {
80 struct mchp_gpio_plat *plat = dev_get_plat(dev);
81 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
82 int val, input;
83
84 if (offset > uc_priv->gpio_count)
85 return -EINVAL;
86
87 input = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN;
88
89 if (input)
90 val = (readl(plat->base + plat->regs->inp) & BIT(offset));
91 else
92 val = (readl(plat->base + plat->regs->outp) & BIT(offset));
93
94 return val >> offset;
95 }
96
mchp_gpio_set_value(struct udevice * dev,u32 offset,int value)97 static int mchp_gpio_set_value(struct udevice *dev, u32 offset, int value)
98 {
99 struct mchp_gpio_plat *plat = dev_get_plat(dev);
100 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
101
102 if (offset > uc_priv->gpio_count)
103 return -EINVAL;
104
105 mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value);
106
107 return 0;
108 }
109
mchp_gpio_get_function(struct udevice * dev,unsigned int offset)110 static int mchp_gpio_get_function(struct udevice *dev, unsigned int offset)
111 {
112 struct mchp_gpio_plat *plat = dev_get_plat(dev);
113 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
114 u32 outdir, indir, val;
115
116 if (offset > uc_priv->gpio_count)
117 return -EINVAL;
118
119 /* Get direction of the pin */
120 outdir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_OUT;
121 indir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN;
122
123 if (outdir)
124 val = GPIOF_OUTPUT;
125 else if (indir)
126 val = GPIOF_INPUT;
127 else
128 val = GPIOF_UNUSED;
129
130 return val;
131 }
132
mchp_gpio_probe(struct udevice * dev)133 static int mchp_gpio_probe(struct udevice *dev)
134 {
135 struct mchp_gpio_plat *plat = dev_get_plat(dev);
136 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
137 char name[18], *str;
138
139 plat->regs = dev_get_driver_data(dev);
140 sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base);
141 str = strdup(name);
142 if (!str)
143 return -ENOMEM;
144 uc_priv->bank_name = str;
145 uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", MPFS_MAX_NUM_GPIO);
146
147 return 0;
148 }
149
150 static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = {
151 .inp = MPFS_INP_REG,
152 .outp = MPFS_OUTP_REG,
153 };
154
155 static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = {
156 .inp = COREGPIO_INP_REG,
157 .outp = COREGPIO_OUTP_REG,
158 };
159
160 static const struct udevice_id mchp_gpio_match[] = {
161 {
162 .compatible = "microchip,mpfs-gpio",
163 .data = &mpfs_reg_offsets,
164 }, {
165 .compatible = "microchip,coregpio-rtl-v3",
166 .data = &coregpio_reg_offsets,
167 },
168 { /* end of list */ }
169 };
170
171 static const struct dm_gpio_ops mchp_gpio_ops = {
172 .direction_input = mchp_gpio_direction_input,
173 .direction_output = mchp_gpio_direction_output,
174 .get_value = mchp_gpio_get_value,
175 .set_value = mchp_gpio_set_value,
176 .get_function = mchp_gpio_get_function,
177 };
178
mchp_gpio_of_to_plat(struct udevice * dev)179 static int mchp_gpio_of_to_plat(struct udevice *dev)
180 {
181 struct mchp_gpio_plat *plat = dev_get_plat(dev);
182
183 plat->base = dev_read_addr_ptr(dev);
184 if (!plat->base)
185 return -EINVAL;
186
187 return 0;
188 }
189
190 U_BOOT_DRIVER(gpio_mpfs) = {
191 .name = "gpio_mpfs",
192 .id = UCLASS_GPIO,
193 .of_match = mchp_gpio_match,
194 .of_to_plat = of_match_ptr(mchp_gpio_of_to_plat),
195 .plat_auto = sizeof(struct mchp_gpio_plat),
196 .ops = &mchp_gpio_ops,
197 .probe = mchp_gpio_probe,
198 };
199