dm: core: Create a new header file for 'compat' features
[oweals/u-boot.git] / drivers / power / pmic / stpmic1.c
1 // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
2 /*
3  * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <i2c.h>
10 #include <misc.h>
11 #include <sysreset.h>
12 #include <time.h>
13 #include <dm/device.h>
14 #include <dm/device_compat.h>
15 #include <dm/lists.h>
16 #include <power/pmic.h>
17 #include <power/stpmic1.h>
18
19 #define STPMIC1_NUM_OF_REGS 0x100
20
21 #define STPMIC1_NVM_SIZE 8
22 #define STPMIC1_NVM_POLL_TIMEOUT 100000
23 #define STPMIC1_NVM_START_ADDRESS 0xf8
24
25 enum pmic_nvm_op {
26         SHADOW_READ,
27         SHADOW_WRITE,
28         NVM_READ,
29         NVM_WRITE,
30 };
31
32 #if CONFIG_IS_ENABLED(DM_REGULATOR)
33 static const struct pmic_child_info stpmic1_children_info[] = {
34         { .prefix = "ldo", .driver = "stpmic1_ldo" },
35         { .prefix = "buck", .driver = "stpmic1_buck" },
36         { .prefix = "vref_ddr", .driver = "stpmic1_vref_ddr" },
37         { .prefix = "pwr_sw", .driver = "stpmic1_pwr_sw" },
38         { .prefix = "boost", .driver = "stpmic1_boost" },
39         { },
40 };
41 #endif /* DM_REGULATOR */
42
43 static int stpmic1_reg_count(struct udevice *dev)
44 {
45         return STPMIC1_NUM_OF_REGS;
46 }
47
48 static int stpmic1_write(struct udevice *dev, uint reg, const uint8_t *buff,
49                          int len)
50 {
51         int ret;
52
53         ret = dm_i2c_write(dev, reg, buff, len);
54         if (ret)
55                 dev_err(dev, "%s: failed to write register %#x :%d",
56                         __func__, reg, ret);
57
58         return ret;
59 }
60
61 static int stpmic1_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
62 {
63         int ret;
64
65         ret = dm_i2c_read(dev, reg, buff, len);
66         if (ret)
67                 dev_err(dev, "%s: failed to read register %#x : %d",
68                         __func__, reg, ret);
69
70         return ret;
71 }
72
73 static int stpmic1_bind(struct udevice *dev)
74 {
75         int ret;
76 #if CONFIG_IS_ENABLED(DM_REGULATOR)
77         ofnode regulators_node;
78         int children;
79
80         regulators_node = dev_read_subnode(dev, "regulators");
81         if (!ofnode_valid(regulators_node)) {
82                 dev_dbg(dev, "regulators subnode not found!");
83                 return -ENXIO;
84         }
85         dev_dbg(dev, "found regulators subnode\n");
86
87         children = pmic_bind_children(dev, regulators_node,
88                                       stpmic1_children_info);
89         if (!children)
90                 dev_dbg(dev, "no child found\n");
91 #endif /* DM_REGULATOR */
92
93         if (!IS_ENABLED(CONFIG_SPL_BUILD)) {
94                 ret = device_bind_driver(dev, "stpmic1-nvm",
95                                          "stpmic1-nvm", NULL);
96                 if (ret)
97                         return ret;
98         }
99
100         if (CONFIG_IS_ENABLED(SYSRESET))
101                 return device_bind_driver(dev, "stpmic1-sysreset",
102                                           "stpmic1-sysreset", NULL);
103
104         return 0;
105 }
106
107 static struct dm_pmic_ops stpmic1_ops = {
108         .reg_count = stpmic1_reg_count,
109         .read = stpmic1_read,
110         .write = stpmic1_write,
111 };
112
113 static const struct udevice_id stpmic1_ids[] = {
114         { .compatible = "st,stpmic1" },
115         { }
116 };
117
118 U_BOOT_DRIVER(pmic_stpmic1) = {
119         .name = "stpmic1_pmic",
120         .id = UCLASS_PMIC,
121         .of_match = stpmic1_ids,
122         .bind = stpmic1_bind,
123         .ops = &stpmic1_ops,
124 };
125
126 #ifndef CONFIG_SPL_BUILD
127 static int stpmic1_nvm_rw(struct udevice *dev, u8 addr, u8 *buf, int buf_len,
128                           enum pmic_nvm_op op)
129 {
130         unsigned long timeout;
131         u8 cmd = STPMIC1_NVM_CMD_READ;
132         int ret, len = buf_len;
133
134         if (addr < STPMIC1_NVM_START_ADDRESS)
135                 return -EACCES;
136         if (addr + buf_len > STPMIC1_NVM_START_ADDRESS + STPMIC1_NVM_SIZE)
137                 len = STPMIC1_NVM_START_ADDRESS + STPMIC1_NVM_SIZE - addr;
138
139         if (op == SHADOW_READ) {
140                 ret = pmic_read(dev, addr, buf, len);
141                 if (ret < 0)
142                         return ret;
143                 else
144                         return len;
145         }
146
147         if (op == SHADOW_WRITE) {
148                 ret = pmic_write(dev, addr, buf, len);
149                 if (ret < 0)
150                         return ret;
151                 else
152                         return len;
153         }
154
155         if (op == NVM_WRITE) {
156                 cmd = STPMIC1_NVM_CMD_PROGRAM;
157
158                 ret = pmic_write(dev, addr, buf, len);
159                 if (ret < 0)
160                         return ret;
161         }
162
163         ret = pmic_reg_read(dev, STPMIC1_NVM_CR);
164         if (ret < 0)
165                 return ret;
166
167         ret = pmic_reg_write(dev, STPMIC1_NVM_CR, ret | cmd);
168         if (ret < 0)
169                 return ret;
170
171         timeout = timer_get_us() + STPMIC1_NVM_POLL_TIMEOUT;
172         for (;;) {
173                 ret = pmic_reg_read(dev, STPMIC1_NVM_SR);
174                 if (ret < 0)
175                         return ret;
176
177                 if (!(ret & STPMIC1_NVM_BUSY))
178                         break;
179
180                 if (time_after(timer_get_us(), timeout))
181                         break;
182         }
183
184         if (ret & STPMIC1_NVM_BUSY)
185                 return -ETIMEDOUT;
186
187         if (op == NVM_READ) {
188                 ret = pmic_read(dev, addr, buf, len);
189                 if (ret < 0)
190                         return ret;
191         }
192
193         return len;
194 }
195
196 static int stpmic1_nvm_read(struct udevice *dev, int offset,
197                             void *buf, int size)
198 {
199         enum pmic_nvm_op op = NVM_READ;
200
201         if (offset < 0) {
202                 op = SHADOW_READ;
203                 offset = -offset;
204         }
205
206         return stpmic1_nvm_rw(dev->parent, offset, buf, size, op);
207 }
208
209 static int stpmic1_nvm_write(struct udevice *dev, int offset,
210                              const void *buf, int size)
211 {
212         enum pmic_nvm_op op = NVM_WRITE;
213
214         if (offset < 0) {
215                 op = SHADOW_WRITE;
216                 offset = -offset;
217         }
218
219         return stpmic1_nvm_rw(dev->parent, offset, (void *)buf, size, op);
220 }
221
222 static const struct misc_ops stpmic1_nvm_ops = {
223         .read = stpmic1_nvm_read,
224         .write = stpmic1_nvm_write,
225 };
226
227 U_BOOT_DRIVER(stpmic1_nvm) = {
228         .name = "stpmic1-nvm",
229         .id = UCLASS_MISC,
230         .ops = &stpmic1_nvm_ops,
231 };
232 #endif /* CONFIG_SPL_BUILD */
233
234 #ifdef CONFIG_SYSRESET
235 static int stpmic1_sysreset_request(struct udevice *dev, enum sysreset_t type)
236 {
237         struct udevice *pmic_dev = dev->parent;
238         int ret;
239
240         if (type != SYSRESET_POWER && type != SYSRESET_POWER_OFF)
241                 return -EPROTONOSUPPORT;
242
243         ret = pmic_reg_read(pmic_dev, STPMIC1_MAIN_CR);
244         if (ret < 0)
245                 return ret;
246
247         ret |= STPMIC1_SWOFF;
248         ret &= ~STPMIC1_RREQ_EN;
249         /* request Power Cycle */
250         if (type == SYSRESET_POWER)
251                 ret |= STPMIC1_RREQ_EN;
252
253         ret = pmic_reg_write(pmic_dev, STPMIC1_MAIN_CR, ret);
254         if (ret < 0)
255                 return ret;
256
257         return -EINPROGRESS;
258 }
259
260 static struct sysreset_ops stpmic1_sysreset_ops = {
261         .request = stpmic1_sysreset_request,
262 };
263
264 U_BOOT_DRIVER(stpmic1_sysreset) = {
265         .name = "stpmic1-sysreset",
266         .id = UCLASS_SYSRESET,
267         .ops = &stpmic1_sysreset_ops,
268 };
269 #endif