1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
4  *
5  * Author: Weijie Gao <weijie.gao@mediatek.com>
6  *
7  * GPIO controller driver for MediaTek MT7620 SoC
8  */
9 
10 #include <dm.h>
11 #include <errno.h>
12 #include <dm/device_compat.h>
13 #include <linux/bitops.h>
14 #include <linux/io.h>
15 #include <asm/gpio.h>
16 
17 enum mt7620_regs {
18 	GPIO_REG_DATA,
19 	GPIO_REG_DIR,
20 	GPIO_REG_SET,
21 	GPIO_REG_CLR,
22 
23 	__GPIO_REG_MAX
24 };
25 
26 struct mt7620_gpio_priv {
27 	void __iomem *base;
28 	u32 regs[__GPIO_REG_MAX];
29 	u32 count;
30 };
31 
mt7620_gpio_get_value(struct udevice * dev,unsigned int offset)32 static int mt7620_gpio_get_value(struct udevice *dev, unsigned int offset)
33 {
34 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
35 
36 	return !!(readl(priv->base + priv->regs[GPIO_REG_DATA]) & BIT(offset));
37 }
38 
mt7620_gpio_set_value(struct udevice * dev,unsigned int offset,int value)39 static int mt7620_gpio_set_value(struct udevice *dev, unsigned int offset,
40 				 int value)
41 {
42 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
43 	u32 reg;
44 
45 	reg = value ? priv->regs[GPIO_REG_SET] : priv->regs[GPIO_REG_CLR];
46 
47 	writel(BIT(offset), priv->base + reg);
48 
49 	return 0;
50 }
51 
mt7620_gpio_direction_input(struct udevice * dev,unsigned int offset)52 static int mt7620_gpio_direction_input(struct udevice *dev, unsigned int offset)
53 {
54 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
55 
56 	clrbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset));
57 
58 	return 0;
59 }
60 
mt7620_gpio_direction_output(struct udevice * dev,unsigned int offset,int value)61 static int mt7620_gpio_direction_output(struct udevice *dev,
62 					unsigned int offset, int value)
63 {
64 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
65 
66 	/* Set value first */
67 	mt7620_gpio_set_value(dev, offset, value);
68 
69 	setbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset));
70 
71 	return 0;
72 }
73 
mt7620_gpio_get_function(struct udevice * dev,unsigned int offset)74 static int mt7620_gpio_get_function(struct udevice *dev, unsigned int offset)
75 {
76 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
77 
78 	return (readl(priv->base + priv->regs[GPIO_REG_DIR]) & BIT(offset)) ?
79 	       GPIOF_OUTPUT : GPIOF_INPUT;
80 }
81 
82 static const struct dm_gpio_ops mt7620_gpio_ops = {
83 	.direction_input	= mt7620_gpio_direction_input,
84 	.direction_output	= mt7620_gpio_direction_output,
85 	.get_value		= mt7620_gpio_get_value,
86 	.set_value		= mt7620_gpio_set_value,
87 	.get_function		= mt7620_gpio_get_function,
88 };
89 
mt7620_gpio_probe(struct udevice * dev)90 static int mt7620_gpio_probe(struct udevice *dev)
91 {
92 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
93 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
94 	const char *name;
95 
96 	name = dev_read_string(dev, "mediatek,bank-name");
97 	if (!name)
98 		name = dev->name;
99 
100 	uc_priv->gpio_count = priv->count;
101 	uc_priv->bank_name = name;
102 
103 	return 0;
104 }
105 
mt7620_gpio_of_to_plat(struct udevice * dev)106 static int mt7620_gpio_of_to_plat(struct udevice *dev)
107 {
108 	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
109 	int ret;
110 
111 	priv->base = dev_remap_addr_index(dev, 0);
112 	if (!priv->base) {
113 		dev_err(dev, "mt7620_gpio: unable to map registers\n");
114 		return -EINVAL;
115 	}
116 
117 	ret = dev_read_u32(dev, "mediatek,gpio-num", &priv->count);
118 	if (ret) {
119 		dev_err(dev, "mt7620_gpio: failed to get GPIO count\n");
120 		return -EINVAL;
121 	}
122 
123 	ret = dev_read_u32_array(dev, "mediatek,register-map", priv->regs,
124 				 __GPIO_REG_MAX);
125 	if (ret) {
126 		dev_err(dev, "mt7620_gpio: unable to get register map\n");
127 		return -EINVAL;
128 	}
129 
130 	return 0;
131 }
132 
133 static const struct udevice_id mt7620_gpio_ids[] = {
134 	{ .compatible = "mediatek,mt7620-gpio" },
135 	{ }
136 };
137 
138 U_BOOT_DRIVER(mt7620_gpio) = {
139 	.name	= "mt7620_gpio",
140 	.id	= UCLASS_GPIO,
141 	.ops	= &mt7620_gpio_ops,
142 	.of_match = mt7620_gpio_ids,
143 	.probe	= mt7620_gpio_probe,
144 	.of_to_plat = mt7620_gpio_of_to_plat,
145 	.priv_auto = sizeof(struct mt7620_gpio_priv),
146 };
147