Commit 139a6f95be94f75e4038e0eacddbd1f0a365f601
1 parent
0b65071afa
Exists in
smarc-rel_imx_4.1.15_2.0.0_ga
MLK-12852 ocotp: mxc: mx6ull: fix GP3/GP4 prog
Bank 7 and Bank 8 only supports 4 words each. 'bank << 3 | word' is not correct when program bank 8, since ocotp controller actully use word index. For example: fuse prog 8 3 1; The word index is (8 << 3 | 3) --> 67. But actully it should be (7 << 3 | 7) ---> 63. So fix it. Signed-off-by: Peng Fan <peng.fan@nxp.com>
Showing 1 changed file with 7 additions and 1 deletions Inline Diff
drivers/misc/mxc_ocotp.c
1 | /* | 1 | /* |
2 | * (C) Copyright 2013 ADVANSEE | 2 | * (C) Copyright 2013 ADVANSEE |
3 | * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> | 3 | * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> |
4 | * | 4 | * |
5 | * Based on Dirk Behme's | 5 | * Based on Dirk Behme's |
6 | * https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c, | 6 | * https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c, |
7 | * which is based on Freescale's | 7 | * which is based on Freescale's |
8 | * http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tree/drivers/misc/imx_otp.c?h=imx_v2009.08_1.1.0&id=9aa74e6, | 8 | * http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tree/drivers/misc/imx_otp.c?h=imx_v2009.08_1.1.0&id=9aa74e6, |
9 | * which is: | 9 | * which is: |
10 | * Copyright (C) 2011-2016 Freescale Semiconductor, Inc. | 10 | * Copyright (C) 2011-2016 Freescale Semiconductor, Inc. |
11 | * | 11 | * |
12 | * SPDX-License-Identifier: GPL-2.0+ | 12 | * SPDX-License-Identifier: GPL-2.0+ |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <common.h> | 15 | #include <common.h> |
16 | #include <fuse.h> | 16 | #include <fuse.h> |
17 | #include <asm/errno.h> | 17 | #include <asm/errno.h> |
18 | #include <asm/io.h> | 18 | #include <asm/io.h> |
19 | #include <asm/arch/clock.h> | 19 | #include <asm/arch/clock.h> |
20 | #include <asm/arch/imx-regs.h> | 20 | #include <asm/arch/imx-regs.h> |
21 | #include <asm/imx-common/sys_proto.h> | 21 | #include <asm/imx-common/sys_proto.h> |
22 | 22 | ||
23 | #define BO_CTRL_WR_UNLOCK 16 | 23 | #define BO_CTRL_WR_UNLOCK 16 |
24 | #define BM_CTRL_WR_UNLOCK 0xffff0000 | 24 | #define BM_CTRL_WR_UNLOCK 0xffff0000 |
25 | #define BV_CTRL_WR_UNLOCK_KEY 0x3e77 | 25 | #define BV_CTRL_WR_UNLOCK_KEY 0x3e77 |
26 | #define BM_CTRL_ERROR 0x00000200 | 26 | #define BM_CTRL_ERROR 0x00000200 |
27 | #define BM_CTRL_BUSY 0x00000100 | 27 | #define BM_CTRL_BUSY 0x00000100 |
28 | #define BO_CTRL_ADDR 0 | 28 | #define BO_CTRL_ADDR 0 |
29 | #ifdef CONFIG_MX7 | 29 | #ifdef CONFIG_MX7 |
30 | #define BM_CTRL_ADDR 0x0000000f | 30 | #define BM_CTRL_ADDR 0x0000000f |
31 | #define BM_CTRL_RELOAD 0x00000400 | 31 | #define BM_CTRL_RELOAD 0x00000400 |
32 | #else | 32 | #else |
33 | #define BM_CTRL_ADDR 0x0000007f | 33 | #define BM_CTRL_ADDR 0x0000007f |
34 | #endif | 34 | #endif |
35 | 35 | ||
36 | #ifdef CONFIG_MX7 | 36 | #ifdef CONFIG_MX7 |
37 | #define BO_TIMING_FSOURCE 12 | 37 | #define BO_TIMING_FSOURCE 12 |
38 | #define BM_TIMING_FSOURCE 0x0007f000 | 38 | #define BM_TIMING_FSOURCE 0x0007f000 |
39 | #define BV_TIMING_FSOURCE_NS 1001 | 39 | #define BV_TIMING_FSOURCE_NS 1001 |
40 | #define BO_TIMING_PROG 0 | 40 | #define BO_TIMING_PROG 0 |
41 | #define BM_TIMING_PROG 0x00000fff | 41 | #define BM_TIMING_PROG 0x00000fff |
42 | #define BV_TIMING_PROG_US 10 | 42 | #define BV_TIMING_PROG_US 10 |
43 | #else | 43 | #else |
44 | #define BO_TIMING_STROBE_READ 16 | 44 | #define BO_TIMING_STROBE_READ 16 |
45 | #define BM_TIMING_STROBE_READ 0x003f0000 | 45 | #define BM_TIMING_STROBE_READ 0x003f0000 |
46 | #define BV_TIMING_STROBE_READ_NS 37 | 46 | #define BV_TIMING_STROBE_READ_NS 37 |
47 | #define BO_TIMING_RELAX 12 | 47 | #define BO_TIMING_RELAX 12 |
48 | #define BM_TIMING_RELAX 0x0000f000 | 48 | #define BM_TIMING_RELAX 0x0000f000 |
49 | #define BV_TIMING_RELAX_NS 17 | 49 | #define BV_TIMING_RELAX_NS 17 |
50 | #define BO_TIMING_STROBE_PROG 0 | 50 | #define BO_TIMING_STROBE_PROG 0 |
51 | #define BM_TIMING_STROBE_PROG 0x00000fff | 51 | #define BM_TIMING_STROBE_PROG 0x00000fff |
52 | #define BV_TIMING_STROBE_PROG_US 10 | 52 | #define BV_TIMING_STROBE_PROG_US 10 |
53 | #endif | 53 | #endif |
54 | 54 | ||
55 | #define BM_READ_CTRL_READ_FUSE 0x00000001 | 55 | #define BM_READ_CTRL_READ_FUSE 0x00000001 |
56 | 56 | ||
57 | #define BF(value, field) (((value) << BO_##field) & BM_##field) | 57 | #define BF(value, field) (((value) << BO_##field) & BM_##field) |
58 | 58 | ||
59 | #define WRITE_POSTAMBLE_US 2 | 59 | #define WRITE_POSTAMBLE_US 2 |
60 | 60 | ||
61 | #if defined(CONFIG_MX6) || defined(CONFIG_VF610) | 61 | #if defined(CONFIG_MX6) || defined(CONFIG_VF610) |
62 | #define FUSE_BANK_SIZE 0x80 | 62 | #define FUSE_BANK_SIZE 0x80 |
63 | #ifdef CONFIG_MX6SL | 63 | #ifdef CONFIG_MX6SL |
64 | #define FUSE_BANKS 8 | 64 | #define FUSE_BANKS 8 |
65 | #elif defined CONFIG_MX6ULL | 65 | #elif defined CONFIG_MX6ULL |
66 | #define FUSE_BANKS 9 | 66 | #define FUSE_BANKS 9 |
67 | #else | 67 | #else |
68 | #define FUSE_BANKS 16 | 68 | #define FUSE_BANKS 16 |
69 | #endif | 69 | #endif |
70 | #elif defined CONFIG_MX7 | 70 | #elif defined CONFIG_MX7 |
71 | #define FUSE_BANK_SIZE 0x40 | 71 | #define FUSE_BANK_SIZE 0x40 |
72 | #define FUSE_BANKS 16 | 72 | #define FUSE_BANKS 16 |
73 | #else | 73 | #else |
74 | #error "Unsupported architecture\n" | 74 | #error "Unsupported architecture\n" |
75 | #endif | 75 | #endif |
76 | 76 | ||
77 | #if defined(CONFIG_MX6) | 77 | #if defined(CONFIG_MX6) |
78 | 78 | ||
79 | /* | 79 | /* |
80 | * There is a hole in shadow registers address map of size 0x100 | 80 | * There is a hole in shadow registers address map of size 0x100 |
81 | * between bank 5 and bank 6 on iMX6QP, iMX6DQ, iMX6SDL, iMX6SX, iMX6UL and iMX6ULL. | 81 | * between bank 5 and bank 6 on iMX6QP, iMX6DQ, iMX6SDL, iMX6SX, iMX6UL and iMX6ULL. |
82 | * Bank 5 ends at 0x6F0 and Bank 6 starts at 0x800. When reading the fuses, | 82 | * Bank 5 ends at 0x6F0 and Bank 6 starts at 0x800. When reading the fuses, |
83 | * we should account for this hole in address space. | 83 | * we should account for this hole in address space. |
84 | * | 84 | * |
85 | * Similar hole exists between bank 14 and bank 15 of size | 85 | * Similar hole exists between bank 14 and bank 15 of size |
86 | * 0x80 on iMX6QP, iMX6DQ, iMX6SDL and iMX6SX. | 86 | * 0x80 on iMX6QP, iMX6DQ, iMX6SDL and iMX6SX. |
87 | * Note: iMX6SL has only 0-7 banks and there is no hole. | 87 | * Note: iMX6SL has only 0-7 banks and there is no hole. |
88 | * Note: iMX6UL doesn't have this one. | 88 | * Note: iMX6UL doesn't have this one. |
89 | * | 89 | * |
90 | * This function is to covert user input to physical bank index. | 90 | * This function is to covert user input to physical bank index. |
91 | * Only needed when read fuse, because we use register offset, so | 91 | * Only needed when read fuse, because we use register offset, so |
92 | * need to calculate real register offset. | 92 | * need to calculate real register offset. |
93 | * When write, no need to consider hole, always use the bank/word | 93 | * When write, no need to consider hole, always use the bank/word |
94 | * index from fuse map. | 94 | * index from fuse map. |
95 | */ | 95 | */ |
96 | u32 fuse_bank_physical(int index) | 96 | u32 fuse_bank_physical(int index) |
97 | { | 97 | { |
98 | u32 phy_index; | 98 | u32 phy_index; |
99 | 99 | ||
100 | if (is_cpu_type(MXC_CPU_MX6SL)) { | 100 | if (is_cpu_type(MXC_CPU_MX6SL)) { |
101 | phy_index = index; | 101 | phy_index = index; |
102 | } else if (is_cpu_type(MXC_CPU_MX6UL) || is_cpu_type(MXC_CPU_MX6ULL)) { | 102 | } else if (is_cpu_type(MXC_CPU_MX6UL) || is_cpu_type(MXC_CPU_MX6ULL)) { |
103 | if (is_cpu_type(MXC_CPU_MX6ULL) && index == 8) | 103 | if (is_cpu_type(MXC_CPU_MX6ULL) && index == 8) |
104 | index = 7; | 104 | index = 7; |
105 | 105 | ||
106 | if (index >= 6) | 106 | if (index >= 6) |
107 | phy_index = fuse_bank_physical(5) + (index - 6) + 3; | 107 | phy_index = fuse_bank_physical(5) + (index - 6) + 3; |
108 | else | 108 | else |
109 | phy_index = index; | 109 | phy_index = index; |
110 | } else { | 110 | } else { |
111 | if (index >= 15) | 111 | if (index >= 15) |
112 | phy_index = fuse_bank_physical(14) + (index - 15) + 2; | 112 | phy_index = fuse_bank_physical(14) + (index - 15) + 2; |
113 | else if (index >= 6) | 113 | else if (index >= 6) |
114 | phy_index = fuse_bank_physical(5) + (index - 6) + 3; | 114 | phy_index = fuse_bank_physical(5) + (index - 6) + 3; |
115 | else | 115 | else |
116 | phy_index = index; | 116 | phy_index = index; |
117 | } | 117 | } |
118 | return phy_index; | 118 | return phy_index; |
119 | } | 119 | } |
120 | 120 | ||
121 | u32 fuse_word_physical(u32 bank, u32 word_index) | 121 | u32 fuse_word_physical(u32 bank, u32 word_index) |
122 | { | 122 | { |
123 | if (is_cpu_type(MXC_CPU_MX6ULL)) { | 123 | if (is_cpu_type(MXC_CPU_MX6ULL)) { |
124 | if (bank == 8) | 124 | if (bank == 8) |
125 | word_index = word_index + 4; | 125 | word_index = word_index + 4; |
126 | } | 126 | } |
127 | 127 | ||
128 | return word_index; | 128 | return word_index; |
129 | } | 129 | } |
130 | #else | 130 | #else |
131 | u32 fuse_bank_physical(int index) | 131 | u32 fuse_bank_physical(int index) |
132 | { | 132 | { |
133 | return index; | 133 | return index; |
134 | } | 134 | } |
135 | 135 | ||
136 | u32 fuse_word_physical(u32 bank, u32 word_index) | 136 | u32 fuse_word_physical(u32 bank, u32 word_index) |
137 | { | 137 | { |
138 | return word_index; | 138 | return word_index; |
139 | } | 139 | } |
140 | 140 | ||
141 | #endif | 141 | #endif |
142 | 142 | ||
143 | static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us) | 143 | static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us) |
144 | { | 144 | { |
145 | while (readl(®s->ctrl) & BM_CTRL_BUSY) | 145 | while (readl(®s->ctrl) & BM_CTRL_BUSY) |
146 | udelay(delay_us); | 146 | udelay(delay_us); |
147 | } | 147 | } |
148 | 148 | ||
149 | static void clear_error(struct ocotp_regs *regs) | 149 | static void clear_error(struct ocotp_regs *regs) |
150 | { | 150 | { |
151 | writel(BM_CTRL_ERROR, ®s->ctrl_clr); | 151 | writel(BM_CTRL_ERROR, ®s->ctrl_clr); |
152 | } | 152 | } |
153 | 153 | ||
154 | static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word, | 154 | static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word, |
155 | int assert, const char *caller) | 155 | int assert, const char *caller) |
156 | { | 156 | { |
157 | *regs = (struct ocotp_regs *)OCOTP_BASE_ADDR; | 157 | *regs = (struct ocotp_regs *)OCOTP_BASE_ADDR; |
158 | 158 | ||
159 | if (bank >= FUSE_BANKS || | 159 | if (bank >= FUSE_BANKS || |
160 | word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 || | 160 | word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 || |
161 | !assert) { | 161 | !assert) { |
162 | printf("mxc_ocotp %s(): Invalid argument\n", caller); | 162 | printf("mxc_ocotp %s(): Invalid argument\n", caller); |
163 | return -EINVAL; | 163 | return -EINVAL; |
164 | } | 164 | } |
165 | 165 | ||
166 | if (is_cpu_type(MXC_CPU_MX6ULL)) { | 166 | if (is_cpu_type(MXC_CPU_MX6ULL)) { |
167 | if ((bank == 7 || bank == 8) && | 167 | if ((bank == 7 || bank == 8) && |
168 | word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 3) { | 168 | word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 3) { |
169 | printf("mxc_ocotp %s(): Invalid argument on 6ULL\n", caller); | 169 | printf("mxc_ocotp %s(): Invalid argument on 6ULL\n", caller); |
170 | return -EINVAL; | 170 | return -EINVAL; |
171 | } | 171 | } |
172 | } | 172 | } |
173 | 173 | ||
174 | enable_ocotp_clk(1); | 174 | enable_ocotp_clk(1); |
175 | 175 | ||
176 | wait_busy(*regs, 1); | 176 | wait_busy(*regs, 1); |
177 | clear_error(*regs); | 177 | clear_error(*regs); |
178 | 178 | ||
179 | return 0; | 179 | return 0; |
180 | } | 180 | } |
181 | 181 | ||
182 | static int finish_access(struct ocotp_regs *regs, const char *caller) | 182 | static int finish_access(struct ocotp_regs *regs, const char *caller) |
183 | { | 183 | { |
184 | u32 err; | 184 | u32 err; |
185 | 185 | ||
186 | err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); | 186 | err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); |
187 | clear_error(regs); | 187 | clear_error(regs); |
188 | 188 | ||
189 | if (err) { | 189 | if (err) { |
190 | printf("mxc_ocotp %s(): Access protect error\n", caller); | 190 | printf("mxc_ocotp %s(): Access protect error\n", caller); |
191 | return -EIO; | 191 | return -EIO; |
192 | } | 192 | } |
193 | 193 | ||
194 | return 0; | 194 | return 0; |
195 | } | 195 | } |
196 | 196 | ||
197 | static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val, | 197 | static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val, |
198 | const char *caller) | 198 | const char *caller) |
199 | { | 199 | { |
200 | return prepare_access(regs, bank, word, val != NULL, caller); | 200 | return prepare_access(regs, bank, word, val != NULL, caller); |
201 | } | 201 | } |
202 | 202 | ||
203 | int fuse_read(u32 bank, u32 word, u32 *val) | 203 | int fuse_read(u32 bank, u32 word, u32 *val) |
204 | { | 204 | { |
205 | struct ocotp_regs *regs; | 205 | struct ocotp_regs *regs; |
206 | int ret; | 206 | int ret; |
207 | u32 phy_bank; | 207 | u32 phy_bank; |
208 | u32 phy_word; | 208 | u32 phy_word; |
209 | 209 | ||
210 | ret = prepare_read(®s, bank, word, val, __func__); | 210 | ret = prepare_read(®s, bank, word, val, __func__); |
211 | if (ret) | 211 | if (ret) |
212 | return ret; | 212 | return ret; |
213 | 213 | ||
214 | phy_bank = fuse_bank_physical(bank); | 214 | phy_bank = fuse_bank_physical(bank); |
215 | phy_word = fuse_word_physical(bank, word); | 215 | phy_word = fuse_word_physical(bank, word); |
216 | 216 | ||
217 | *val = readl(®s->bank[phy_bank].fuse_regs[phy_word << 2]); | 217 | *val = readl(®s->bank[phy_bank].fuse_regs[phy_word << 2]); |
218 | 218 | ||
219 | return finish_access(regs, __func__); | 219 | return finish_access(regs, __func__); |
220 | } | 220 | } |
221 | 221 | ||
222 | #ifdef CONFIG_MX7 | 222 | #ifdef CONFIG_MX7 |
223 | static void set_timing(struct ocotp_regs *regs) | 223 | static void set_timing(struct ocotp_regs *regs) |
224 | { | 224 | { |
225 | u32 ipg_clk; | 225 | u32 ipg_clk; |
226 | u32 fsource, prog; | 226 | u32 fsource, prog; |
227 | u32 timing; | 227 | u32 timing; |
228 | 228 | ||
229 | ipg_clk = mxc_get_clock(MXC_IPG_CLK); | 229 | ipg_clk = mxc_get_clock(MXC_IPG_CLK); |
230 | 230 | ||
231 | fsource = DIV_ROUND_UP((ipg_clk / 1000) * BV_TIMING_FSOURCE_NS, | 231 | fsource = DIV_ROUND_UP((ipg_clk / 1000) * BV_TIMING_FSOURCE_NS, |
232 | + 1000000) + 1; | 232 | + 1000000) + 1; |
233 | prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_PROG_US, 1000000) + 1; | 233 | prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_PROG_US, 1000000) + 1; |
234 | 234 | ||
235 | timing = BF(fsource, TIMING_FSOURCE) | BF(prog, TIMING_PROG); | 235 | timing = BF(fsource, TIMING_FSOURCE) | BF(prog, TIMING_PROG); |
236 | 236 | ||
237 | clrsetbits_le32(®s->timing, BM_TIMING_FSOURCE | BM_TIMING_PROG, | 237 | clrsetbits_le32(®s->timing, BM_TIMING_FSOURCE | BM_TIMING_PROG, |
238 | timing); | 238 | timing); |
239 | } | 239 | } |
240 | #else | 240 | #else |
241 | static void set_timing(struct ocotp_regs *regs) | 241 | static void set_timing(struct ocotp_regs *regs) |
242 | { | 242 | { |
243 | u32 ipg_clk; | 243 | u32 ipg_clk; |
244 | u32 relax, strobe_read, strobe_prog; | 244 | u32 relax, strobe_read, strobe_prog; |
245 | u32 timing; | 245 | u32 timing; |
246 | 246 | ||
247 | ipg_clk = mxc_get_clock(MXC_IPG_CLK); | 247 | ipg_clk = mxc_get_clock(MXC_IPG_CLK); |
248 | 248 | ||
249 | relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1; | 249 | relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1; |
250 | strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS, | 250 | strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS, |
251 | 1000000000) + 2 * (relax + 1) - 1; | 251 | 1000000000) + 2 * (relax + 1) - 1; |
252 | strobe_prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_STROBE_PROG_US, | 252 | strobe_prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_STROBE_PROG_US, |
253 | 1000000) + 2 * (relax + 1) - 1; | 253 | 1000000) + 2 * (relax + 1) - 1; |
254 | 254 | ||
255 | timing = BF(strobe_read, TIMING_STROBE_READ) | | 255 | timing = BF(strobe_read, TIMING_STROBE_READ) | |
256 | BF(relax, TIMING_RELAX) | | 256 | BF(relax, TIMING_RELAX) | |
257 | BF(strobe_prog, TIMING_STROBE_PROG); | 257 | BF(strobe_prog, TIMING_STROBE_PROG); |
258 | 258 | ||
259 | clrsetbits_le32(®s->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX | | 259 | clrsetbits_le32(®s->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX | |
260 | BM_TIMING_STROBE_PROG, timing); | 260 | BM_TIMING_STROBE_PROG, timing); |
261 | } | 261 | } |
262 | #endif | 262 | #endif |
263 | 263 | ||
264 | static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word, | 264 | static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word, |
265 | int write) | 265 | int write) |
266 | { | 266 | { |
267 | u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0; | 267 | u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0; |
268 | #ifdef CONFIG_MX7 | 268 | #ifdef CONFIG_MX7 |
269 | u32 addr = bank; | 269 | u32 addr = bank; |
270 | #else | 270 | #else |
271 | u32 addr = bank << 3 | word; | 271 | u32 addr; |
272 | /* Bank 7 and Bank 8 only supports 4 words each */ | ||
273 | if ((is_cpu_type(MXC_CPU_MX6ULL)) && (bank > 7)) { | ||
274 | bank = bank - 1; | ||
275 | word += 4; | ||
276 | } | ||
277 | addr = bank << 3 | word; | ||
272 | #endif | 278 | #endif |
273 | 279 | ||
274 | set_timing(regs); | 280 | set_timing(regs); |
275 | clrsetbits_le32(®s->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR, | 281 | clrsetbits_le32(®s->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR, |
276 | BF(wr_unlock, CTRL_WR_UNLOCK) | | 282 | BF(wr_unlock, CTRL_WR_UNLOCK) | |
277 | BF(addr, CTRL_ADDR)); | 283 | BF(addr, CTRL_ADDR)); |
278 | } | 284 | } |
279 | 285 | ||
280 | int fuse_sense(u32 bank, u32 word, u32 *val) | 286 | int fuse_sense(u32 bank, u32 word, u32 *val) |
281 | { | 287 | { |
282 | struct ocotp_regs *regs; | 288 | struct ocotp_regs *regs; |
283 | int ret; | 289 | int ret; |
284 | 290 | ||
285 | ret = prepare_read(®s, bank, word, val, __func__); | 291 | ret = prepare_read(®s, bank, word, val, __func__); |
286 | if (ret) | 292 | if (ret) |
287 | return ret; | 293 | return ret; |
288 | 294 | ||
289 | setup_direct_access(regs, bank, word, false); | 295 | setup_direct_access(regs, bank, word, false); |
290 | writel(BM_READ_CTRL_READ_FUSE, ®s->read_ctrl); | 296 | writel(BM_READ_CTRL_READ_FUSE, ®s->read_ctrl); |
291 | wait_busy(regs, 1); | 297 | wait_busy(regs, 1); |
292 | #ifdef CONFIG_MX7 | 298 | #ifdef CONFIG_MX7 |
293 | *val = readl((®s->read_fuse_data0) + (word << 2)); | 299 | *val = readl((®s->read_fuse_data0) + (word << 2)); |
294 | #else | 300 | #else |
295 | *val = readl(®s->read_fuse_data); | 301 | *val = readl(®s->read_fuse_data); |
296 | #endif | 302 | #endif |
297 | 303 | ||
298 | return finish_access(regs, __func__); | 304 | return finish_access(regs, __func__); |
299 | } | 305 | } |
300 | 306 | ||
301 | static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word, | 307 | static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word, |
302 | const char *caller) | 308 | const char *caller) |
303 | { | 309 | { |
304 | return prepare_access(regs, bank, word, true, caller); | 310 | return prepare_access(regs, bank, word, true, caller); |
305 | } | 311 | } |
306 | 312 | ||
307 | int fuse_prog(u32 bank, u32 word, u32 val) | 313 | int fuse_prog(u32 bank, u32 word, u32 val) |
308 | { | 314 | { |
309 | struct ocotp_regs *regs; | 315 | struct ocotp_regs *regs; |
310 | int ret; | 316 | int ret; |
311 | 317 | ||
312 | ret = prepare_write(®s, bank, word, __func__); | 318 | ret = prepare_write(®s, bank, word, __func__); |
313 | if (ret) | 319 | if (ret) |
314 | return ret; | 320 | return ret; |
315 | 321 | ||
316 | setup_direct_access(regs, bank, word, true); | 322 | setup_direct_access(regs, bank, word, true); |
317 | #ifdef CONFIG_MX7 | 323 | #ifdef CONFIG_MX7 |
318 | switch (word) { | 324 | switch (word) { |
319 | case 0: | 325 | case 0: |
320 | writel(0, ®s->data1); | 326 | writel(0, ®s->data1); |
321 | writel(0, ®s->data2); | 327 | writel(0, ®s->data2); |
322 | writel(0, ®s->data3); | 328 | writel(0, ®s->data3); |
323 | writel(val, ®s->data0); | 329 | writel(val, ®s->data0); |
324 | break; | 330 | break; |
325 | case 1: | 331 | case 1: |
326 | writel(val, ®s->data1); | 332 | writel(val, ®s->data1); |
327 | writel(0, ®s->data2); | 333 | writel(0, ®s->data2); |
328 | writel(0, ®s->data3); | 334 | writel(0, ®s->data3); |
329 | writel(0, ®s->data0); | 335 | writel(0, ®s->data0); |
330 | break; | 336 | break; |
331 | case 2: | 337 | case 2: |
332 | writel(0, ®s->data1); | 338 | writel(0, ®s->data1); |
333 | writel(val, ®s->data2); | 339 | writel(val, ®s->data2); |
334 | writel(0, ®s->data3); | 340 | writel(0, ®s->data3); |
335 | writel(0, ®s->data0); | 341 | writel(0, ®s->data0); |
336 | break; | 342 | break; |
337 | case 3: | 343 | case 3: |
338 | writel(0, ®s->data1); | 344 | writel(0, ®s->data1); |
339 | writel(0, ®s->data2); | 345 | writel(0, ®s->data2); |
340 | writel(val, ®s->data3); | 346 | writel(val, ®s->data3); |
341 | writel(0, ®s->data0); | 347 | writel(0, ®s->data0); |
342 | break; | 348 | break; |
343 | } | 349 | } |
344 | wait_busy(regs, BV_TIMING_PROG_US); | 350 | wait_busy(regs, BV_TIMING_PROG_US); |
345 | #else | 351 | #else |
346 | writel(val, ®s->data); | 352 | writel(val, ®s->data); |
347 | wait_busy(regs, BV_TIMING_STROBE_PROG_US); | 353 | wait_busy(regs, BV_TIMING_STROBE_PROG_US); |
348 | #endif | 354 | #endif |
349 | udelay(WRITE_POSTAMBLE_US); | 355 | udelay(WRITE_POSTAMBLE_US); |
350 | 356 | ||
351 | return finish_access(regs, __func__); | 357 | return finish_access(regs, __func__); |
352 | } | 358 | } |
353 | 359 | ||
354 | int fuse_override(u32 bank, u32 word, u32 val) | 360 | int fuse_override(u32 bank, u32 word, u32 val) |
355 | { | 361 | { |
356 | struct ocotp_regs *regs; | 362 | struct ocotp_regs *regs; |
357 | int ret; | 363 | int ret; |
358 | u32 phy_bank; | 364 | u32 phy_bank; |
359 | u32 phy_word; | 365 | u32 phy_word; |
360 | 366 | ||
361 | ret = prepare_write(®s, bank, word, __func__); | 367 | ret = prepare_write(®s, bank, word, __func__); |
362 | if (ret) | 368 | if (ret) |
363 | return ret; | 369 | return ret; |
364 | 370 | ||
365 | phy_bank = fuse_bank_physical(bank); | 371 | phy_bank = fuse_bank_physical(bank); |
366 | phy_word = fuse_word_physical(bank, word); | 372 | phy_word = fuse_word_physical(bank, word); |
367 | 373 | ||
368 | writel(val, ®s->bank[phy_bank].fuse_regs[phy_word << 2]); | 374 | writel(val, ®s->bank[phy_bank].fuse_regs[phy_word << 2]); |
369 | 375 | ||
370 | return finish_access(regs, __func__); | 376 | return finish_access(regs, __func__); |
371 | } | 377 | } |
372 | 378 |