blob: 2297af4157a4513fc631174cd4708026bd85f64b [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>
14#include <dm/lists.h>
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010015#include <power/pmic.h>
Patrick Delaunayd46c22b2019-02-04 11:26:16 +010016#include <power/stpmic1.h>
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010017
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010018#define STPMIC1_NUM_OF_REGS 0x100
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010019
Patrick Delaunay31e45a12019-02-04 11:26:22 +010020#define STPMIC1_NVM_SIZE 8
21#define STPMIC1_NVM_POLL_TIMEOUT 100000
22#define STPMIC1_NVM_START_ADDRESS 0xf8
23
24enum pmic_nvm_op {
25 SHADOW_READ,
26 SHADOW_WRITE,
27 NVM_READ,
28 NVM_WRITE,
29};
30
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010031#if CONFIG_IS_ENABLED(DM_REGULATOR)
32static const struct pmic_child_info stpmic1_children_info[] = {
33 { .prefix = "ldo", .driver = "stpmic1_ldo" },
34 { .prefix = "buck", .driver = "stpmic1_buck" },
35 { .prefix = "vref_ddr", .driver = "stpmic1_vref_ddr" },
36 { .prefix = "pwr_sw", .driver = "stpmic1_pwr_sw" },
37 { .prefix = "boost", .driver = "stpmic1_boost" },
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020038 { },
39};
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010040#endif /* DM_REGULATOR */
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020041
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010042static int stpmic1_reg_count(struct udevice *dev)
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010043{
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010044 return STPMIC1_NUM_OF_REGS;
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010045}
46
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010047static int stpmic1_write(struct udevice *dev, uint reg, const uint8_t *buff,
48 int len)
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010049{
50 int ret;
51
52 ret = dm_i2c_write(dev, reg, buff, len);
53 if (ret)
54 dev_err(dev, "%s: failed to write register %#x :%d",
55 __func__, reg, ret);
56
57 return ret;
58}
59
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010060static int stpmic1_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +010061{
62 int ret;
63
64 ret = dm_i2c_read(dev, reg, buff, len);
65 if (ret)
66 dev_err(dev, "%s: failed to read register %#x : %d",
67 __func__, reg, ret);
68
69 return ret;
70}
71
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010072static int stpmic1_bind(struct udevice *dev)
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020073{
Patrick Delaunay234a6022019-08-02 13:08:03 +020074 int ret;
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010075#if CONFIG_IS_ENABLED(DM_REGULATOR)
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020076 ofnode regulators_node;
77 int children;
78
79 regulators_node = dev_read_subnode(dev, "regulators");
80 if (!ofnode_valid(regulators_node)) {
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010081 dev_dbg(dev, "regulators subnode not found!");
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020082 return -ENXIO;
83 }
84 dev_dbg(dev, "found regulators subnode\n");
85
86 children = pmic_bind_children(dev, regulators_node,
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010087 stpmic1_children_info);
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020088 if (!children)
89 dev_dbg(dev, "no child found\n");
Patrick Delaunay42f01aa2019-02-04 11:26:17 +010090#endif /* DM_REGULATOR */
Patrice Chotard1f0dfa12018-04-26 17:13:10 +020091
Patrick Delaunay234a6022019-08-02 13:08:03 +020092 if (!IS_ENABLED(CONFIG_SPL_BUILD)) {
93 ret = device_bind_driver(dev, "stpmic1-nvm",
94 "stpmic1-nvm", NULL);
95 if (ret)
96 return ret;
97 }
98
Patrick Delaunay8811583e2019-02-04 11:26:19 +010099 if (CONFIG_IS_ENABLED(SYSRESET))
100 return device_bind_driver(dev, "stpmic1-sysreset",
101 "stpmic1-sysreset", NULL);
102
Patrice Chotard1f0dfa12018-04-26 17:13:10 +0200103 return 0;
104}
105
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100106static struct dm_pmic_ops stpmic1_ops = {
107 .reg_count = stpmic1_reg_count,
108 .read = stpmic1_read,
109 .write = stpmic1_write,
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100110};
111
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100112static const struct udevice_id stpmic1_ids[] = {
113 { .compatible = "st,stpmic1" },
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100114 { }
115};
116
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100117U_BOOT_DRIVER(pmic_stpmic1) = {
118 .name = "stpmic1_pmic",
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100119 .id = UCLASS_PMIC,
Patrick Delaunay42f01aa2019-02-04 11:26:17 +0100120 .of_match = stpmic1_ids,
121 .bind = stpmic1_bind,
122 .ops = &stpmic1_ops,
Patrick Delaunay5d0c74e2018-03-12 10:46:12 +0100123};
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100124
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100125#ifndef CONFIG_SPL_BUILD
Patrick Delaunay234a6022019-08-02 13:08:03 +0200126static int stpmic1_nvm_rw(struct udevice *dev, u8 addr, u8 *buf, int buf_len,
127 enum pmic_nvm_op op)
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100128{
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100129 unsigned long timeout;
130 u8 cmd = STPMIC1_NVM_CMD_READ;
Patrick Delaunay234a6022019-08-02 13:08:03 +0200131 int ret, len = buf_len;
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100132
133 if (addr < STPMIC1_NVM_START_ADDRESS)
134 return -EACCES;
Patrick Delaunay234a6022019-08-02 13:08:03 +0200135 if (addr + buf_len > STPMIC1_NVM_START_ADDRESS + STPMIC1_NVM_SIZE)
136 len = STPMIC1_NVM_START_ADDRESS + STPMIC1_NVM_SIZE - addr;
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100137
Patrick Delaunay234a6022019-08-02 13:08:03 +0200138 if (op == SHADOW_READ) {
139 ret = pmic_read(dev, addr, buf, len);
140 if (ret < 0)
141 return ret;
142 else
143 return len;
144 }
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100145
Patrick Delaunay234a6022019-08-02 13:08:03 +0200146 if (op == SHADOW_WRITE) {
147 ret = pmic_write(dev, addr, buf, len);
148 if (ret < 0)
149 return ret;
150 else
151 return len;
152 }
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100153
154 if (op == NVM_WRITE) {
155 cmd = STPMIC1_NVM_CMD_PROGRAM;
156
Patrick Delaunay234a6022019-08-02 13:08:03 +0200157 ret = pmic_write(dev, addr, buf, len);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100158 if (ret < 0)
159 return ret;
160 }
161
162 ret = pmic_reg_read(dev, STPMIC1_NVM_CR);
163 if (ret < 0)
164 return ret;
165
166 ret = pmic_reg_write(dev, STPMIC1_NVM_CR, ret | cmd);
167 if (ret < 0)
168 return ret;
169
170 timeout = timer_get_us() + STPMIC1_NVM_POLL_TIMEOUT;
171 for (;;) {
172 ret = pmic_reg_read(dev, STPMIC1_NVM_SR);
173 if (ret < 0)
174 return ret;
175
176 if (!(ret & STPMIC1_NVM_BUSY))
177 break;
178
179 if (time_after(timer_get_us(), timeout))
180 break;
181 }
182
183 if (ret & STPMIC1_NVM_BUSY)
184 return -ETIMEDOUT;
185
186 if (op == NVM_READ) {
Patrick Delaunay234a6022019-08-02 13:08:03 +0200187 ret = pmic_read(dev, addr, buf, len);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100188 if (ret < 0)
189 return ret;
190 }
191
Patrick Delaunay234a6022019-08-02 13:08:03 +0200192 return len;
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100193}
194
Patrick Delaunay234a6022019-08-02 13:08:03 +0200195static int stpmic1_nvm_read(struct udevice *dev, int offset,
196 void *buf, int size)
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100197{
Patrick Delaunay234a6022019-08-02 13:08:03 +0200198 enum pmic_nvm_op op = NVM_READ;
199
200 if (offset < 0) {
201 op = SHADOW_READ;
202 offset = -offset;
203 }
204
205 return stpmic1_nvm_rw(dev->parent, offset, buf, size, op);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100206}
207
Patrick Delaunay234a6022019-08-02 13:08:03 +0200208static int stpmic1_nvm_write(struct udevice *dev, int offset,
209 const void *buf, int size)
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100210{
Patrick Delaunay234a6022019-08-02 13:08:03 +0200211 enum pmic_nvm_op op = NVM_WRITE;
212
213 if (offset < 0) {
214 op = SHADOW_WRITE;
215 offset = -offset;
216 }
217
218 return stpmic1_nvm_rw(dev->parent, offset, (void *)buf, size, op);
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100219}
220
Patrick Delaunay234a6022019-08-02 13:08:03 +0200221static const struct misc_ops stpmic1_nvm_ops = {
222 .read = stpmic1_nvm_read,
223 .write = stpmic1_nvm_write,
224};
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100225
Patrick Delaunay234a6022019-08-02 13:08:03 +0200226U_BOOT_DRIVER(stpmic1_nvm) = {
227 .name = "stpmic1-nvm",
228 .id = UCLASS_MISC,
229 .ops = &stpmic1_nvm_ops,
230};
Patrick Delaunay31e45a12019-02-04 11:26:22 +0100231#endif /* CONFIG_SPL_BUILD */
232
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100233#ifdef CONFIG_SYSRESET
234static int stpmic1_sysreset_request(struct udevice *dev, enum sysreset_t type)
235{
Patrick Delaunayef32dcf2019-08-02 13:08:04 +0200236 struct udevice *pmic_dev = dev->parent;
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100237 int ret;
238
Patrick Delaunay82cd1a22019-05-20 09:47:07 +0200239 if (type != SYSRESET_POWER && type != SYSRESET_POWER_OFF)
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100240 return -EPROTONOSUPPORT;
241
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100242 ret = pmic_reg_read(pmic_dev, STPMIC1_MAIN_CR);
243 if (ret < 0)
244 return ret;
245
Patrick Delaunay82cd1a22019-05-20 09:47:07 +0200246 ret |= STPMIC1_SWOFF;
247 ret &= ~STPMIC1_RREQ_EN;
248 /* request Power Cycle */
249 if (type == SYSRESET_POWER)
250 ret |= STPMIC1_RREQ_EN;
251
252 ret = pmic_reg_write(pmic_dev, STPMIC1_MAIN_CR, ret);
Patrick Delaunay8811583e2019-02-04 11:26:19 +0100253 if (ret < 0)
254 return ret;
255
256 return -EINPROGRESS;
257}
258
259static struct sysreset_ops stpmic1_sysreset_ops = {
260 .request = stpmic1_sysreset_request,
261};
262
263U_BOOT_DRIVER(stpmic1_sysreset) = {
264 .name = "stpmic1-sysreset",
265 .id = UCLASS_SYSRESET,
266 .ops = &stpmic1_sysreset_ops,
267};
268#endif