power: axp209: VBUS detection support
[oweals/u-boot.git] / drivers / power / axp209.c
1 /*
2  * (C) Copyright 2012
3  * Henrik Nordstrom <henrik@henriknordstrom.net>
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9 #include <i2c.h>
10 #include <asm/arch/gpio.h>
11 #include <axp209.h>
12
13 enum axp209_reg {
14         AXP209_POWER_STATUS = 0x00,
15         AXP209_CHIP_VERSION = 0x03,
16         AXP209_DCDC2_VOLTAGE = 0x23,
17         AXP209_DCDC3_VOLTAGE = 0x27,
18         AXP209_LDO24_VOLTAGE = 0x28,
19         AXP209_LDO3_VOLTAGE = 0x29,
20         AXP209_IRQ_ENABLE1 = 0x40,
21         AXP209_IRQ_ENABLE2 = 0x41,
22         AXP209_IRQ_ENABLE3 = 0x42,
23         AXP209_IRQ_ENABLE4 = 0x43,
24         AXP209_IRQ_ENABLE5 = 0x44,
25         AXP209_IRQ_STATUS5 = 0x4c,
26         AXP209_SHUTDOWN = 0x32,
27         AXP209_GPIO0_CTRL = 0x90,
28         AXP209_GPIO1_CTRL = 0x92,
29         AXP209_GPIO2_CTRL = 0x93,
30         AXP209_GPIO_STATE = 0x94,
31         AXP209_GPIO3_CTRL = 0x95,
32 };
33
34 #define AXP209_POWER_STATUS_ON_BY_DC    (1 << 0)
35 #define AXP209_POWER_STATUS_VBUS_USABLE (1 << 4)
36
37 #define AXP209_IRQ5_PEK_UP              (1 << 6)
38 #define AXP209_IRQ5_PEK_DOWN            (1 << 5)
39
40 #define AXP209_POWEROFF                 (1 << 7)
41
42 #define AXP209_GPIO_OUTPUT_LOW          0x00 /* Drive pin low */
43 #define AXP209_GPIO_OUTPUT_HIGH         0x01 /* Drive pin high */
44 #define AXP209_GPIO_INPUT               0x02 /* Float pin */
45
46 /* GPIO3 is different from the others */
47 #define AXP209_GPIO3_OUTPUT_LOW         0x00 /* Drive pin low, Output mode */
48 #define AXP209_GPIO3_OUTPUT_HIGH        0x02 /* Float pin, Output mode */
49 #define AXP209_GPIO3_INPUT              0x06 /* Float pin, Input mode */
50
51 static int axp209_write(enum axp209_reg reg, u8 val)
52 {
53         return i2c_write(0x34, reg, 1, &val, 1);
54 }
55
56 static int axp209_read(enum axp209_reg reg, u8 *val)
57 {
58         return i2c_read(0x34, reg, 1, val, 1);
59 }
60
61 static u8 axp209_mvolt_to_cfg(int mvolt, int min, int max, int div)
62 {
63         if (mvolt < min)
64                 mvolt = min;
65         else if (mvolt > max)
66                 mvolt = max;
67
68         return (mvolt - min) / div;
69 }
70
71 int axp209_set_dcdc2(int mvolt)
72 {
73         int rc;
74         u8 cfg, current;
75
76         cfg = axp209_mvolt_to_cfg(mvolt, 700, 2275, 25);
77
78         /* Do we really need to be this gentle? It has built-in voltage slope */
79         while ((rc = axp209_read(AXP209_DCDC2_VOLTAGE, &current)) == 0 &&
80                current != cfg) {
81                 if (current < cfg)
82                         current++;
83                 else
84                         current--;
85
86                 rc = axp209_write(AXP209_DCDC2_VOLTAGE, current);
87                 if (rc)
88                         break;
89         }
90
91         return rc;
92 }
93
94 int axp209_set_dcdc3(int mvolt)
95 {
96         u8 cfg = axp209_mvolt_to_cfg(mvolt, 700, 3500, 25);
97
98         return axp209_write(AXP209_DCDC3_VOLTAGE, cfg);
99 }
100
101 int axp209_set_ldo2(int mvolt)
102 {
103         int rc;
104         u8 cfg, reg;
105
106         cfg = axp209_mvolt_to_cfg(mvolt, 1800, 3300, 100);
107
108         rc = axp209_read(AXP209_LDO24_VOLTAGE, &reg);
109         if (rc)
110                 return rc;
111
112         /* LDO2 configuration is in upper 4 bits */
113         reg = (reg & 0x0f) | (cfg << 4);
114         return axp209_write(AXP209_LDO24_VOLTAGE, reg);
115 }
116
117 int axp209_set_ldo3(int mvolt)
118 {
119         u8 cfg;
120
121         if (mvolt == -1)
122                 cfg = 0x80;     /* determined by LDO3IN pin */
123         else
124                 cfg = axp209_mvolt_to_cfg(mvolt, 700, 3500, 25);
125
126         return axp209_write(AXP209_LDO3_VOLTAGE, cfg);
127 }
128
129 int axp209_set_ldo4(int mvolt)
130 {
131         int rc;
132         static const int vindex[] = {
133                 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2500,
134                 2700, 2800, 3000, 3100, 3200, 3300
135         };
136         u8 cfg, reg;
137
138         /* Translate mvolt to register cfg value, requested <= selected */
139         for (cfg = 15; vindex[cfg] > mvolt && cfg > 0; cfg--);
140
141         rc = axp209_read(AXP209_LDO24_VOLTAGE, &reg);
142         if (rc)
143                 return rc;
144
145         /* LDO4 configuration is in lower 4 bits */
146         reg = (reg & 0xf0) | (cfg << 0);
147         return axp209_write(AXP209_LDO24_VOLTAGE, reg);
148 }
149
150 int axp209_init(void)
151 {
152         u8 ver;
153         int i, rc;
154
155         rc = axp209_read(AXP209_CHIP_VERSION, &ver);
156         if (rc)
157                 return rc;
158
159         /* Low 4 bits is chip version */
160         ver &= 0x0f;
161
162         if (ver != 0x1)
163                 return -1;
164
165         /* Mask all interrupts */
166         for (i = AXP209_IRQ_ENABLE1; i <= AXP209_IRQ_ENABLE5; i++) {
167                 rc = axp209_write(i, 0);
168                 if (rc)
169                         return rc;
170         }
171
172         return 0;
173 }
174
175 int axp209_poweron_by_dc(void)
176 {
177         u8 v;
178
179         if (axp209_read(AXP209_POWER_STATUS, &v))
180                 return 0;
181
182         return (v & AXP209_POWER_STATUS_ON_BY_DC);
183 }
184
185 int axp209_power_button(void)
186 {
187         u8 v;
188
189         if (axp209_read(AXP209_IRQ_STATUS5, &v))
190                 return 0;
191
192         axp209_write(AXP209_IRQ_STATUS5, AXP209_IRQ5_PEK_DOWN);
193
194         return v & AXP209_IRQ5_PEK_DOWN;
195 }
196
197 static u8 axp209_get_gpio_ctrl_reg(unsigned int pin)
198 {
199         switch (pin) {
200         case 0: return AXP209_GPIO0_CTRL;
201         case 1: return AXP209_GPIO1_CTRL;
202         case 2: return AXP209_GPIO2_CTRL;
203         case 3: return AXP209_GPIO3_CTRL;
204         }
205         return 0;
206 }
207
208 int axp_gpio_direction_input(unsigned int pin)
209 {
210         if (pin == SUNXI_GPIO_AXP0_VBUS_DETECT)
211                 return 0;
212
213         u8 reg = axp209_get_gpio_ctrl_reg(pin);
214         /* GPIO3 is "special" */
215         u8 val = (pin == 3) ? AXP209_GPIO3_INPUT : AXP209_GPIO_INPUT;
216
217         return axp209_write(reg, val);
218 }
219
220 int axp_gpio_direction_output(unsigned int pin, unsigned int val)
221 {
222         u8 reg = axp209_get_gpio_ctrl_reg(pin);
223
224         if (val) {
225                 val = (pin == 3) ? AXP209_GPIO3_OUTPUT_HIGH :
226                                    AXP209_GPIO_OUTPUT_HIGH;
227         } else {
228                 val = (pin == 3) ? AXP209_GPIO3_OUTPUT_LOW :
229                                    AXP209_GPIO_OUTPUT_LOW;
230         }
231
232         return axp209_write(reg, val);
233 }
234
235 int axp_gpio_get_value(unsigned int pin)
236 {
237         u8 val, mask;
238         int rc;
239
240         if (pin == SUNXI_GPIO_AXP0_VBUS_DETECT) {
241                 rc = axp209_read(AXP209_POWER_STATUS, &val);
242                 mask = AXP209_POWER_STATUS_VBUS_USABLE;
243         } else if (pin == 3) {
244                 rc = axp209_read(AXP209_GPIO3_CTRL, &val);
245                 mask = 1;
246         } else {
247                 rc = axp209_read(AXP209_GPIO_STATE, &val);
248                 mask = 1 << (pin + 4);
249         }
250         if (rc)
251                 return rc;
252
253         return (val & mask) ? 1 : 0;
254 }
255
256 int axp_gpio_set_value(unsigned int pin, unsigned int val)
257 {
258         return axp_gpio_direction_output(pin, val);
259 }