Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpio/gpio-arizona.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* gpiolib support for Wolfson Arizona class devices
4
*
5
* Copyright 2012 Wolfson Microelectronics PLC.
6
*
7
* Author: Mark Brown <[email protected]>
8
*/
9
10
#include <linux/gpio/driver.h>
11
#include <linux/kernel.h>
12
#include <linux/module.h>
13
#include <linux/platform_device.h>
14
#include <linux/pm_runtime.h>
15
#include <linux/slab.h>
16
17
#include <linux/mfd/arizona/core.h>
18
#include <linux/mfd/arizona/pdata.h>
19
#include <linux/mfd/arizona/registers.h>
20
21
struct arizona_gpio {
22
struct arizona *arizona;
23
struct gpio_chip gpio_chip;
24
};
25
26
static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
27
{
28
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
29
struct arizona *arizona = arizona_gpio->arizona;
30
bool persistent = gpiochip_line_is_persistent(chip, offset);
31
bool change;
32
int ret;
33
34
ret = regmap_update_bits_check(arizona->regmap,
35
ARIZONA_GPIO1_CTRL + offset,
36
ARIZONA_GPN_DIR, ARIZONA_GPN_DIR,
37
&change);
38
if (ret < 0)
39
return ret;
40
41
if (change && persistent) {
42
pm_runtime_put_autosuspend(chip->parent);
43
}
44
45
return 0;
46
}
47
48
static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
49
{
50
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
51
struct arizona *arizona = arizona_gpio->arizona;
52
unsigned int reg, val;
53
int ret;
54
55
reg = ARIZONA_GPIO1_CTRL + offset;
56
ret = regmap_read(arizona->regmap, reg, &val);
57
if (ret < 0)
58
return ret;
59
60
/* Resume to read actual registers for input pins */
61
if (val & ARIZONA_GPN_DIR) {
62
ret = pm_runtime_get_sync(chip->parent);
63
if (ret < 0) {
64
dev_err(chip->parent, "Failed to resume: %d\n", ret);
65
pm_runtime_put_autosuspend(chip->parent);
66
return ret;
67
}
68
69
/* Register is cached, drop it to ensure a physical read */
70
ret = regcache_drop_region(arizona->regmap, reg, reg);
71
if (ret < 0) {
72
dev_err(chip->parent, "Failed to drop cache: %d\n",
73
ret);
74
pm_runtime_put_autosuspend(chip->parent);
75
return ret;
76
}
77
78
ret = regmap_read(arizona->regmap, reg, &val);
79
if (ret < 0) {
80
pm_runtime_put_autosuspend(chip->parent);
81
return ret;
82
}
83
84
pm_runtime_put_autosuspend(chip->parent);
85
}
86
87
if (val & ARIZONA_GPN_LVL)
88
return 1;
89
else
90
return 0;
91
}
92
93
static int arizona_gpio_direction_out(struct gpio_chip *chip,
94
unsigned offset, int value)
95
{
96
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
97
struct arizona *arizona = arizona_gpio->arizona;
98
bool persistent = gpiochip_line_is_persistent(chip, offset);
99
unsigned int val;
100
int ret;
101
102
ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val);
103
if (ret < 0)
104
return ret;
105
106
if ((val & ARIZONA_GPN_DIR) && persistent) {
107
ret = pm_runtime_get_sync(chip->parent);
108
if (ret < 0) {
109
dev_err(chip->parent, "Failed to resume: %d\n", ret);
110
pm_runtime_put(chip->parent);
111
return ret;
112
}
113
}
114
115
if (value)
116
value = ARIZONA_GPN_LVL;
117
118
return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
119
ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
120
}
121
122
static int arizona_gpio_set(struct gpio_chip *chip, unsigned int offset,
123
int value)
124
{
125
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
126
struct arizona *arizona = arizona_gpio->arizona;
127
128
if (value)
129
value = ARIZONA_GPN_LVL;
130
131
return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
132
ARIZONA_GPN_LVL, value);
133
}
134
135
static const struct gpio_chip template_chip = {
136
.label = "arizona",
137
.owner = THIS_MODULE,
138
.direction_input = arizona_gpio_direction_in,
139
.get = arizona_gpio_get,
140
.direction_output = arizona_gpio_direction_out,
141
.set = arizona_gpio_set,
142
.can_sleep = true,
143
};
144
145
static int arizona_gpio_probe(struct platform_device *pdev)
146
{
147
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
148
struct arizona_pdata *pdata = &arizona->pdata;
149
struct arizona_gpio *arizona_gpio;
150
int ret;
151
152
device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
153
154
arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio),
155
GFP_KERNEL);
156
if (!arizona_gpio)
157
return -ENOMEM;
158
159
arizona_gpio->arizona = arizona;
160
arizona_gpio->gpio_chip = template_chip;
161
arizona_gpio->gpio_chip.parent = &pdev->dev;
162
163
switch (arizona->type) {
164
case WM5102:
165
case WM5110:
166
case WM8280:
167
case WM8997:
168
case WM8998:
169
case WM1814:
170
arizona_gpio->gpio_chip.ngpio = 5;
171
break;
172
case WM1831:
173
case CS47L24:
174
arizona_gpio->gpio_chip.ngpio = 2;
175
break;
176
default:
177
dev_err(&pdev->dev, "Unknown chip variant %d\n",
178
arizona->type);
179
return -EINVAL;
180
}
181
182
if (pdata->gpio_base)
183
arizona_gpio->gpio_chip.base = pdata->gpio_base;
184
else
185
arizona_gpio->gpio_chip.base = -1;
186
187
pm_runtime_enable(&pdev->dev);
188
189
ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
190
arizona_gpio);
191
if (ret < 0) {
192
pm_runtime_disable(&pdev->dev);
193
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
194
ret);
195
return ret;
196
}
197
198
return 0;
199
}
200
201
static struct platform_driver arizona_gpio_driver = {
202
.driver.name = "arizona-gpio",
203
.probe = arizona_gpio_probe,
204
};
205
206
module_platform_driver(arizona_gpio_driver);
207
208
MODULE_AUTHOR("Mark Brown <[email protected]>");
209
MODULE_DESCRIPTION("GPIO interface for Arizona devices");
210
MODULE_LICENSE("GPL");
211
MODULE_ALIAS("platform:arizona-gpio");
212
213