xref: /openbmc/u-boot/drivers/gpio/pm8916_gpio.c (revision 1a68faac)
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 };
301