1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2592f4aedSMax Krummenacher /*
3592f4aedSMax Krummenacher  * Copyright (C) 2014-2016, Toradex AG
4592f4aedSMax Krummenacher  */
5592f4aedSMax Krummenacher 
6592f4aedSMax Krummenacher /*
7592f4aedSMax Krummenacher  * Helpers for Freescale PMIC PF0100
8592f4aedSMax Krummenacher */
9592f4aedSMax Krummenacher 
10592f4aedSMax Krummenacher #include <common.h>
11592f4aedSMax Krummenacher #include <i2c.h>
1264c7abf0STom Rini #include <linux/compiler.h>
13592f4aedSMax Krummenacher #include <asm/arch/imx-regs.h>
14592f4aedSMax Krummenacher #include <asm/arch/iomux.h>
15592f4aedSMax Krummenacher #include <asm/arch/mx6-pins.h>
16592f4aedSMax Krummenacher #include <asm/gpio.h>
17552a848eSStefano Babic #include <asm/mach-imx/iomux-v3.h>
18592f4aedSMax Krummenacher 
19592f4aedSMax Krummenacher #include "pf0100_otp.inc"
20592f4aedSMax Krummenacher #include "pf0100.h"
21592f4aedSMax Krummenacher 
22592f4aedSMax Krummenacher /* define for PMIC register dump */
23592f4aedSMax Krummenacher /*#define DEBUG */
24592f4aedSMax Krummenacher 
25592f4aedSMax Krummenacher /* use Apalis GPIO1 to switch on VPGM, ON: 1 */
2664c7abf0STom Rini static __maybe_unused iomux_v3_cfg_t const pmic_prog_pads[] = {
27592f4aedSMax Krummenacher 	MX6_PAD_NANDF_D4__GPIO2_IO04 | MUX_PAD_CTRL(NO_PAD_CTRL),
28592f4aedSMax Krummenacher #	define PMIC_PROG_VOLTAGE IMX_GPIO_NR(2, 4)
29592f4aedSMax Krummenacher };
30592f4aedSMax Krummenacher 
pmic_init(void)31592f4aedSMax Krummenacher unsigned pmic_init(void)
32592f4aedSMax Krummenacher {
33592f4aedSMax Krummenacher 	unsigned programmed = 0;
34592f4aedSMax Krummenacher 	uchar bus = 1;
35592f4aedSMax Krummenacher 	uchar devid, revid, val;
36592f4aedSMax Krummenacher 
37592f4aedSMax Krummenacher 	puts("PMIC: ");
38592f4aedSMax Krummenacher 	if (!((0 == i2c_set_bus_num(bus)) &&
39592f4aedSMax Krummenacher 	      (0 == i2c_probe(PFUZE100_I2C_ADDR)))) {
40592f4aedSMax Krummenacher 		puts("i2c bus failed\n");
41592f4aedSMax Krummenacher 		return 0;
42592f4aedSMax Krummenacher 	}
43592f4aedSMax Krummenacher 	/* get device ident */
44592f4aedSMax Krummenacher 	if (i2c_read(PFUZE100_I2C_ADDR, PFUZE100_DEVICEID, 1, &devid, 1) < 0) {
45592f4aedSMax Krummenacher 		puts("i2c pmic devid read failed\n");
46592f4aedSMax Krummenacher 		return 0;
47592f4aedSMax Krummenacher 	}
48592f4aedSMax Krummenacher 	if (i2c_read(PFUZE100_I2C_ADDR, PFUZE100_REVID, 1, &revid, 1) < 0) {
49592f4aedSMax Krummenacher 		puts("i2c pmic revid read failed\n");
50592f4aedSMax Krummenacher 		return 0;
51592f4aedSMax Krummenacher 	}
52592f4aedSMax Krummenacher 	printf("device id: 0x%.2x, revision id: 0x%.2x\n", devid, revid);
53592f4aedSMax Krummenacher 
54592f4aedSMax Krummenacher #ifdef DEBUG
55592f4aedSMax Krummenacher 	{
56592f4aedSMax Krummenacher 		unsigned i, j;
57592f4aedSMax Krummenacher 
58592f4aedSMax Krummenacher 		for (i = 0; i < 16; i++)
59592f4aedSMax Krummenacher 			printf("\t%x", i);
60592f4aedSMax Krummenacher 		for (j = 0; j < 0x80; ) {
61592f4aedSMax Krummenacher 			printf("\n%2x", j);
62592f4aedSMax Krummenacher 			for (i = 0; i < 16; i++) {
63592f4aedSMax Krummenacher 				i2c_read(PFUZE100_I2C_ADDR, j+i, 1, &val, 1);
64592f4aedSMax Krummenacher 				printf("\t%2x", val);
65592f4aedSMax Krummenacher 			}
66592f4aedSMax Krummenacher 			j += 0x10;
67592f4aedSMax Krummenacher 		}
68592f4aedSMax Krummenacher 		printf("\nEXT Page 1");
69592f4aedSMax Krummenacher 
70592f4aedSMax Krummenacher 		val = PFUZE100_PAGE_REGISTER_PAGE1;
71592f4aedSMax Krummenacher 		if (i2c_write(PFUZE100_I2C_ADDR, PFUZE100_PAGE_REGISTER, 1,
72592f4aedSMax Krummenacher 			      &val, 1)) {
73592f4aedSMax Krummenacher 			puts("i2c write failed\n");
74592f4aedSMax Krummenacher 			return 0;
75592f4aedSMax Krummenacher 		}
76592f4aedSMax Krummenacher 
77592f4aedSMax Krummenacher 		for (j = 0x80; j < 0x100; ) {
78592f4aedSMax Krummenacher 			printf("\n%2x", j);
79592f4aedSMax Krummenacher 			for (i = 0; i < 16; i++) {
80592f4aedSMax Krummenacher 				i2c_read(PFUZE100_I2C_ADDR, j+i, 1, &val, 1);
81592f4aedSMax Krummenacher 				printf("\t%2x", val);
82592f4aedSMax Krummenacher 			}
83592f4aedSMax Krummenacher 			j += 0x10;
84592f4aedSMax Krummenacher 		}
85592f4aedSMax Krummenacher 		printf("\nEXT Page 2");
86592f4aedSMax Krummenacher 
87592f4aedSMax Krummenacher 		val = PFUZE100_PAGE_REGISTER_PAGE2;
88592f4aedSMax Krummenacher 		if (i2c_write(PFUZE100_I2C_ADDR, PFUZE100_PAGE_REGISTER, 1,
89592f4aedSMax Krummenacher 			      &val, 1)) {
90592f4aedSMax Krummenacher 			puts("i2c write failed\n");
91592f4aedSMax Krummenacher 			return 0;
92592f4aedSMax Krummenacher 		}
93592f4aedSMax Krummenacher 
94592f4aedSMax Krummenacher 		for (j = 0x80; j < 0x100; ) {
95592f4aedSMax Krummenacher 			printf("\n%2x", j);
96592f4aedSMax Krummenacher 			for (i = 0; i < 16; i++) {
97592f4aedSMax Krummenacher 				i2c_read(PFUZE100_I2C_ADDR, j+i, 1, &val, 1);
98592f4aedSMax Krummenacher 				printf("\t%2x", val);
99592f4aedSMax Krummenacher 			}
100592f4aedSMax Krummenacher 			j += 0x10;
101592f4aedSMax Krummenacher 		}
102592f4aedSMax Krummenacher 		printf("\n");
103592f4aedSMax Krummenacher 	}
104592f4aedSMax Krummenacher #endif
105592f4aedSMax Krummenacher 	/* get device programmed state */
106592f4aedSMax Krummenacher 	val = PFUZE100_PAGE_REGISTER_PAGE1;
107592f4aedSMax Krummenacher 	if (i2c_write(PFUZE100_I2C_ADDR, PFUZE100_PAGE_REGISTER, 1, &val, 1)) {
108592f4aedSMax Krummenacher 		puts("i2c write failed\n");
109592f4aedSMax Krummenacher 		return 0;
110592f4aedSMax Krummenacher 	}
111592f4aedSMax Krummenacher 	if (i2c_read(PFUZE100_I2C_ADDR, PFUZE100_FUSE_POR1, 1, &val, 1) < 0) {
112592f4aedSMax Krummenacher 		puts("i2c fuse_por read failed\n");
113592f4aedSMax Krummenacher 		return 0;
114592f4aedSMax Krummenacher 	}
115592f4aedSMax Krummenacher 	if (val & PFUZE100_FUSE_POR_M)
116592f4aedSMax Krummenacher 		programmed++;
117592f4aedSMax Krummenacher 
118592f4aedSMax Krummenacher 	if (i2c_read(PFUZE100_I2C_ADDR, PFUZE100_FUSE_POR2, 1, &val, 1) < 0) {
119592f4aedSMax Krummenacher 		puts("i2c fuse_por read failed\n");
120592f4aedSMax Krummenacher 		return programmed;
121592f4aedSMax Krummenacher 	}
122592f4aedSMax Krummenacher 	if (val & PFUZE100_FUSE_POR_M)
123592f4aedSMax Krummenacher 		programmed++;
124592f4aedSMax Krummenacher 
125592f4aedSMax Krummenacher 	if (i2c_read(PFUZE100_I2C_ADDR, PFUZE100_FUSE_POR3, 1, &val, 1) < 0) {
126592f4aedSMax Krummenacher 		puts("i2c fuse_por read failed\n");
127592f4aedSMax Krummenacher 		return programmed;
128592f4aedSMax Krummenacher 	}
129592f4aedSMax Krummenacher 	if (val & PFUZE100_FUSE_POR_M)
130592f4aedSMax Krummenacher 		programmed++;
131592f4aedSMax Krummenacher 
132592f4aedSMax Krummenacher 	switch (programmed) {
133592f4aedSMax Krummenacher 	case 0:
134592f4aedSMax Krummenacher 		printf("PMIC: not programmed\n");
135592f4aedSMax Krummenacher 		break;
136592f4aedSMax Krummenacher 	case 3:
137592f4aedSMax Krummenacher 		printf("PMIC: programmed\n");
138592f4aedSMax Krummenacher 		break;
139592f4aedSMax Krummenacher 	default:
140592f4aedSMax Krummenacher 		printf("PMIC: undefined programming state\n");
141592f4aedSMax Krummenacher 		break;
142592f4aedSMax Krummenacher 	}
143592f4aedSMax Krummenacher 
144592f4aedSMax Krummenacher 	/* The following is needed during production */
145592f4aedSMax Krummenacher 	if (programmed != 3) {
146592f4aedSMax Krummenacher 		/* set VGEN1 to 1.2V */
147592f4aedSMax Krummenacher 		val = PFUZE100_VGEN1_VAL;
148592f4aedSMax Krummenacher 		if (i2c_write(PFUZE100_I2C_ADDR, PFUZE100_VGEN1CTL, 1,
149592f4aedSMax Krummenacher 			      &val, 1)) {
150592f4aedSMax Krummenacher 			puts("i2c write failed\n");
151592f4aedSMax Krummenacher 			return programmed;
152592f4aedSMax Krummenacher 		}
153592f4aedSMax Krummenacher 
154592f4aedSMax Krummenacher 		/* set SWBST to 5.0V */
155592f4aedSMax Krummenacher 		val = PFUZE100_SWBST_VAL;
156592f4aedSMax Krummenacher 		if (i2c_write(PFUZE100_I2C_ADDR, PFUZE100_SWBSTCTL, 1,
157592f4aedSMax Krummenacher 			      &val, 1)) {
158592f4aedSMax Krummenacher 			puts("i2c write failed\n");
159592f4aedSMax Krummenacher 		}
160592f4aedSMax Krummenacher 	}
161592f4aedSMax Krummenacher 	return programmed;
162592f4aedSMax Krummenacher }
163592f4aedSMax Krummenacher 
16464c7abf0STom Rini #ifndef CONFIG_SPL_BUILD
pf0100_prog(void)16564c7abf0STom Rini static int pf0100_prog(void)
166592f4aedSMax Krummenacher {
167592f4aedSMax Krummenacher 	unsigned char bus = 1;
168592f4aedSMax Krummenacher 	unsigned char val;
169592f4aedSMax Krummenacher 	unsigned int i;
170592f4aedSMax Krummenacher 
171592f4aedSMax Krummenacher 	if (pmic_init() == 3) {
172592f4aedSMax Krummenacher 		puts("PMIC already programmed, exiting\n");
173592f4aedSMax Krummenacher 		return CMD_RET_FAILURE;
174592f4aedSMax Krummenacher 	}
175592f4aedSMax Krummenacher 	/* set up gpio to manipulate vprog, initially off */
176592f4aedSMax Krummenacher 	imx_iomux_v3_setup_multiple_pads(pmic_prog_pads,
177592f4aedSMax Krummenacher 					 ARRAY_SIZE(pmic_prog_pads));
178592f4aedSMax Krummenacher 	gpio_direction_output(PMIC_PROG_VOLTAGE, 0);
179592f4aedSMax Krummenacher 
180592f4aedSMax Krummenacher 	if (!((0 == i2c_set_bus_num(bus)) &&
181592f4aedSMax Krummenacher 	      (0 == i2c_probe(PFUZE100_I2C_ADDR)))) {
182592f4aedSMax Krummenacher 		puts("i2c bus failed\n");
183592f4aedSMax Krummenacher 		return CMD_RET_FAILURE;
184592f4aedSMax Krummenacher 	}
185592f4aedSMax Krummenacher 
186592f4aedSMax Krummenacher 	for (i = 0; i < ARRAY_SIZE(pmic_otp_prog); i++) {
187592f4aedSMax Krummenacher 		switch (pmic_otp_prog[i].cmd) {
188592f4aedSMax Krummenacher 		case pmic_i2c:
189592f4aedSMax Krummenacher 			val = (unsigned char) (pmic_otp_prog[i].value & 0xff);
190592f4aedSMax Krummenacher 			if (i2c_write(PFUZE100_I2C_ADDR, pmic_otp_prog[i].reg,
191592f4aedSMax Krummenacher 				      1, &val, 1)) {
192592f4aedSMax Krummenacher 				printf("i2c write failed, reg 0x%2x, value 0x%2x\n",
193592f4aedSMax Krummenacher 				       pmic_otp_prog[i].reg, val);
194592f4aedSMax Krummenacher 				return CMD_RET_FAILURE;
195592f4aedSMax Krummenacher 			}
196592f4aedSMax Krummenacher 			break;
197592f4aedSMax Krummenacher 		case pmic_delay:
198592f4aedSMax Krummenacher 			udelay(pmic_otp_prog[i].value * 1000);
199592f4aedSMax Krummenacher 			break;
200592f4aedSMax Krummenacher 		case pmic_vpgm:
201592f4aedSMax Krummenacher 			gpio_direction_output(PMIC_PROG_VOLTAGE,
202592f4aedSMax Krummenacher 					      pmic_otp_prog[i].value);
203592f4aedSMax Krummenacher 			break;
204592f4aedSMax Krummenacher 		case pmic_pwr:
205592f4aedSMax Krummenacher 			/* TODO */
206592f4aedSMax Krummenacher 			break;
207592f4aedSMax Krummenacher 		}
208592f4aedSMax Krummenacher 	}
209592f4aedSMax Krummenacher 	return CMD_RET_SUCCESS;
210592f4aedSMax Krummenacher }
211592f4aedSMax Krummenacher 
do_pf0100_prog(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])21264c7abf0STom Rini static int do_pf0100_prog(cmd_tbl_t *cmdtp, int flag, int argc,
213592f4aedSMax Krummenacher 		char * const argv[])
214592f4aedSMax Krummenacher {
215592f4aedSMax Krummenacher 	int ret;
216592f4aedSMax Krummenacher 	puts("Programming PMIC OTP...");
217592f4aedSMax Krummenacher 	ret = pf0100_prog();
218592f4aedSMax Krummenacher 	if (ret == CMD_RET_SUCCESS)
219592f4aedSMax Krummenacher 		puts("done.\n");
220592f4aedSMax Krummenacher 	else
221592f4aedSMax Krummenacher 		puts("failed.\n");
222592f4aedSMax Krummenacher 	return ret;
223592f4aedSMax Krummenacher }
224592f4aedSMax Krummenacher 
225592f4aedSMax Krummenacher U_BOOT_CMD(
226592f4aedSMax Krummenacher 	pf0100_otp_prog, 1, 0, do_pf0100_prog,
227592f4aedSMax Krummenacher 	"Program the OTP fuses on the PMIC PF0100",
228592f4aedSMax Krummenacher 	""
229592f4aedSMax Krummenacher );
23064c7abf0STom Rini #endif
231