Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpio/adp5520-gpio.c
15111 views
1
/*
2
* GPIO driver for Analog Devices ADP5520 MFD PMICs
3
*
4
* Copyright 2009 Analog Devices Inc.
5
*
6
* Licensed under the GPL-2 or later.
7
*/
8
9
#include <linux/module.h>
10
#include <linux/slab.h>
11
#include <linux/kernel.h>
12
#include <linux/init.h>
13
#include <linux/platform_device.h>
14
#include <linux/mfd/adp5520.h>
15
16
#include <linux/gpio.h>
17
18
struct adp5520_gpio {
19
struct device *master;
20
struct gpio_chip gpio_chip;
21
unsigned char lut[ADP5520_MAXGPIOS];
22
unsigned long output;
23
};
24
25
static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
26
{
27
struct adp5520_gpio *dev;
28
uint8_t reg_val;
29
30
dev = container_of(chip, struct adp5520_gpio, gpio_chip);
31
32
/*
33
* There are dedicated registers for GPIO IN/OUT.
34
* Make sure we return the right value, even when configured as output
35
*/
36
37
if (test_bit(off, &dev->output))
38
adp5520_read(dev->master, ADP5520_GPIO_OUT, &reg_val);
39
else
40
adp5520_read(dev->master, ADP5520_GPIO_IN, &reg_val);
41
42
return !!(reg_val & dev->lut[off]);
43
}
44
45
static void adp5520_gpio_set_value(struct gpio_chip *chip,
46
unsigned off, int val)
47
{
48
struct adp5520_gpio *dev;
49
dev = container_of(chip, struct adp5520_gpio, gpio_chip);
50
51
if (val)
52
adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
53
else
54
adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
55
}
56
57
static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
58
{
59
struct adp5520_gpio *dev;
60
dev = container_of(chip, struct adp5520_gpio, gpio_chip);
61
62
clear_bit(off, &dev->output);
63
64
return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
65
dev->lut[off]);
66
}
67
68
static int adp5520_gpio_direction_output(struct gpio_chip *chip,
69
unsigned off, int val)
70
{
71
struct adp5520_gpio *dev;
72
int ret = 0;
73
dev = container_of(chip, struct adp5520_gpio, gpio_chip);
74
75
set_bit(off, &dev->output);
76
77
if (val)
78
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
79
dev->lut[off]);
80
else
81
ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
82
dev->lut[off]);
83
84
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
85
dev->lut[off]);
86
87
return ret;
88
}
89
90
static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
91
{
92
struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data;
93
struct adp5520_gpio *dev;
94
struct gpio_chip *gc;
95
int ret, i, gpios;
96
unsigned char ctl_mask = 0;
97
98
if (pdata == NULL) {
99
dev_err(&pdev->dev, "missing platform data\n");
100
return -ENODEV;
101
}
102
103
if (pdev->id != ID_ADP5520) {
104
dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
105
return -ENODEV;
106
}
107
108
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
109
if (dev == NULL) {
110
dev_err(&pdev->dev, "failed to alloc memory\n");
111
return -ENOMEM;
112
}
113
114
dev->master = pdev->dev.parent;
115
116
for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
117
if (pdata->gpio_en_mask & (1 << i))
118
dev->lut[gpios++] = 1 << i;
119
120
if (gpios < 1) {
121
ret = -EINVAL;
122
goto err;
123
}
124
125
gc = &dev->gpio_chip;
126
gc->direction_input = adp5520_gpio_direction_input;
127
gc->direction_output = adp5520_gpio_direction_output;
128
gc->get = adp5520_gpio_get_value;
129
gc->set = adp5520_gpio_set_value;
130
gc->can_sleep = 1;
131
132
gc->base = pdata->gpio_start;
133
gc->ngpio = gpios;
134
gc->label = pdev->name;
135
gc->owner = THIS_MODULE;
136
137
ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
138
pdata->gpio_en_mask);
139
140
if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
141
ctl_mask |= ADP5520_C3_MODE;
142
143
if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
144
ctl_mask |= ADP5520_R3_MODE;
145
146
if (ctl_mask)
147
ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
148
ctl_mask);
149
150
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
151
pdata->gpio_pullup_mask);
152
153
if (ret) {
154
dev_err(&pdev->dev, "failed to write\n");
155
goto err;
156
}
157
158
ret = gpiochip_add(&dev->gpio_chip);
159
if (ret)
160
goto err;
161
162
platform_set_drvdata(pdev, dev);
163
return 0;
164
165
err:
166
kfree(dev);
167
return ret;
168
}
169
170
static int __devexit adp5520_gpio_remove(struct platform_device *pdev)
171
{
172
struct adp5520_gpio *dev;
173
int ret;
174
175
dev = platform_get_drvdata(pdev);
176
ret = gpiochip_remove(&dev->gpio_chip);
177
if (ret) {
178
dev_err(&pdev->dev, "%s failed, %d\n",
179
"gpiochip_remove()", ret);
180
return ret;
181
}
182
183
kfree(dev);
184
return 0;
185
}
186
187
static struct platform_driver adp5520_gpio_driver = {
188
.driver = {
189
.name = "adp5520-gpio",
190
.owner = THIS_MODULE,
191
},
192
.probe = adp5520_gpio_probe,
193
.remove = __devexit_p(adp5520_gpio_remove),
194
};
195
196
static int __init adp5520_gpio_init(void)
197
{
198
return platform_driver_register(&adp5520_gpio_driver);
199
}
200
module_init(adp5520_gpio_init);
201
202
static void __exit adp5520_gpio_exit(void)
203
{
204
platform_driver_unregister(&adp5520_gpio_driver);
205
}
206
module_exit(adp5520_gpio_exit);
207
208
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
209
MODULE_DESCRIPTION("GPIO ADP5520 Driver");
210
MODULE_LICENSE("GPL");
211
MODULE_ALIAS("platform:adp5520-gpio");
212
213