xref: /rk3399_rockchip-uboot/drivers/misc/mxs_ocotp.c (revision 2bbcccf552cbbd076ba8055fc693fc207cd60482)
1*2bbcccf5SMarek Vasut /*
2*2bbcccf5SMarek Vasut  * Freescale i.MX28 OCOTP Driver
3*2bbcccf5SMarek Vasut  *
4*2bbcccf5SMarek Vasut  * Copyright (C) 2014 Marek Vasut <marex@denx.de>
5*2bbcccf5SMarek Vasut  *
6*2bbcccf5SMarek Vasut  * SPDX-License-Identifier:	GPL-2.0+
7*2bbcccf5SMarek Vasut  *
8*2bbcccf5SMarek Vasut  * Note: The i.MX23/i.MX28 OCOTP block is a predecessor to the OCOTP block
9*2bbcccf5SMarek Vasut  *       used in i.MX6 . While these blocks are very similar at the first
10*2bbcccf5SMarek Vasut  *       glance, by digging deeper, one will notice differences (like the
11*2bbcccf5SMarek Vasut  *       tight dependence on MXS power block, some completely new registers
12*2bbcccf5SMarek Vasut  *       etc.) which would make common driver an ifdef nightmare :-(
13*2bbcccf5SMarek Vasut  */
14*2bbcccf5SMarek Vasut 
15*2bbcccf5SMarek Vasut #include <common.h>
16*2bbcccf5SMarek Vasut #include <fuse.h>
17*2bbcccf5SMarek Vasut #include <asm/errno.h>
18*2bbcccf5SMarek Vasut #include <asm/io.h>
19*2bbcccf5SMarek Vasut #include <asm/arch/clock.h>
20*2bbcccf5SMarek Vasut #include <asm/arch/imx-regs.h>
21*2bbcccf5SMarek Vasut #include <asm/arch/sys_proto.h>
22*2bbcccf5SMarek Vasut 
23*2bbcccf5SMarek Vasut #define MXS_OCOTP_TIMEOUT	100000
24*2bbcccf5SMarek Vasut 
25*2bbcccf5SMarek Vasut static struct mxs_ocotp_regs *ocotp_regs =
26*2bbcccf5SMarek Vasut 	(struct mxs_ocotp_regs *)MXS_OCOTP_BASE;
27*2bbcccf5SMarek Vasut static struct mxs_power_regs *power_regs =
28*2bbcccf5SMarek Vasut 	(struct mxs_power_regs *)MXS_POWER_BASE;
29*2bbcccf5SMarek Vasut static struct mxs_clkctrl_regs *clkctrl_regs =
30*2bbcccf5SMarek Vasut 	(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
31*2bbcccf5SMarek Vasut 
32*2bbcccf5SMarek Vasut static int mxs_ocotp_wait_busy_clear(void)
33*2bbcccf5SMarek Vasut {
34*2bbcccf5SMarek Vasut 	uint32_t reg;
35*2bbcccf5SMarek Vasut 	int timeout = MXS_OCOTP_TIMEOUT;
36*2bbcccf5SMarek Vasut 
37*2bbcccf5SMarek Vasut 	while (--timeout) {
38*2bbcccf5SMarek Vasut 		reg = readl(&ocotp_regs->hw_ocotp_ctrl);
39*2bbcccf5SMarek Vasut 		if (!(reg & OCOTP_CTRL_BUSY))
40*2bbcccf5SMarek Vasut 			break;
41*2bbcccf5SMarek Vasut 		udelay(10);
42*2bbcccf5SMarek Vasut 	}
43*2bbcccf5SMarek Vasut 
44*2bbcccf5SMarek Vasut 	if (!timeout)
45*2bbcccf5SMarek Vasut 		return -EINVAL;
46*2bbcccf5SMarek Vasut 
47*2bbcccf5SMarek Vasut 	/* Wait a little as per FSL datasheet's 'write postamble' section. */
48*2bbcccf5SMarek Vasut 	udelay(10);
49*2bbcccf5SMarek Vasut 
50*2bbcccf5SMarek Vasut 	return 0;
51*2bbcccf5SMarek Vasut }
52*2bbcccf5SMarek Vasut 
53*2bbcccf5SMarek Vasut static void mxs_ocotp_clear_error(void)
54*2bbcccf5SMarek Vasut {
55*2bbcccf5SMarek Vasut 	writel(OCOTP_CTRL_ERROR, &ocotp_regs->hw_ocotp_ctrl_clr);
56*2bbcccf5SMarek Vasut }
57*2bbcccf5SMarek Vasut 
58*2bbcccf5SMarek Vasut static int mxs_ocotp_read_bank_open(bool open)
59*2bbcccf5SMarek Vasut {
60*2bbcccf5SMarek Vasut 	int ret = 0;
61*2bbcccf5SMarek Vasut 
62*2bbcccf5SMarek Vasut 	if (open) {
63*2bbcccf5SMarek Vasut 		writel(OCOTP_CTRL_RD_BANK_OPEN,
64*2bbcccf5SMarek Vasut 		       &ocotp_regs->hw_ocotp_ctrl_set);
65*2bbcccf5SMarek Vasut 
66*2bbcccf5SMarek Vasut 		/*
67*2bbcccf5SMarek Vasut 		 * Wait before polling the BUSY bit, since the BUSY bit might
68*2bbcccf5SMarek Vasut 		 * be asserted only after a few HCLK cycles and if we were to
69*2bbcccf5SMarek Vasut 		 * poll immediatelly, we could miss the busy bit.
70*2bbcccf5SMarek Vasut 		 */
71*2bbcccf5SMarek Vasut 		udelay(10);
72*2bbcccf5SMarek Vasut 		ret = mxs_ocotp_wait_busy_clear();
73*2bbcccf5SMarek Vasut 	} else {
74*2bbcccf5SMarek Vasut 		writel(OCOTP_CTRL_RD_BANK_OPEN,
75*2bbcccf5SMarek Vasut 		       &ocotp_regs->hw_ocotp_ctrl_clr);
76*2bbcccf5SMarek Vasut 	}
77*2bbcccf5SMarek Vasut 
78*2bbcccf5SMarek Vasut 	return ret;
79*2bbcccf5SMarek Vasut }
80*2bbcccf5SMarek Vasut 
81*2bbcccf5SMarek Vasut static void mxs_ocotp_scale_vddio(bool enter, uint32_t *val)
82*2bbcccf5SMarek Vasut {
83*2bbcccf5SMarek Vasut 	uint32_t scale_val;
84*2bbcccf5SMarek Vasut 
85*2bbcccf5SMarek Vasut 	if (enter) {
86*2bbcccf5SMarek Vasut 		/*
87*2bbcccf5SMarek Vasut 		 * Enter the fuse programming VDDIO voltage setup. We start
88*2bbcccf5SMarek Vasut 		 * scaling the voltage from it's current value down to 2.8V
89*2bbcccf5SMarek Vasut 		 * which is the one and only correct voltage for programming
90*2bbcccf5SMarek Vasut 		 * the OCOTP fuses (according to datasheet).
91*2bbcccf5SMarek Vasut 		 */
92*2bbcccf5SMarek Vasut 		scale_val = readl(&power_regs->hw_power_vddioctrl);
93*2bbcccf5SMarek Vasut 		scale_val &= POWER_VDDIOCTRL_TRG_MASK;
94*2bbcccf5SMarek Vasut 
95*2bbcccf5SMarek Vasut 		/* Return the original voltage. */
96*2bbcccf5SMarek Vasut 		*val = scale_val;
97*2bbcccf5SMarek Vasut 
98*2bbcccf5SMarek Vasut 		/*
99*2bbcccf5SMarek Vasut 		 * Start scaling VDDIO down to 0x2, which is 2.8V . Actually,
100*2bbcccf5SMarek Vasut 		 * the value 0x0 should be 2.8V, but that's not the case on
101*2bbcccf5SMarek Vasut 		 * most designs due to load etc., so we play safe. Undervolt
102*2bbcccf5SMarek Vasut 		 * can actually cause incorrect programming of the fuses and
103*2bbcccf5SMarek Vasut 		 * or reboots of the board.
104*2bbcccf5SMarek Vasut 		 */
105*2bbcccf5SMarek Vasut 		while (scale_val > 2) {
106*2bbcccf5SMarek Vasut 			clrsetbits_le32(&power_regs->hw_power_vddioctrl,
107*2bbcccf5SMarek Vasut 					POWER_VDDIOCTRL_TRG_MASK, --scale_val);
108*2bbcccf5SMarek Vasut 			udelay(500);
109*2bbcccf5SMarek Vasut 		}
110*2bbcccf5SMarek Vasut 	} else {
111*2bbcccf5SMarek Vasut 		/* Start scaling VDDIO up to original value . */
112*2bbcccf5SMarek Vasut 		for (scale_val = 2; scale_val <= *val; scale_val++) {
113*2bbcccf5SMarek Vasut 			clrsetbits_le32(&power_regs->hw_power_vddioctrl,
114*2bbcccf5SMarek Vasut 					POWER_VDDIOCTRL_TRG_MASK, scale_val);
115*2bbcccf5SMarek Vasut 			udelay(500);
116*2bbcccf5SMarek Vasut 		}
117*2bbcccf5SMarek Vasut 	}
118*2bbcccf5SMarek Vasut 
119*2bbcccf5SMarek Vasut 	mdelay(10);
120*2bbcccf5SMarek Vasut }
121*2bbcccf5SMarek Vasut 
122*2bbcccf5SMarek Vasut static int mxs_ocotp_wait_hclk_ready(void)
123*2bbcccf5SMarek Vasut {
124*2bbcccf5SMarek Vasut 	uint32_t reg, timeout = MXS_OCOTP_TIMEOUT;
125*2bbcccf5SMarek Vasut 
126*2bbcccf5SMarek Vasut 	while (--timeout) {
127*2bbcccf5SMarek Vasut 		reg = readl(&clkctrl_regs->hw_clkctrl_hbus);
128*2bbcccf5SMarek Vasut 		if (!(reg & CLKCTRL_HBUS_ASM_BUSY))
129*2bbcccf5SMarek Vasut 			break;
130*2bbcccf5SMarek Vasut 	}
131*2bbcccf5SMarek Vasut 
132*2bbcccf5SMarek Vasut 	if (!timeout)
133*2bbcccf5SMarek Vasut 		return -EINVAL;
134*2bbcccf5SMarek Vasut 
135*2bbcccf5SMarek Vasut 	return 0;
136*2bbcccf5SMarek Vasut }
137*2bbcccf5SMarek Vasut 
138*2bbcccf5SMarek Vasut static int mxs_ocotp_scale_hclk(bool enter, uint32_t *val)
139*2bbcccf5SMarek Vasut {
140*2bbcccf5SMarek Vasut 	uint32_t scale_val;
141*2bbcccf5SMarek Vasut 	int ret;
142*2bbcccf5SMarek Vasut 
143*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_wait_hclk_ready();
144*2bbcccf5SMarek Vasut 	if (ret)
145*2bbcccf5SMarek Vasut 		return ret;
146*2bbcccf5SMarek Vasut 
147*2bbcccf5SMarek Vasut 	/* Set CPU bypass */
148*2bbcccf5SMarek Vasut 	writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
149*2bbcccf5SMarek Vasut 	       &clkctrl_regs->hw_clkctrl_clkseq_set);
150*2bbcccf5SMarek Vasut 
151*2bbcccf5SMarek Vasut 	if (enter) {
152*2bbcccf5SMarek Vasut 		/* Return the original HCLK clock speed. */
153*2bbcccf5SMarek Vasut 		*val = readl(&clkctrl_regs->hw_clkctrl_hbus);
154*2bbcccf5SMarek Vasut 		*val &= CLKCTRL_HBUS_DIV_MASK;
155*2bbcccf5SMarek Vasut 
156*2bbcccf5SMarek Vasut 		/* Scale the HCLK to 454/19 = 23.9 MHz . */
157*2bbcccf5SMarek Vasut 		scale_val = (~19) << CLKCTRL_HBUS_DIV_OFFSET;
158*2bbcccf5SMarek Vasut 		scale_val &= CLKCTRL_HBUS_DIV_MASK;
159*2bbcccf5SMarek Vasut 	} else {
160*2bbcccf5SMarek Vasut 		/* Scale the HCLK back to original frequency. */
161*2bbcccf5SMarek Vasut 		scale_val = (~(*val)) << CLKCTRL_HBUS_DIV_OFFSET;
162*2bbcccf5SMarek Vasut 		scale_val &= CLKCTRL_HBUS_DIV_MASK;
163*2bbcccf5SMarek Vasut 	}
164*2bbcccf5SMarek Vasut 
165*2bbcccf5SMarek Vasut 	writel(CLKCTRL_HBUS_DIV_MASK,
166*2bbcccf5SMarek Vasut 	       &clkctrl_regs->hw_clkctrl_hbus_set);
167*2bbcccf5SMarek Vasut 	writel(scale_val,
168*2bbcccf5SMarek Vasut 	       &clkctrl_regs->hw_clkctrl_hbus_clr);
169*2bbcccf5SMarek Vasut 
170*2bbcccf5SMarek Vasut 	mdelay(10);
171*2bbcccf5SMarek Vasut 
172*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_wait_hclk_ready();
173*2bbcccf5SMarek Vasut 	if (ret)
174*2bbcccf5SMarek Vasut 		return ret;
175*2bbcccf5SMarek Vasut 
176*2bbcccf5SMarek Vasut 	/* Disable CPU bypass */
177*2bbcccf5SMarek Vasut 	writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
178*2bbcccf5SMarek Vasut 	       &clkctrl_regs->hw_clkctrl_clkseq_clr);
179*2bbcccf5SMarek Vasut 
180*2bbcccf5SMarek Vasut 	mdelay(10);
181*2bbcccf5SMarek Vasut 
182*2bbcccf5SMarek Vasut 	return 0;
183*2bbcccf5SMarek Vasut }
184*2bbcccf5SMarek Vasut 
185*2bbcccf5SMarek Vasut static int mxs_ocotp_write_fuse(uint32_t addr, uint32_t mask)
186*2bbcccf5SMarek Vasut {
187*2bbcccf5SMarek Vasut 	uint32_t hclk_val, vddio_val;
188*2bbcccf5SMarek Vasut 	int ret;
189*2bbcccf5SMarek Vasut 
190*2bbcccf5SMarek Vasut 	/* Make sure the banks are closed for reading. */
191*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_read_bank_open(0);
192*2bbcccf5SMarek Vasut 	if (ret) {
193*2bbcccf5SMarek Vasut 		puts("Failed closing banks for reading!\n");
194*2bbcccf5SMarek Vasut 		return ret;
195*2bbcccf5SMarek Vasut 	}
196*2bbcccf5SMarek Vasut 
197*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_scale_hclk(1, &hclk_val);
198*2bbcccf5SMarek Vasut 	if (ret) {
199*2bbcccf5SMarek Vasut 		puts("Failed scaling down the HCLK!\n");
200*2bbcccf5SMarek Vasut 		return ret;
201*2bbcccf5SMarek Vasut 	}
202*2bbcccf5SMarek Vasut 	mxs_ocotp_scale_vddio(1, &vddio_val);
203*2bbcccf5SMarek Vasut 
204*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_wait_busy_clear();
205*2bbcccf5SMarek Vasut 	if (ret) {
206*2bbcccf5SMarek Vasut 		puts("Failed waiting for ready state!\n");
207*2bbcccf5SMarek Vasut 		goto fail;
208*2bbcccf5SMarek Vasut 	}
209*2bbcccf5SMarek Vasut 
210*2bbcccf5SMarek Vasut 	/* Program the fuse address */
211*2bbcccf5SMarek Vasut 	writel(addr | OCOTP_CTRL_WR_UNLOCK_KEY, &ocotp_regs->hw_ocotp_ctrl);
212*2bbcccf5SMarek Vasut 
213*2bbcccf5SMarek Vasut 	/* Program the data. */
214*2bbcccf5SMarek Vasut 	writel(mask, &ocotp_regs->hw_ocotp_data);
215*2bbcccf5SMarek Vasut 
216*2bbcccf5SMarek Vasut 	udelay(10);
217*2bbcccf5SMarek Vasut 
218*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_wait_busy_clear();
219*2bbcccf5SMarek Vasut 	if (ret) {
220*2bbcccf5SMarek Vasut 		puts("Failed waiting for ready state!\n");
221*2bbcccf5SMarek Vasut 		goto fail;
222*2bbcccf5SMarek Vasut 	}
223*2bbcccf5SMarek Vasut 
224*2bbcccf5SMarek Vasut fail:
225*2bbcccf5SMarek Vasut 	mxs_ocotp_scale_vddio(0, &vddio_val);
226*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_scale_hclk(0, &hclk_val);
227*2bbcccf5SMarek Vasut 	if (ret) {
228*2bbcccf5SMarek Vasut 		puts("Failed scaling up the HCLK!\n");
229*2bbcccf5SMarek Vasut 		return ret;
230*2bbcccf5SMarek Vasut 	}
231*2bbcccf5SMarek Vasut 
232*2bbcccf5SMarek Vasut 	return ret;
233*2bbcccf5SMarek Vasut }
234*2bbcccf5SMarek Vasut 
235*2bbcccf5SMarek Vasut static int mxs_ocotp_read_fuse(uint32_t reg, uint32_t *val)
236*2bbcccf5SMarek Vasut {
237*2bbcccf5SMarek Vasut 	int ret;
238*2bbcccf5SMarek Vasut 
239*2bbcccf5SMarek Vasut 	/* Register offset from CUST0 */
240*2bbcccf5SMarek Vasut 	reg = ((uint32_t)&ocotp_regs->hw_ocotp_cust0) + (reg << 4);
241*2bbcccf5SMarek Vasut 
242*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_wait_busy_clear();
243*2bbcccf5SMarek Vasut 	if (ret) {
244*2bbcccf5SMarek Vasut 		puts("Failed waiting for ready state!\n");
245*2bbcccf5SMarek Vasut 		return ret;
246*2bbcccf5SMarek Vasut 	}
247*2bbcccf5SMarek Vasut 
248*2bbcccf5SMarek Vasut 	mxs_ocotp_clear_error();
249*2bbcccf5SMarek Vasut 
250*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_read_bank_open(1);
251*2bbcccf5SMarek Vasut 	if (ret) {
252*2bbcccf5SMarek Vasut 		puts("Failed opening banks for reading!\n");
253*2bbcccf5SMarek Vasut 		return ret;
254*2bbcccf5SMarek Vasut 	}
255*2bbcccf5SMarek Vasut 
256*2bbcccf5SMarek Vasut 	*val = readl(reg);
257*2bbcccf5SMarek Vasut 
258*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_read_bank_open(0);
259*2bbcccf5SMarek Vasut 	if (ret) {
260*2bbcccf5SMarek Vasut 		puts("Failed closing banks for reading!\n");
261*2bbcccf5SMarek Vasut 		return ret;
262*2bbcccf5SMarek Vasut 	}
263*2bbcccf5SMarek Vasut 
264*2bbcccf5SMarek Vasut 	return ret;
265*2bbcccf5SMarek Vasut }
266*2bbcccf5SMarek Vasut 
267*2bbcccf5SMarek Vasut static int mxs_ocotp_valid(u32 bank, u32 word)
268*2bbcccf5SMarek Vasut {
269*2bbcccf5SMarek Vasut 	if (bank > 4)
270*2bbcccf5SMarek Vasut 		return -EINVAL;
271*2bbcccf5SMarek Vasut 	if (word > 7)
272*2bbcccf5SMarek Vasut 		return -EINVAL;
273*2bbcccf5SMarek Vasut 	return 0;
274*2bbcccf5SMarek Vasut }
275*2bbcccf5SMarek Vasut 
276*2bbcccf5SMarek Vasut /*
277*2bbcccf5SMarek Vasut  * The 'fuse' command API
278*2bbcccf5SMarek Vasut  */
279*2bbcccf5SMarek Vasut int fuse_read(u32 bank, u32 word, u32 *val)
280*2bbcccf5SMarek Vasut {
281*2bbcccf5SMarek Vasut 	int ret;
282*2bbcccf5SMarek Vasut 
283*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_valid(bank, word);
284*2bbcccf5SMarek Vasut 	if (ret)
285*2bbcccf5SMarek Vasut 		return ret;
286*2bbcccf5SMarek Vasut 
287*2bbcccf5SMarek Vasut 	return mxs_ocotp_read_fuse((bank << 3) | word, val);
288*2bbcccf5SMarek Vasut }
289*2bbcccf5SMarek Vasut 
290*2bbcccf5SMarek Vasut int fuse_prog(u32 bank, u32 word, u32 val)
291*2bbcccf5SMarek Vasut {
292*2bbcccf5SMarek Vasut 	int ret;
293*2bbcccf5SMarek Vasut 
294*2bbcccf5SMarek Vasut 	ret = mxs_ocotp_valid(bank, word);
295*2bbcccf5SMarek Vasut 	if (ret)
296*2bbcccf5SMarek Vasut 		return ret;
297*2bbcccf5SMarek Vasut 
298*2bbcccf5SMarek Vasut 	return mxs_ocotp_write_fuse((bank << 3) | word, val);
299*2bbcccf5SMarek Vasut }
300*2bbcccf5SMarek Vasut 
301*2bbcccf5SMarek Vasut int fuse_sense(u32 bank, u32 word, u32 *val)
302*2bbcccf5SMarek Vasut {
303*2bbcccf5SMarek Vasut 	/* We do not support sensing :-( */
304*2bbcccf5SMarek Vasut 	return -EINVAL;
305*2bbcccf5SMarek Vasut }
306*2bbcccf5SMarek Vasut 
307*2bbcccf5SMarek Vasut int fuse_override(u32 bank, u32 word, u32 val)
308*2bbcccf5SMarek Vasut {
309*2bbcccf5SMarek Vasut 	/* We do not support overriding :-( */
310*2bbcccf5SMarek Vasut 	return -EINVAL;
311*2bbcccf5SMarek Vasut }
312