]> git.sur5r.net Git - u-boot/blob - drivers/gpio/pm8916_gpio.c
Merge branch 'master' of git://git.denx.de/u-boot-sunxi
[u-boot] / drivers / gpio / pm8916_gpio.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Qualcomm pm8916 pmic gpio driver - part of Qualcomm PM8916 PMIC
4  *
5  * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6  */
7
8 #include <common.h>
9 #include <dm.h>
10 #include <power/pmic.h>
11 #include <spmi/spmi.h>
12 #include <asm/io.h>
13 #include <asm/gpio.h>
14 #include <linux/bitops.h>
15
16 /* Register offset for each gpio */
17 #define REG_OFFSET(x)          ((x) * 0x100)
18
19 /* Register maps */
20
21 /* Type and subtype are shared for all pm8916 peripherals */
22 #define REG_TYPE               0x4
23 #define REG_SUBTYPE            0x5
24
25 #define REG_STATUS             0x08
26 #define REG_STATUS_VAL_MASK    0x1
27
28 /* MODE_CTL */
29 #define REG_CTL         0x40
30 #define REG_CTL_MODE_MASK       0x70
31 #define REG_CTL_MODE_INPUT      0x00
32 #define REG_CTL_MODE_INOUT      0x20
33 #define REG_CTL_MODE_OUTPUT     0x10
34 #define REG_CTL_OUTPUT_MASK     0x0F
35
36 #define REG_DIG_VIN_CTL        0x41
37 #define REG_DIG_VIN_VIN0       0
38
39 #define REG_DIG_PULL_CTL       0x42
40 #define REG_DIG_PULL_NO_PU     0x5
41
42 #define REG_DIG_OUT_CTL        0x45
43 #define REG_DIG_OUT_CTL_CMOS   (0x0 << 4)
44 #define REG_DIG_OUT_CTL_DRIVE_L 0x1
45
46 #define REG_EN_CTL             0x46
47 #define REG_EN_CTL_ENABLE      (1 << 7)
48
49 struct pm8916_gpio_bank {
50         uint32_t pid; /* Peripheral ID on SPMI bus */
51 };
52
53 static int pm8916_gpio_set_direction(struct udevice *dev, unsigned offset,
54                                      bool input, int value)
55 {
56         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
57         uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
58         int ret;
59
60         /* Disable the GPIO */
61         ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL,
62                               REG_EN_CTL_ENABLE, 0);
63         if (ret < 0)
64                 return ret;
65
66         /* Select the mode */
67         if (input)
68                 ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL,
69                                      REG_CTL_MODE_INPUT);
70         else
71                 ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL,
72                                      REG_CTL_MODE_INOUT | (value ? 1 : 0));
73         if (ret < 0)
74                 return ret;
75
76         /* Set the right pull (no pull) */
77         ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_PULL_CTL,
78                              REG_DIG_PULL_NO_PU);
79         if (ret < 0)
80                 return ret;
81
82         /* Configure output pin drivers if needed */
83         if (!input) {
84                 /* Select the VIN - VIN0, pin is input so it doesn't matter */
85                 ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_VIN_CTL,
86                                      REG_DIG_VIN_VIN0);
87                 if (ret < 0)
88                         return ret;
89
90                 /* Set the right dig out control */
91                 ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_OUT_CTL,
92                                      REG_DIG_OUT_CTL_CMOS |
93                                      REG_DIG_OUT_CTL_DRIVE_L);
94                 if (ret < 0)
95                         return ret;
96         }
97
98         /* Enable the GPIO */
99         return pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, 0,
100                                REG_EN_CTL_ENABLE);
101 }
102
103 static int pm8916_gpio_direction_input(struct udevice *dev, unsigned offset)
104 {
105         return pm8916_gpio_set_direction(dev, offset, true, 0);
106 }
107
108 static int pm8916_gpio_direction_output(struct udevice *dev, unsigned offset,
109                                         int value)
110 {
111         return pm8916_gpio_set_direction(dev, offset, false, value);
112 }
113
114 static int pm8916_gpio_get_function(struct udevice *dev, unsigned offset)
115 {
116         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
117         uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
118         int reg;
119
120         /* Set the output value of the gpio */
121         reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL);
122         if (reg < 0)
123                 return reg;
124
125         switch (reg & REG_CTL_MODE_MASK) {
126         case REG_CTL_MODE_INPUT:
127                 return GPIOF_INPUT;
128         case REG_CTL_MODE_INOUT: /* Fallthrough */
129         case REG_CTL_MODE_OUTPUT:
130                 return GPIOF_OUTPUT;
131         default:
132                 return GPIOF_UNKNOWN;
133         }
134 }
135
136 static int pm8916_gpio_get_value(struct udevice *dev, unsigned offset)
137 {
138         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
139         uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
140         int reg;
141
142         reg = pmic_reg_read(dev->parent, gpio_base + REG_STATUS);
143         if (reg < 0)
144                 return reg;
145
146         return !!(reg & REG_STATUS_VAL_MASK);
147 }
148
149 static int pm8916_gpio_set_value(struct udevice *dev, unsigned offset,
150                                  int value)
151 {
152         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
153         uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
154
155         /* Set the output value of the gpio */
156         return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL,
157                                REG_CTL_OUTPUT_MASK, !!value);
158 }
159
160 static const struct dm_gpio_ops pm8916_gpio_ops = {
161         .direction_input        = pm8916_gpio_direction_input,
162         .direction_output       = pm8916_gpio_direction_output,
163         .get_value              = pm8916_gpio_get_value,
164         .set_value              = pm8916_gpio_set_value,
165         .get_function           = pm8916_gpio_get_function,
166 };
167
168 static int pm8916_gpio_probe(struct udevice *dev)
169 {
170         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
171         int reg;
172
173         priv->pid = dev_read_addr(dev);
174         if (priv->pid == FDT_ADDR_T_NONE)
175                 return -EINVAL;
176
177         /* Do a sanity check */
178         reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
179         if (reg != 0x10)
180                 return -ENODEV;
181
182         reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
183         if (reg != 0x5 && reg != 0x1)
184                 return -ENODEV;
185
186         return 0;
187 }
188
189 static int pm8916_gpio_ofdata_to_platdata(struct udevice *dev)
190 {
191         struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
192
193         uc_priv->gpio_count = dev_read_u32_default(dev, "gpio-count", 0);
194         uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
195         if (uc_priv->bank_name == NULL)
196                 uc_priv->bank_name = "pm8916";
197
198         return 0;
199 }
200
201 static const struct udevice_id pm8916_gpio_ids[] = {
202         { .compatible = "qcom,pm8916-gpio" },
203         { .compatible = "qcom,pm8994-gpio" },   /* 22 GPIO's */
204         { }
205 };
206
207 U_BOOT_DRIVER(gpio_pm8916) = {
208         .name   = "gpio_pm8916",
209         .id     = UCLASS_GPIO,
210         .of_match = pm8916_gpio_ids,
211         .ofdata_to_platdata = pm8916_gpio_ofdata_to_platdata,
212         .probe  = pm8916_gpio_probe,
213         .ops    = &pm8916_gpio_ops,
214         .priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank),
215 };
216
217
218 /* Add pmic buttons as GPIO as well - there is no generic way for now */
219 #define PON_INT_RT_STS                        0x10
220 #define KPDPWR_ON_INT_BIT                     0
221 #define RESIN_ON_INT_BIT                      1
222
223 static int pm8941_pwrkey_get_function(struct udevice *dev, unsigned offset)
224 {
225         return GPIOF_INPUT;
226 }
227
228 static int pm8941_pwrkey_get_value(struct udevice *dev, unsigned offset)
229 {
230         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
231
232         int reg = pmic_reg_read(dev->parent, priv->pid + PON_INT_RT_STS);
233
234         if (reg < 0)
235                 return 0;
236
237         switch (offset) {
238         case 0: /* Power button */
239                 return (reg & BIT(KPDPWR_ON_INT_BIT)) != 0;
240                 break;
241         case 1: /* Reset button */
242         default:
243                 return (reg & BIT(RESIN_ON_INT_BIT)) != 0;
244                 break;
245         }
246 }
247
248 static const struct dm_gpio_ops pm8941_pwrkey_ops = {
249         .get_value              = pm8941_pwrkey_get_value,
250         .get_function           = pm8941_pwrkey_get_function,
251 };
252
253 static int pm8941_pwrkey_probe(struct udevice *dev)
254 {
255         struct pm8916_gpio_bank *priv = dev_get_priv(dev);
256         int reg;
257
258         priv->pid = devfdt_get_addr(dev);
259         if (priv->pid == FDT_ADDR_T_NONE)
260                 return -EINVAL;
261
262         /* Do a sanity check */
263         reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
264         if (reg != 0x1)
265                 return -ENODEV;
266
267         reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
268         if (reg != 0x1)
269                 return -ENODEV;
270
271         return 0;
272 }
273
274 static int pm8941_pwrkey_ofdata_to_platdata(struct udevice *dev)
275 {
276         struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
277
278         uc_priv->gpio_count = 2;
279         uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
280         if (uc_priv->bank_name == NULL)
281                 uc_priv->bank_name = "pm8916_key";
282
283         return 0;
284 }
285
286 static const struct udevice_id pm8941_pwrkey_ids[] = {
287         { .compatible = "qcom,pm8916-pwrkey" },
288         { .compatible = "qcom,pm8994-pwrkey" },
289         { }
290 };
291
292 U_BOOT_DRIVER(pwrkey_pm8941) = {
293         .name   = "pwrkey_pm8916",
294         .id     = UCLASS_GPIO,
295         .of_match = pm8941_pwrkey_ids,
296         .ofdata_to_platdata = pm8941_pwrkey_ofdata_to_platdata,
297         .probe  = pm8941_pwrkey_probe,
298         .ops    = &pm8941_pwrkey_ops,
299         .priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank),
300 };