1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * GPIO Driver for Dialog DA9052 PMICs.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright(c) 2011 Dialog Semiconductor Ltd.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Author: David Dajun Chen <dchen@diasemi.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/fs.h>
11*4882a593Smuzhiyun #include <linux/uaccess.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/gpio/driver.h>
14*4882a593Smuzhiyun #include <linux/syscalls.h>
15*4882a593Smuzhiyun #include <linux/seq_file.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <linux/mfd/da9052/da9052.h>
18*4882a593Smuzhiyun #include <linux/mfd/da9052/reg.h>
19*4882a593Smuzhiyun #include <linux/mfd/da9052/pdata.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #define DA9052_INPUT 1
22*4882a593Smuzhiyun #define DA9052_OUTPUT_OPENDRAIN 2
23*4882a593Smuzhiyun #define DA9052_OUTPUT_PUSHPULL 3
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define DA9052_SUPPLY_VDD_IO1 0
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define DA9052_DEBOUNCING_OFF 0
28*4882a593Smuzhiyun #define DA9052_DEBOUNCING_ON 1
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define DA9052_OUTPUT_LOWLEVEL 0
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #define DA9052_ACTIVE_LOW 0
33*4882a593Smuzhiyun #define DA9052_ACTIVE_HIGH 1
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8
36*4882a593Smuzhiyun #define DA9052_GPIO_SHIFT_COUNT(no) (no%8)
37*4882a593Smuzhiyun #define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0
38*4882a593Smuzhiyun #define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F
39*4882a593Smuzhiyun #define DA9052_GPIO_NIBBLE_SHIFT 4
40*4882a593Smuzhiyun #define DA9052_IRQ_GPI0 16
41*4882a593Smuzhiyun #define DA9052_GPIO_ODD_SHIFT 7
42*4882a593Smuzhiyun #define DA9052_GPIO_EVEN_SHIFT 3
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun struct da9052_gpio {
45*4882a593Smuzhiyun struct da9052 *da9052;
46*4882a593Smuzhiyun struct gpio_chip gp;
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun
da9052_gpio_port_odd(unsigned offset)49*4882a593Smuzhiyun static unsigned char da9052_gpio_port_odd(unsigned offset)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun return offset % 2;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun
da9052_gpio_get(struct gpio_chip * gc,unsigned offset)54*4882a593Smuzhiyun static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun struct da9052_gpio *gpio = gpiochip_get_data(gc);
57*4882a593Smuzhiyun int da9052_port_direction = 0;
58*4882a593Smuzhiyun int ret;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun ret = da9052_reg_read(gpio->da9052,
61*4882a593Smuzhiyun DA9052_GPIO_0_1_REG + (offset >> 1));
62*4882a593Smuzhiyun if (ret < 0)
63*4882a593Smuzhiyun return ret;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun if (da9052_gpio_port_odd(offset)) {
66*4882a593Smuzhiyun da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN;
67*4882a593Smuzhiyun da9052_port_direction >>= 4;
68*4882a593Smuzhiyun } else {
69*4882a593Smuzhiyun da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun switch (da9052_port_direction) {
73*4882a593Smuzhiyun case DA9052_INPUT:
74*4882a593Smuzhiyun if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER)
75*4882a593Smuzhiyun ret = da9052_reg_read(gpio->da9052,
76*4882a593Smuzhiyun DA9052_STATUS_C_REG);
77*4882a593Smuzhiyun else
78*4882a593Smuzhiyun ret = da9052_reg_read(gpio->da9052,
79*4882a593Smuzhiyun DA9052_STATUS_D_REG);
80*4882a593Smuzhiyun if (ret < 0)
81*4882a593Smuzhiyun return ret;
82*4882a593Smuzhiyun return !!(ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset)));
83*4882a593Smuzhiyun case DA9052_OUTPUT_PUSHPULL:
84*4882a593Smuzhiyun if (da9052_gpio_port_odd(offset))
85*4882a593Smuzhiyun return !!(ret & DA9052_GPIO_ODD_PORT_MODE);
86*4882a593Smuzhiyun else
87*4882a593Smuzhiyun return !!(ret & DA9052_GPIO_EVEN_PORT_MODE);
88*4882a593Smuzhiyun default:
89*4882a593Smuzhiyun return -EINVAL;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
da9052_gpio_set(struct gpio_chip * gc,unsigned offset,int value)93*4882a593Smuzhiyun static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun struct da9052_gpio *gpio = gpiochip_get_data(gc);
96*4882a593Smuzhiyun int ret;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (da9052_gpio_port_odd(offset)) {
99*4882a593Smuzhiyun ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
100*4882a593Smuzhiyun DA9052_GPIO_0_1_REG,
101*4882a593Smuzhiyun DA9052_GPIO_ODD_PORT_MODE,
102*4882a593Smuzhiyun value << DA9052_GPIO_ODD_SHIFT);
103*4882a593Smuzhiyun if (ret != 0)
104*4882a593Smuzhiyun dev_err(gpio->da9052->dev,
105*4882a593Smuzhiyun "Failed to updated gpio odd reg,%d",
106*4882a593Smuzhiyun ret);
107*4882a593Smuzhiyun } else {
108*4882a593Smuzhiyun ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
109*4882a593Smuzhiyun DA9052_GPIO_0_1_REG,
110*4882a593Smuzhiyun DA9052_GPIO_EVEN_PORT_MODE,
111*4882a593Smuzhiyun value << DA9052_GPIO_EVEN_SHIFT);
112*4882a593Smuzhiyun if (ret != 0)
113*4882a593Smuzhiyun dev_err(gpio->da9052->dev,
114*4882a593Smuzhiyun "Failed to updated gpio even reg,%d",
115*4882a593Smuzhiyun ret);
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
da9052_gpio_direction_input(struct gpio_chip * gc,unsigned offset)119*4882a593Smuzhiyun static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun struct da9052_gpio *gpio = gpiochip_get_data(gc);
122*4882a593Smuzhiyun unsigned char register_value;
123*4882a593Smuzhiyun int ret;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /* Format: function - 2 bits type - 1 bit mode - 1 bit */
126*4882a593Smuzhiyun register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 |
127*4882a593Smuzhiyun DA9052_DEBOUNCING_ON << 3;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (da9052_gpio_port_odd(offset))
130*4882a593Smuzhiyun ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
131*4882a593Smuzhiyun DA9052_GPIO_0_1_REG,
132*4882a593Smuzhiyun DA9052_GPIO_MASK_UPPER_NIBBLE,
133*4882a593Smuzhiyun (register_value <<
134*4882a593Smuzhiyun DA9052_GPIO_NIBBLE_SHIFT));
135*4882a593Smuzhiyun else
136*4882a593Smuzhiyun ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
137*4882a593Smuzhiyun DA9052_GPIO_0_1_REG,
138*4882a593Smuzhiyun DA9052_GPIO_MASK_LOWER_NIBBLE,
139*4882a593Smuzhiyun register_value);
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun return ret;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
da9052_gpio_direction_output(struct gpio_chip * gc,unsigned offset,int value)144*4882a593Smuzhiyun static int da9052_gpio_direction_output(struct gpio_chip *gc,
145*4882a593Smuzhiyun unsigned offset, int value)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct da9052_gpio *gpio = gpiochip_get_data(gc);
148*4882a593Smuzhiyun unsigned char register_value;
149*4882a593Smuzhiyun int ret;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */
152*4882a593Smuzhiyun register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 |
153*4882a593Smuzhiyun value << 3;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (da9052_gpio_port_odd(offset))
156*4882a593Smuzhiyun ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
157*4882a593Smuzhiyun DA9052_GPIO_0_1_REG,
158*4882a593Smuzhiyun DA9052_GPIO_MASK_UPPER_NIBBLE,
159*4882a593Smuzhiyun (register_value <<
160*4882a593Smuzhiyun DA9052_GPIO_NIBBLE_SHIFT));
161*4882a593Smuzhiyun else
162*4882a593Smuzhiyun ret = da9052_reg_update(gpio->da9052, (offset >> 1) +
163*4882a593Smuzhiyun DA9052_GPIO_0_1_REG,
164*4882a593Smuzhiyun DA9052_GPIO_MASK_LOWER_NIBBLE,
165*4882a593Smuzhiyun register_value);
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun return ret;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
da9052_gpio_to_irq(struct gpio_chip * gc,u32 offset)170*4882a593Smuzhiyun static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun struct da9052_gpio *gpio = gpiochip_get_data(gc);
173*4882a593Smuzhiyun struct da9052 *da9052 = gpio->da9052;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun int irq;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun irq = regmap_irq_get_virq(da9052->irq_data, DA9052_IRQ_GPI0 + offset);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun return irq;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun static const struct gpio_chip reference_gp = {
183*4882a593Smuzhiyun .label = "da9052-gpio",
184*4882a593Smuzhiyun .owner = THIS_MODULE,
185*4882a593Smuzhiyun .get = da9052_gpio_get,
186*4882a593Smuzhiyun .set = da9052_gpio_set,
187*4882a593Smuzhiyun .direction_input = da9052_gpio_direction_input,
188*4882a593Smuzhiyun .direction_output = da9052_gpio_direction_output,
189*4882a593Smuzhiyun .to_irq = da9052_gpio_to_irq,
190*4882a593Smuzhiyun .can_sleep = true,
191*4882a593Smuzhiyun .ngpio = 16,
192*4882a593Smuzhiyun .base = -1,
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun
da9052_gpio_probe(struct platform_device * pdev)195*4882a593Smuzhiyun static int da9052_gpio_probe(struct platform_device *pdev)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun struct da9052_gpio *gpio;
198*4882a593Smuzhiyun struct da9052_pdata *pdata;
199*4882a593Smuzhiyun int ret;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
202*4882a593Smuzhiyun if (!gpio)
203*4882a593Smuzhiyun return -ENOMEM;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun gpio->da9052 = dev_get_drvdata(pdev->dev.parent);
206*4882a593Smuzhiyun pdata = dev_get_platdata(gpio->da9052->dev);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun gpio->gp = reference_gp;
209*4882a593Smuzhiyun if (pdata && pdata->gpio_base)
210*4882a593Smuzhiyun gpio->gp.base = pdata->gpio_base;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
213*4882a593Smuzhiyun if (ret < 0) {
214*4882a593Smuzhiyun dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
215*4882a593Smuzhiyun return ret;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun platform_set_drvdata(pdev, gpio);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun return 0;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun static struct platform_driver da9052_gpio_driver = {
224*4882a593Smuzhiyun .probe = da9052_gpio_probe,
225*4882a593Smuzhiyun .driver = {
226*4882a593Smuzhiyun .name = "da9052-gpio",
227*4882a593Smuzhiyun },
228*4882a593Smuzhiyun };
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun module_platform_driver(da9052_gpio_driver);
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
233*4882a593Smuzhiyun MODULE_DESCRIPTION("DA9052 GPIO Device Driver");
234*4882a593Smuzhiyun MODULE_LICENSE("GPL");
235*4882a593Smuzhiyun MODULE_ALIAS("platform:da9052-gpio");
236