summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/mt7620_gpio.c
blob: 713fa2ed78b7ea7f8912c9f5261c3cc393c2624a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 *
 * Author: Weijie Gao <weijie.gao@mediatek.com>
 *
 * GPIO controller driver for MediaTek MT7620 SoC
 */

#include <dm.h>
#include <errno.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <asm/gpio.h>

enum mt7620_regs {
	GPIO_REG_DATA,
	GPIO_REG_DIR,
	GPIO_REG_SET,
	GPIO_REG_CLR,

	__GPIO_REG_MAX
};

struct mt7620_gpio_priv {
	void __iomem *base;
	u32 regs[__GPIO_REG_MAX];
	u32 count;
};

static int mt7620_gpio_get_value(struct udevice *dev, unsigned int offset)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);

	return !!(readl(priv->base + priv->regs[GPIO_REG_DATA]) & BIT(offset));
}

static int mt7620_gpio_set_value(struct udevice *dev, unsigned int offset,
				 int value)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
	u32 reg;

	reg = value ? priv->regs[GPIO_REG_SET] : priv->regs[GPIO_REG_CLR];

	writel(BIT(offset), priv->base + reg);

	return 0;
}

static int mt7620_gpio_direction_input(struct udevice *dev, unsigned int offset)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);

	clrbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset));

	return 0;
}

static int mt7620_gpio_direction_output(struct udevice *dev,
					unsigned int offset, int value)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);

	/* Set value first */
	mt7620_gpio_set_value(dev, offset, value);

	setbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset));

	return 0;
}

static int mt7620_gpio_get_function(struct udevice *dev, unsigned int offset)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);

	return (readl(priv->base + priv->regs[GPIO_REG_DIR]) & BIT(offset)) ?
	       GPIOF_OUTPUT : GPIOF_INPUT;
}

static const struct dm_gpio_ops mt7620_gpio_ops = {
	.direction_input	= mt7620_gpio_direction_input,
	.direction_output	= mt7620_gpio_direction_output,
	.get_value		= mt7620_gpio_get_value,
	.set_value		= mt7620_gpio_set_value,
	.get_function		= mt7620_gpio_get_function,
};

static int mt7620_gpio_probe(struct udevice *dev)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
	const char *name;

	name = dev_read_string(dev, "mediatek,bank-name");
	if (!name)
		name = dev->name;

	uc_priv->gpio_count = priv->count;
	uc_priv->bank_name = name;

	return 0;
}

static int mt7620_gpio_of_to_plat(struct udevice *dev)
{
	struct mt7620_gpio_priv *priv = dev_get_priv(dev);
	int ret;

	priv->base = dev_remap_addr_index(dev, 0);
	if (!priv->base) {
		dev_err(dev, "mt7620_gpio: unable to map registers\n");
		return -EINVAL;
	}

	ret = dev_read_u32(dev, "mediatek,gpio-num", &priv->count);
	if (ret) {
		dev_err(dev, "mt7620_gpio: failed to get GPIO count\n");
		return -EINVAL;
	}

	ret = dev_read_u32_array(dev, "mediatek,register-map", priv->regs,
				 __GPIO_REG_MAX);
	if (ret) {
		dev_err(dev, "mt7620_gpio: unable to get register map\n");
		return -EINVAL;
	}

	return 0;
}

static const struct udevice_id mt7620_gpio_ids[] = {
	{ .compatible = "mediatek,mt7620-gpio" },
	{ }
};

U_BOOT_DRIVER(mt7620_gpio) = {
	.name	= "mt7620_gpio",
	.id	= UCLASS_GPIO,
	.ops	= &mt7620_gpio_ops,
	.of_match = mt7620_gpio_ids,
	.probe	= mt7620_gpio_probe,
	.of_to_plat = mt7620_gpio_of_to_plat,
	.priv_auto = sizeof(struct mt7620_gpio_priv),
};