1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * GPIO Driver for Dialog DA9055 PMICs. |
4 | * |
5 | * Copyright(c) 2012 Dialog Semiconductor Ltd. |
6 | * |
7 | * Author: David Dajun Chen <dchen@diasemi.com> |
8 | */ |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/gpio/driver.h> |
12 | |
13 | #include <linux/mfd/da9055/core.h> |
14 | #include <linux/mfd/da9055/reg.h> |
15 | #include <linux/mfd/da9055/pdata.h> |
16 | |
17 | #define DA9055_VDD_IO 0x0 |
18 | #define DA9055_PUSH_PULL 0x3 |
19 | #define DA9055_ACT_LOW 0x0 |
20 | #define DA9055_GPI 0x1 |
21 | #define DA9055_PORT_MASK 0x3 |
22 | #define DA9055_PORT_SHIFT(offset) (4 * (offset % 2)) |
23 | |
24 | #define DA9055_INPUT DA9055_GPI |
25 | #define DA9055_OUTPUT DA9055_PUSH_PULL |
26 | #define DA9055_IRQ_GPI0 3 |
27 | |
28 | struct da9055_gpio { |
29 | struct da9055 *da9055; |
30 | struct gpio_chip gp; |
31 | }; |
32 | |
33 | static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset) |
34 | { |
35 | struct da9055_gpio *gpio = gpiochip_get_data(gc); |
36 | int gpio_direction = 0; |
37 | int ret; |
38 | |
39 | /* Get GPIO direction */ |
40 | ret = da9055_reg_read(da9055: gpio->da9055, reg: (offset >> 1) + DA9055_REG_GPIO0_1); |
41 | if (ret < 0) |
42 | return ret; |
43 | |
44 | gpio_direction = ret & (DA9055_PORT_MASK) << DA9055_PORT_SHIFT(offset); |
45 | gpio_direction >>= DA9055_PORT_SHIFT(offset); |
46 | switch (gpio_direction) { |
47 | case DA9055_INPUT: |
48 | ret = da9055_reg_read(da9055: gpio->da9055, DA9055_REG_STATUS_B); |
49 | if (ret < 0) |
50 | return ret; |
51 | break; |
52 | case DA9055_OUTPUT: |
53 | ret = da9055_reg_read(da9055: gpio->da9055, DA9055_REG_GPIO_MODE0_2); |
54 | if (ret < 0) |
55 | return ret; |
56 | } |
57 | |
58 | return ret & (1 << offset); |
59 | |
60 | } |
61 | |
62 | static void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value) |
63 | { |
64 | struct da9055_gpio *gpio = gpiochip_get_data(gc); |
65 | |
66 | da9055_reg_update(da9055: gpio->da9055, |
67 | DA9055_REG_GPIO_MODE0_2, |
68 | bit_mask: 1 << offset, |
69 | reg_val: value << offset); |
70 | } |
71 | |
72 | static int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset) |
73 | { |
74 | struct da9055_gpio *gpio = gpiochip_get_data(gc); |
75 | unsigned char reg_byte; |
76 | |
77 | reg_byte = (DA9055_ACT_LOW | DA9055_GPI) |
78 | << DA9055_PORT_SHIFT(offset); |
79 | |
80 | return da9055_reg_update(da9055: gpio->da9055, reg: (offset >> 1) + |
81 | DA9055_REG_GPIO0_1, |
82 | DA9055_PORT_MASK << |
83 | DA9055_PORT_SHIFT(offset), |
84 | reg_val: reg_byte); |
85 | } |
86 | |
87 | static int da9055_gpio_direction_output(struct gpio_chip *gc, |
88 | unsigned offset, int value) |
89 | { |
90 | struct da9055_gpio *gpio = gpiochip_get_data(gc); |
91 | unsigned char reg_byte; |
92 | int ret; |
93 | |
94 | reg_byte = (DA9055_VDD_IO | DA9055_PUSH_PULL) |
95 | << DA9055_PORT_SHIFT(offset); |
96 | |
97 | ret = da9055_reg_update(da9055: gpio->da9055, reg: (offset >> 1) + |
98 | DA9055_REG_GPIO0_1, |
99 | DA9055_PORT_MASK << |
100 | DA9055_PORT_SHIFT(offset), |
101 | reg_val: reg_byte); |
102 | if (ret < 0) |
103 | return ret; |
104 | |
105 | da9055_gpio_set(gc, offset, value); |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset) |
111 | { |
112 | struct da9055_gpio *gpio = gpiochip_get_data(gc); |
113 | struct da9055 *da9055 = gpio->da9055; |
114 | |
115 | return regmap_irq_get_virq(data: da9055->irq_data, |
116 | DA9055_IRQ_GPI0 + offset); |
117 | } |
118 | |
119 | static const struct gpio_chip reference_gp = { |
120 | .label = "da9055-gpio" , |
121 | .owner = THIS_MODULE, |
122 | .get = da9055_gpio_get, |
123 | .set = da9055_gpio_set, |
124 | .direction_input = da9055_gpio_direction_input, |
125 | .direction_output = da9055_gpio_direction_output, |
126 | .to_irq = da9055_gpio_to_irq, |
127 | .can_sleep = true, |
128 | .ngpio = 3, |
129 | .base = -1, |
130 | }; |
131 | |
132 | static int da9055_gpio_probe(struct platform_device *pdev) |
133 | { |
134 | struct da9055_gpio *gpio; |
135 | struct da9055_pdata *pdata; |
136 | |
137 | gpio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*gpio), GFP_KERNEL); |
138 | if (!gpio) |
139 | return -ENOMEM; |
140 | |
141 | gpio->da9055 = dev_get_drvdata(dev: pdev->dev.parent); |
142 | pdata = dev_get_platdata(dev: gpio->da9055->dev); |
143 | |
144 | gpio->gp = reference_gp; |
145 | if (pdata && pdata->gpio_base) |
146 | gpio->gp.base = pdata->gpio_base; |
147 | |
148 | return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio); |
149 | } |
150 | |
151 | static struct platform_driver da9055_gpio_driver = { |
152 | .probe = da9055_gpio_probe, |
153 | .driver = { |
154 | .name = "da9055-gpio" , |
155 | }, |
156 | }; |
157 | |
158 | static int __init da9055_gpio_init(void) |
159 | { |
160 | return platform_driver_register(&da9055_gpio_driver); |
161 | } |
162 | subsys_initcall(da9055_gpio_init); |
163 | |
164 | static void __exit da9055_gpio_exit(void) |
165 | { |
166 | platform_driver_unregister(&da9055_gpio_driver); |
167 | } |
168 | module_exit(da9055_gpio_exit); |
169 | |
170 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>" ); |
171 | MODULE_DESCRIPTION("DA9055 GPIO Device Driver" ); |
172 | MODULE_LICENSE("GPL" ); |
173 | MODULE_ALIAS("platform:da9055-gpio" ); |
174 | |