blob: e744243297b0af03f45d2233d7674ea40f2eea0d [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Max Krummenachereeb16b22016-11-30 19:43:09 +01002/*
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +01003 * Copyright (C) 2014-2019, Toradex AG
Max Krummenachereeb16b22016-11-30 19:43:09 +01004 */
5
6/*
7 * Helpers for Freescale PMIC PF0100
8*/
9
10#include <common.h>
11#include <i2c.h>
12#include <asm/arch/imx-regs.h>
13#include <asm/arch/iomux.h>
14#include <asm/arch/mx6-pins.h>
15#include <asm/gpio.h>
Stefano Babic33731bc2017-06-29 10:16:06 +020016#include <asm/mach-imx/iomux-v3.h>
Max Krummenachereeb16b22016-11-30 19:43:09 +010017
18#include "pf0100_otp.inc"
19#include "pf0100.h"
20
21/* define for PMIC register dump */
22/*#define DEBUG */
23
Gerard Salvatella6115ed72019-02-08 18:42:29 +010024#define WARNBAR "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
25
Max Krummenachereeb16b22016-11-30 19:43:09 +010026/* use GPIO: EXT_IO1 to switch on VPGM, ON: 1 */
Tom Rinic9515392018-01-03 09:19:17 -050027static __maybe_unused iomux_v3_cfg_t const pmic_prog_pads[] = {
Max Krummenachereeb16b22016-11-30 19:43:09 +010028 MX6_PAD_NANDF_D3__GPIO2_IO03 | MUX_PAD_CTRL(NO_PAD_CTRL),
29# define PMIC_PROG_VOLTAGE IMX_GPIO_NR(2, 3)
30};
31
32unsigned pmic_init(void)
33{
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +010034 int rc;
35 struct udevice *dev = NULL;
Max Krummenachereeb16b22016-11-30 19:43:09 +010036 unsigned programmed = 0;
37 uchar bus = 1;
38 uchar devid, revid, val;
39
Max Krummenacherc4725752019-02-08 18:42:20 +010040 puts("PMIC: ");
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +010041 rc = i2c_get_chip_for_busnum(bus, PFUZE100_I2C_ADDR, 1, &dev);
42 if (rc) {
43 printf("failed to get device for PMIC at address 0x%x\n",
44 PFUZE100_I2C_ADDR);
Max Krummenachereeb16b22016-11-30 19:43:09 +010045 return 0;
46 }
Gerard Salvatella6115ed72019-02-08 18:42:29 +010047
48 /* check for errors in PMIC fuses */
49 if (dm_i2c_read(dev, PFUZE100_INTSTAT3, &val, 1) < 0) {
50 puts("i2c pmic INTSTAT3 register read failed\n");
51 return 0;
52 }
53 if (val & PFUZE100_BIT_OTP_ECCI) {
54 puts("\n" WARNBAR);
55 puts("WARNING: ecc errors found in pmic fuse banks\n");
56 puts(WARNBAR);
57 }
58 if (dm_i2c_read(dev, PFUZE100_OTP_ECC_SE1, &val, 1) < 0) {
59 puts("i2c pmic ECC_SE1 register read failed\n");
60 return 0;
61 }
62 if (val & PFUZE100_BITS_ECC_SE1) {
63 puts(WARNBAR);
64 puts("WARNING: ecc has made bit corrections in banks 1 to 5\n");
65 puts(WARNBAR);
66 }
67 if (dm_i2c_read(dev, PFUZE100_OTP_ECC_SE2, &val, 1) < 0) {
68 puts("i2c pmic ECC_SE2 register read failed\n");
69 return 0;
70 }
71 if (val & PFUZE100_BITS_ECC_SE2) {
72 puts(WARNBAR);
73 puts("WARNING: ecc has made bit corrections in banks 6 to 10\n"
74 );
75 puts(WARNBAR);
76 }
77 if (dm_i2c_read(dev, PFUZE100_OTP_ECC_DE1, &val, 1) < 0) {
78 puts("i2c pmic ECC_DE register read failed\n");
79 return 0;
80 }
81 if (val & PFUZE100_BITS_ECC_DE1) {
82 puts(WARNBAR);
83 puts("ERROR: banks 1 to 5 have uncorrectable bits\n");
84 puts(WARNBAR);
85 }
86 if (dm_i2c_read(dev, PFUZE100_OTP_ECC_DE2, &val, 1) < 0) {
87 puts("i2c pmic ECC_DE register read failed\n");
88 return 0;
89 }
90 if (val & PFUZE100_BITS_ECC_DE2) {
91 puts(WARNBAR);
92 puts("ERROR: banks 6 to 10 have uncorrectable bits\n");
93 puts(WARNBAR);
94 }
95
Max Krummenachereeb16b22016-11-30 19:43:09 +010096 /* get device ident */
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +010097 if (dm_i2c_read(dev, PFUZE100_DEVICEID, &devid, 1) < 0) {
Max Krummenachereeb16b22016-11-30 19:43:09 +010098 puts("i2c pmic devid read failed\n");
99 return 0;
100 }
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100101 if (dm_i2c_read(dev, PFUZE100_REVID, &revid, 1) < 0) {
Max Krummenachereeb16b22016-11-30 19:43:09 +0100102 puts("i2c pmic revid read failed\n");
103 return 0;
104 }
Max Krummenacherc4725752019-02-08 18:42:20 +0100105 printf("device id: 0x%.2x, revision id: 0x%.2x, ", devid, revid);
106
107 /* get device programmed state */
108 val = PFUZE100_PAGE_REGISTER_PAGE1;
109 if (dm_i2c_write(dev, PFUZE100_PAGE_REGISTER, &val, 1)) {
110 puts("i2c write failed\n");
111 return 0;
112 }
113 if (dm_i2c_read(dev, PFUZE100_FUSE_POR1, &val, 1) < 0) {
114 puts("i2c fuse_por read failed\n");
115 return 0;
116 }
117 if (val & PFUZE100_FUSE_POR_M)
118 programmed++;
119
120 if (dm_i2c_read(dev, PFUZE100_FUSE_POR2, &val, 1) < 0) {
121 puts("i2c fuse_por read failed\n");
122 return programmed;
123 }
124 if (val & PFUZE100_FUSE_POR_M)
125 programmed++;
126
127 if (dm_i2c_read(dev, PFUZE100_FUSE_POR3, &val, 1) < 0) {
128 puts("i2c fuse_por read failed\n");
129 return programmed;
130 }
131 if (val & PFUZE100_FUSE_POR_M)
132 programmed++;
133
134 switch (programmed) {
135 case 0:
136 puts("not programmed\n");
137 break;
138 case 3:
139 puts("programmed\n");
140 break;
141 default:
142 puts("undefined programming state\n");
143 break;
144 }
Max Krummenachereeb16b22016-11-30 19:43:09 +0100145
146#ifdef DEBUG
147 {
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100148 unsigned int i, j;
Max Krummenachereeb16b22016-11-30 19:43:09 +0100149
150 for (i = 0; i < 16; i++)
151 printf("\t%x", i);
152 for (j = 0; j < 0x80; ) {
153 printf("\n%2x", j);
154 for (i = 0; i < 16; i++) {
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100155 dm_i2c_read(dev, j + i, &val, 1);
Max Krummenachereeb16b22016-11-30 19:43:09 +0100156 printf("\t%2x", val);
157 }
158 j += 0x10;
159 }
160 printf("\nEXT Page 1");
161
162 val = PFUZE100_PAGE_REGISTER_PAGE1;
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100163 if (dm_i2c_write(dev, PFUZE100_PAGE_REGISTER, &val, 1)) {
Max Krummenachereeb16b22016-11-30 19:43:09 +0100164 puts("i2c write failed\n");
165 return 0;
166 }
167
168 for (j = 0x80; j < 0x100; ) {
169 printf("\n%2x", j);
170 for (i = 0; i < 16; i++) {
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100171 dm_i2c_read(dev, j + i, &val, 1);
Max Krummenachereeb16b22016-11-30 19:43:09 +0100172 printf("\t%2x", val);
173 }
174 j += 0x10;
175 }
176 printf("\nEXT Page 2");
177
178 val = PFUZE100_PAGE_REGISTER_PAGE2;
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100179 if (dm_i2c_write(dev, PFUZE100_PAGE_REGISTER, &val, 1)) {
Max Krummenachereeb16b22016-11-30 19:43:09 +0100180 puts("i2c write failed\n");
181 return 0;
182 }
183
184 for (j = 0x80; j < 0x100; ) {
185 printf("\n%2x", j);
186 for (i = 0; i < 16; i++) {
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100187 dm_i2c_read(dev, j + i, &val, 1);
Max Krummenachereeb16b22016-11-30 19:43:09 +0100188 printf("\t%2x", val);
189 }
190 j += 0x10;
191 }
192 printf("\n");
193 }
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100194#endif /* DEBUG */
195
Max Krummenachereeb16b22016-11-30 19:43:09 +0100196 return programmed;
197}
198
Tom Rinic9515392018-01-03 09:19:17 -0500199#ifndef CONFIG_SPL_BUILD
200static int pf0100_prog(void)
Max Krummenachereeb16b22016-11-30 19:43:09 +0100201{
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100202 int rc;
203 struct udevice *dev = NULL;
Max Krummenachereeb16b22016-11-30 19:43:09 +0100204 unsigned char bus = 1;
205 unsigned char val;
206 unsigned int i;
207
208 if (pmic_init() == 3) {
209 puts("PMIC already programmed, exiting\n");
210 return CMD_RET_FAILURE;
211 }
212 /* set up gpio to manipulate vprog, initially off */
213 imx_iomux_v3_setup_multiple_pads(pmic_prog_pads,
214 ARRAY_SIZE(pmic_prog_pads));
215 gpio_direction_output(PMIC_PROG_VOLTAGE, 0);
216
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100217 rc = i2c_get_chip_for_busnum(bus, PFUZE100_I2C_ADDR, 1, &dev);
218 if (rc) {
219 printf("failed to get device for PMIC at address 0x%x\n",
220 PFUZE100_I2C_ADDR);
Max Krummenachereeb16b22016-11-30 19:43:09 +0100221 return CMD_RET_FAILURE;
222 }
223
224 for (i = 0; i < ARRAY_SIZE(pmic_otp_prog); i++) {
225 switch (pmic_otp_prog[i].cmd) {
226 case pmic_i2c:
227 val = (unsigned char) (pmic_otp_prog[i].value & 0xff);
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100228 if (dm_i2c_write(dev, pmic_otp_prog[i].reg, &val, 1)) {
Max Krummenachereeb16b22016-11-30 19:43:09 +0100229 printf("i2c write failed, reg 0x%2x, value 0x%2x\n",
230 pmic_otp_prog[i].reg, val);
231 return CMD_RET_FAILURE;
232 }
233 break;
234 case pmic_delay:
235 udelay(pmic_otp_prog[i].value * 1000);
236 break;
237 case pmic_vpgm:
238 gpio_direction_output(PMIC_PROG_VOLTAGE,
239 pmic_otp_prog[i].value);
240 break;
241 case pmic_pwr:
242 /* TODO */
243 break;
244 }
245 }
246 return CMD_RET_SUCCESS;
247}
248
Tom Rinic9515392018-01-03 09:19:17 -0500249static int do_pf0100_prog(cmd_tbl_t *cmdtp, int flag, int argc,
Max Krummenachereeb16b22016-11-30 19:43:09 +0100250 char * const argv[])
251{
252 int ret;
253 puts("Programming PMIC OTP...");
254 ret = pf0100_prog();
255 if (ret == CMD_RET_SUCCESS)
256 puts("done.\n");
257 else
258 puts("failed.\n");
259 return ret;
260}
261
262U_BOOT_CMD(
263 pf0100_otp_prog, 1, 0, do_pf0100_prog,
264 "Program the OTP fuses on the PMIC PF0100",
265 ""
266);
Marcel Ziswilerc882cfb2019-02-08 18:42:12 +0100267#endif /* CONFIG_SPL_BUILD */