Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpio/gpio-creg-snps.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0+
2
//
3
// Synopsys CREG (Control REGisters) GPIO driver
4
//
5
// Copyright (C) 2018 Synopsys
6
// Author: Eugeniy Paltsev <[email protected]>
7
8
#include <linux/gpio/driver.h>
9
#include <linux/io.h>
10
#include <linux/of.h>
11
#include <linux/platform_device.h>
12
13
#define MAX_GPIO 32
14
15
struct creg_layout {
16
u8 ngpio;
17
u8 shift[MAX_GPIO];
18
u8 on[MAX_GPIO];
19
u8 off[MAX_GPIO];
20
u8 bit_per_gpio[MAX_GPIO];
21
};
22
23
struct creg_gpio {
24
struct gpio_chip gc;
25
void __iomem *regs;
26
spinlock_t lock;
27
const struct creg_layout *layout;
28
};
29
30
static int creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
31
{
32
struct creg_gpio *hcg = gpiochip_get_data(gc);
33
const struct creg_layout *layout = hcg->layout;
34
u32 reg, reg_shift, value;
35
unsigned long flags;
36
int i;
37
38
value = val ? hcg->layout->on[offset] : hcg->layout->off[offset];
39
40
reg_shift = layout->shift[offset];
41
for (i = 0; i < offset; i++)
42
reg_shift += layout->bit_per_gpio[i] + layout->shift[i];
43
44
spin_lock_irqsave(&hcg->lock, flags);
45
reg = readl(hcg->regs);
46
reg &= ~(GENMASK(layout->bit_per_gpio[i] - 1, 0) << reg_shift);
47
reg |= (value << reg_shift);
48
writel(reg, hcg->regs);
49
spin_unlock_irqrestore(&hcg->lock, flags);
50
51
return 0;
52
}
53
54
static int creg_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
55
{
56
return creg_gpio_set(gc, offset, val);
57
}
58
59
static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg,
60
int i)
61
{
62
const struct creg_layout *layout = hcg->layout;
63
64
if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8)
65
return -EINVAL;
66
67
/* Check that on value fits its placeholder */
68
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i])
69
return -EINVAL;
70
71
/* Check that off value fits its placeholder */
72
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i])
73
return -EINVAL;
74
75
if (layout->on[i] == layout->off[i])
76
return -EINVAL;
77
78
return 0;
79
}
80
81
static int creg_gpio_validate(struct device *dev, struct creg_gpio *hcg,
82
u32 ngpios)
83
{
84
u32 reg_len = 0;
85
int i;
86
87
if (hcg->layout->ngpio < 1 || hcg->layout->ngpio > MAX_GPIO)
88
return -EINVAL;
89
90
if (ngpios < 1 || ngpios > hcg->layout->ngpio) {
91
dev_err(dev, "ngpios must be in [1:%u]\n", hcg->layout->ngpio);
92
return -EINVAL;
93
}
94
95
for (i = 0; i < hcg->layout->ngpio; i++) {
96
if (creg_gpio_validate_pg(dev, hcg, i))
97
return -EINVAL;
98
99
reg_len += hcg->layout->shift[i] + hcg->layout->bit_per_gpio[i];
100
}
101
102
/* Check that we fit in 32 bit register */
103
if (reg_len > 32)
104
return -EINVAL;
105
106
return 0;
107
}
108
109
static const struct creg_layout hsdk_cs_ctl = {
110
.ngpio = 10,
111
.shift = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
112
.off = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
113
.on = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
114
.bit_per_gpio = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }
115
};
116
117
static const struct creg_layout axs10x_flsh_cs_ctl = {
118
.ngpio = 1,
119
.shift = { 0 },
120
.off = { 1 },
121
.on = { 3 },
122
.bit_per_gpio = { 2 }
123
};
124
125
static const struct of_device_id creg_gpio_ids[] = {
126
{
127
.compatible = "snps,creg-gpio-axs10x",
128
.data = &axs10x_flsh_cs_ctl
129
}, {
130
.compatible = "snps,creg-gpio-hsdk",
131
.data = &hsdk_cs_ctl
132
}, { /* sentinel */ }
133
};
134
135
static int creg_gpio_probe(struct platform_device *pdev)
136
{
137
const struct of_device_id *match;
138
struct device *dev = &pdev->dev;
139
struct creg_gpio *hcg;
140
u32 ngpios;
141
int ret;
142
143
hcg = devm_kzalloc(dev, sizeof(struct creg_gpio), GFP_KERNEL);
144
if (!hcg)
145
return -ENOMEM;
146
147
hcg->regs = devm_platform_ioremap_resource(pdev, 0);
148
if (IS_ERR(hcg->regs))
149
return PTR_ERR(hcg->regs);
150
151
match = of_match_node(creg_gpio_ids, pdev->dev.of_node);
152
hcg->layout = match->data;
153
if (!hcg->layout)
154
return -EINVAL;
155
156
ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios);
157
if (ret)
158
return ret;
159
160
ret = creg_gpio_validate(dev, hcg, ngpios);
161
if (ret)
162
return ret;
163
164
spin_lock_init(&hcg->lock);
165
166
hcg->gc.parent = dev;
167
hcg->gc.label = dev_name(dev);
168
hcg->gc.base = -1;
169
hcg->gc.ngpio = ngpios;
170
hcg->gc.set = creg_gpio_set;
171
hcg->gc.direction_output = creg_gpio_dir_out;
172
173
ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg);
174
if (ret)
175
return ret;
176
177
dev_info(dev, "GPIO controller with %d gpios probed\n", ngpios);
178
179
return 0;
180
}
181
182
static struct platform_driver creg_gpio_snps_driver = {
183
.driver = {
184
.name = "snps-creg-gpio",
185
.of_match_table = creg_gpio_ids,
186
},
187
.probe = creg_gpio_probe,
188
};
189
builtin_platform_driver(creg_gpio_snps_driver);
190
191