summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-starfive-vic.c
blob: baa4f584d5e89a4ef1419de74a60ef203a22cfc3 (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// SPDX-License-Identifier: GPL-2.0
/*
 * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
 */

#include <linux/basic_mmio_gpio.h>
#include <linux/reset.h>
#include <linux/clk.h>
#include <printk.h>
#include <driver.h>
#include <errno.h>
#include <pinctrl.h>

#define GPIO_EN		0x0
#define GPIO_IS_LOW	0x10
#define GPIO_IS_HIGH	0x14
#define GPIO_IBE_LOW	0x18
#define GPIO_IBE_HIGH	0x1c
#define GPIO_IEV_LOW	0x20
#define GPIO_IEV_HIGH	0x24
#define GPIO_IE_LOW	0x28
#define GPIO_IE_HIGH	0x2c
#define GPIO_IC_LOW	0x30
#define GPIO_IC_HIGH	0x34
//read only
#define GPIO_RIS_LOW	0x38
#define GPIO_RIS_HIGH	0x3c
#define GPIO_MIS_LOW	0x40
#define GPIO_MIS_HIGH	0x44
#define GPIO_DIN_LOW	0x48
#define GPIO_DIN_HIGH	0x4c

#define GPIO_DOUT_X_REG	0x50
#define GPIO_DOEN_X_REG	0x54

#define MAX_GPIO	 64

struct starfive_gpio {
	void __iomem		*base;
	struct gpio_chip	gc;
};

#define to_starfive_gpio(gc) container_of(gc, struct starfive_gpio, gc)

static int starfive_direction_input(struct gpio_chip *gc, unsigned offset)
{
	struct starfive_gpio *chip = to_starfive_gpio(gc);

	if (offset >= gc->ngpio)
		return -EINVAL;

	writel(0x1, chip->base + GPIO_DOEN_X_REG + offset * 8);

	return 0;
}

static int starfive_direction_output(struct gpio_chip *gc, unsigned offset, int value)
{
	struct starfive_gpio *chip = to_starfive_gpio(gc);

	if (offset >= gc->ngpio)
		return -EINVAL;
	writel(0x0, chip->base + GPIO_DOEN_X_REG + offset * 8);
	writel(value, chip->base + GPIO_DOUT_X_REG + offset * 8);

	return 0;
}

static int starfive_get_direction(struct gpio_chip *gc, unsigned offset)
{
	struct starfive_gpio *chip = to_starfive_gpio(gc);

	if (offset >= gc->ngpio)
		return -EINVAL;

	return readl(chip->base + GPIO_DOEN_X_REG + offset * 8) & 0x1;
}

static int starfive_get_value(struct gpio_chip *gc, unsigned offset)
{
	struct starfive_gpio *chip = to_starfive_gpio(gc);
	int value;

	if (offset >= gc->ngpio)
		return -EINVAL;

	if(offset < 32){
		value = readl(chip->base + GPIO_DIN_LOW);
		return (value >> offset) & 0x1;
	} else {
		value = readl(chip->base + GPIO_DIN_HIGH);
		return (value >> (offset - 32)) & 0x1;
	}
}

static void starfive_set_value(struct gpio_chip *gc, unsigned offset, int value)
{
	struct starfive_gpio *chip = to_starfive_gpio(gc);

	if (offset >= gc->ngpio)
		return;

	writel(value, chip->base + GPIO_DOUT_X_REG + offset * 8);
}

static struct gpio_ops starfive_gpio_ops = {
	.direction_input = starfive_direction_input,
	.direction_output = starfive_direction_output,
	.get_direction = starfive_get_direction,
	.get = starfive_get_value,
	.set = starfive_set_value,
};

static int starfive_gpio_probe(struct device_d *dev)
{
	struct starfive_gpio *chip;
	struct resource *res;
	struct clk *clk;
	int ret;

	clk = clk_get(dev, NULL);
	if (IS_ERR(clk))
		return PTR_ERR(clk);

	clk_enable(clk);

	ret = device_reset(dev);
	if (ret)
		return ret;

	ret = pinctrl_single_probe(dev);
	if (ret)
		return ret;

	res = dev_get_resource(dev, IORESOURCE_MEM, 0);
	if (IS_ERR(res))
		return PTR_ERR(res);

	chip = xzalloc(sizeof(*chip));
	chip->base = IOMEM(res->start);

	chip->gc.base = -1;
	chip->gc.ngpio = MAX_GPIO;
	chip->gc.dev = dev;
	chip->gc.ops = &starfive_gpio_ops;

	/* Disable all GPIO interrupts */
	iowrite32(0, chip->base + GPIO_IE_HIGH);
	iowrite32(0, chip->base + GPIO_IE_LOW);

	ret = gpiochip_add(&chip->gc);
	if (ret) {
		dev_err(dev, "could not add gpiochip\n");
		gpiochip_remove(&chip->gc);
		return ret;
	}

	writel(1, chip->base + GPIO_EN);

	return 0;
}

static const struct of_device_id starfive_gpio_match[] = {
	{ .compatible = "starfive,gpio0", },
	{ },
};

static struct driver_d starfive_gpio_driver = {
	.probe	= starfive_gpio_probe,
	.name		= "starfive_gpio",
	.of_compatible	= starfive_gpio_match,
};
postcore_platform_driver(starfive_gpio_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huan Feng <huan.feng@starfivetech.com>");
MODULE_DESCRIPTION("Starfive VIC GPIO generator driver");