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