blob: 8701d4f971c9ba04c4101b54205d6255e37801e2 [file] [log] [blame]
Tom Rini4549e782018-05-06 18:27:01 -04001// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +01002/*
3 * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +01004 */
5
6#include <common.h>
7#include <dm.h>
8#include <errno.h>
9#include <i2c.h>
Patrick Delaunay234a6022019-08-02 13:08:03 +020010#include <misc.h>
Patrick Delaunay8811583e2019-02-04 11:26:19 +010011#include <sysreset.h>
Simon Glass10453152019-11-14 12:57:30 -070012#include <time.h>
Patrick Delaunay8811583e2019-02-04 11:26:19 +010013#include <dm/device.h>
Simon Glass336d4612020-02-03 07:36:16 -070014#include <dm/device_compat.h>
Patrick Delaunay8811583e2019-02-04 11:26:19 +010015#include <dm/lists.h>
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010016#include <power/pmic.h>
Patrick Delaunayd46c22b2019-02-04 11:26:16 +010017#include <power/stpmic1.h>
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010018
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010019#define STPMIC1_NUM_OF_REGS 0x100
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010020
Patrick Delaunay31e45a12019-02-04 11:26:22 +010021#define STPMIC1_NVM_SIZE 8
22#define STPMIC1_NVM_POLL_TIMEOUT 100000
23#define STPMIC1_NVM_START_ADDRESS 0xf8
24
25enum pmic_nvm_op {
26 SHADOW_READ,
27 SHADOW_WRITE,
28 NVM_READ,
29 NVM_WRITE,
30};
31
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010032#if CONFIG_IS_ENABLED(DM_REGULATOR)
33static 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" },
Patrick Delaunayabddd782023-04-27 15:36:38 +020037 { .prefix = "vref-ddr", .driver = "stpmic1_vref_ddr" },
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010038 { .prefix = "pwr_sw", .driver = "stpmic1_pwr_sw" },
Patrick Delaunayabddd782023-04-27 15:36:38 +020039 { .prefix = "pwr-sw", .driver = "stpmic1_pwr_sw" },
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010040 { .prefix = "boost", .driver = "stpmic1_boost" },
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020041 { },
42};
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010043#endif /* DM_REGULATOR */
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020044
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010045static int stpmic1_reg_count(struct udevice *dev)
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010046{
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010047 return STPMIC1_NUM_OF_REGS;
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010048}
49
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010050static int stpmic1_write(struct udevice *dev, uint reg, const uint8_t *buff,
51 int len)
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010052{
53 int ret;
54
55 ret = dm_i2c_write(dev, reg, buff, len);
56 if (ret)
57 dev_err(dev, "%s: failed to write register %#x :%d",
58 __func__, reg, ret);
59
60 return ret;
61}
62
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010063static int stpmic1_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010064{
65 int ret;
66
67 ret = dm_i2c_read(dev, reg, buff, len);
68 if (ret)
69 dev_err(dev, "%s: failed to read register %#x : %d",
70 __func__, reg, ret);
71
72 return ret;
73}
74
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010075static int stpmic1_bind(struct udevice *dev)
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020076{
Patrick Delaunay234a6022019-08-02 13:08:03 +020077 int ret;
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010078#if CONFIG_IS_ENABLED(DM_REGULATOR)
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020079 ofnode regulators_node;
80 int children;
81
82 regulators_node = dev_read_subnode(dev, "regulators");
83 if (!ofnode_valid(regulators_node)) {
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010084 dev_dbg(dev, "regulators subnode not found!");
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020085 return -ENXIO;
86 }
87 dev_dbg(dev, "found regulators subnode\n");
88
89 children = pmic_bind_children(dev, regulators_node,
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010090 stpmic1_children_info);
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020091 if (!children)
92 dev_dbg(dev, "no child found\n");
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010093#endif /* DM_REGULATOR */
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020094
Patrick Delaunay234a6022019-08-02 13:08:03 +020095 if (!IS_ENABLED(CONFIG_SPL_BUILD)) {
96 ret = device_bind_driver(dev, "stpmic1-nvm",
97 "stpmic1-nvm", NULL);
98 if (ret)
99 return ret;
100 }
101
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100102 if (CONFIG_IS_ENABLED(SYSRESET))
103 return device_bind_driver(dev, "stpmic1-sysreset",
104 "stpmic1-sysreset", NULL);
105
Patrice Chotard1f0dfa12018-04-26 17:13:10 +0200106 return 0;
107}
108
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100109static struct dm_pmic_ops stpmic1_ops = {
110 .reg_count = stpmic1_reg_count,
111 .read = stpmic1_read,
112 .write = stpmic1_write,
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100113};
114
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100115static const struct udevice_id stpmic1_ids[] = {
116 { .compatible = "st,stpmic1" },
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100117 { }
118};
119
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100120U_BOOT_DRIVER(pmic_stpmic1) = {
121 .name = "stpmic1_pmic",
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100122 .id = UCLASS_PMIC,
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100123 .of_match = stpmic1_ids,
124 .bind = stpmic1_bind,
125 .ops = &stpmic1_ops,
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100126};
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100127
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100128#ifndef CONFIG_SPL_BUILD
Patrick Delaunay234a6022019-08-02 13:08:03 +0200129static int stpmic1_nvm_rw(struct udevice *dev, u8 addr, u8 *buf, int buf_len,
130 enum pmic_nvm_op op)
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100131{
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100132 unsigned long timeout;
133 u8 cmd = STPMIC1_NVM_CMD_READ;
Patrick Delaunay234a6022019-08-02 13:08:03 +0200134 int ret, len = buf_len;
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100135
136 if (addr < STPMIC1_NVM_START_ADDRESS)
137 return -EACCES;
Patrick Delaunay234a6022019-08-02 13:08:03 +0200138 if (addr + buf_len > STPMIC1_NVM_START_ADDRESS + STPMIC1_NVM_SIZE)
139 len = STPMIC1_NVM_START_ADDRESS + STPMIC1_NVM_SIZE - addr;
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100140
Patrick Delaunay234a6022019-08-02 13:08:03 +0200141 if (op == SHADOW_READ) {
142 ret = pmic_read(dev, addr, buf, len);
143 if (ret < 0)
144 return ret;
145 else
146 return len;
147 }
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100148
Patrick Delaunay234a6022019-08-02 13:08:03 +0200149 if (op == SHADOW_WRITE) {
150 ret = pmic_write(dev, addr, buf, len);
151 if (ret < 0)
152 return ret;
153 else
154 return len;
155 }
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100156
157 if (op == NVM_WRITE) {
158 cmd = STPMIC1_NVM_CMD_PROGRAM;
159
Patrick Delaunay234a6022019-08-02 13:08:03 +0200160 ret = pmic_write(dev, addr, buf, len);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100161 if (ret < 0)
162 return ret;
163 }
164
165 ret = pmic_reg_read(dev, STPMIC1_NVM_CR);
166 if (ret < 0)
167 return ret;
168
169 ret = pmic_reg_write(dev, STPMIC1_NVM_CR, ret | cmd);
170 if (ret < 0)
171 return ret;
172
173 timeout = timer_get_us() + STPMIC1_NVM_POLL_TIMEOUT;
174 for (;;) {
175 ret = pmic_reg_read(dev, STPMIC1_NVM_SR);
176 if (ret < 0)
177 return ret;
178
179 if (!(ret & STPMIC1_NVM_BUSY))
180 break;
181
182 if (time_after(timer_get_us(), timeout))
183 break;
184 }
185
186 if (ret & STPMIC1_NVM_BUSY)
187 return -ETIMEDOUT;
188
189 if (op == NVM_READ) {
Patrick Delaunay234a6022019-08-02 13:08:03 +0200190 ret = pmic_read(dev, addr, buf, len);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100191 if (ret < 0)
192 return ret;
193 }
194
Patrick Delaunay234a6022019-08-02 13:08:03 +0200195 return len;
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100196}
197
Patrick Delaunay234a6022019-08-02 13:08:03 +0200198static int stpmic1_nvm_read(struct udevice *dev, int offset,
199 void *buf, int size)
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100200{
Patrick Delaunay234a6022019-08-02 13:08:03 +0200201 enum pmic_nvm_op op = NVM_READ;
202
203 if (offset < 0) {
204 op = SHADOW_READ;
205 offset = -offset;
206 }
207
208 return stpmic1_nvm_rw(dev->parent, offset, buf, size, op);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100209}
210
Patrick Delaunay234a6022019-08-02 13:08:03 +0200211static int stpmic1_nvm_write(struct udevice *dev, int offset,
212 const void *buf, int size)
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100213{
Patrick Delaunay234a6022019-08-02 13:08:03 +0200214 enum pmic_nvm_op op = NVM_WRITE;
215
216 if (offset < 0) {
217 op = SHADOW_WRITE;
218 offset = -offset;
219 }
220
221 return stpmic1_nvm_rw(dev->parent, offset, (void *)buf, size, op);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100222}
223
Patrick Delaunay234a6022019-08-02 13:08:03 +0200224static const struct misc_ops stpmic1_nvm_ops = {
225 .read = stpmic1_nvm_read,
226 .write = stpmic1_nvm_write,
227};
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100228
Patrick Delaunay234a6022019-08-02 13:08:03 +0200229U_BOOT_DRIVER(stpmic1_nvm) = {
230 .name = "stpmic1-nvm",
231 .id = UCLASS_MISC,
232 .ops = &stpmic1_nvm_ops,
233};
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100234#endif /* CONFIG_SPL_BUILD */
235
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100236#ifdef CONFIG_SYSRESET
237static int stpmic1_sysreset_request(struct udevice *dev, enum sysreset_t type)
238{
Patrick Delaunayef32dcf2019-08-02 13:08:04 +0200239 struct udevice *pmic_dev = dev->parent;
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100240 int ret;
241
Patrick Delaunay82cd1a22019-05-20 09:47:07 +0200242 if (type != SYSRESET_POWER && type != SYSRESET_POWER_OFF)
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100243 return -EPROTONOSUPPORT;
244
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100245 ret = pmic_reg_read(pmic_dev, STPMIC1_MAIN_CR);
246 if (ret < 0)
247 return ret;
248
Patrick Delaunay82cd1a22019-05-20 09:47:07 +0200249 ret |= STPMIC1_SWOFF;
250 ret &= ~STPMIC1_RREQ_EN;
251 /* request Power Cycle */
252 if (type == SYSRESET_POWER)
253 ret |= STPMIC1_RREQ_EN;
254
255 ret = pmic_reg_write(pmic_dev, STPMIC1_MAIN_CR, ret);
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100256 if (ret < 0)
257 return ret;
258
259 return -EINPROGRESS;
260}
261
262static struct sysreset_ops stpmic1_sysreset_ops = {
263 .request = stpmic1_sysreset_request,
264};
265
266U_BOOT_DRIVER(stpmic1_sysreset) = {
267 .name = "stpmic1-sysreset",
268 .id = UCLASS_SYSRESET,
269 .ops = &stpmic1_sysreset_ops,
270};
271#endif