1112fd2ecSBenoît Thébaudeau /* 2112fd2ecSBenoît Thébaudeau * (C) Copyright 2013 ADVANSEE 3112fd2ecSBenoît Thébaudeau * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> 4112fd2ecSBenoît Thébaudeau * 5112fd2ecSBenoît Thébaudeau * Based on Dirk Behme's 6112fd2ecSBenoît Thébaudeau * https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c, 7112fd2ecSBenoît Thébaudeau * which is based on Freescale's 8112fd2ecSBenoît Thébaudeau * 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, 9112fd2ecSBenoît Thébaudeau * which is: 10112fd2ecSBenoît Thébaudeau * Copyright (C) 2011 Freescale Semiconductor, Inc. 11112fd2ecSBenoît Thébaudeau * 121a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 13112fd2ecSBenoît Thébaudeau */ 14112fd2ecSBenoît Thébaudeau 15112fd2ecSBenoît Thébaudeau #include <common.h> 16112fd2ecSBenoît Thébaudeau #include <fuse.h> 171221ce45SMasahiro Yamada #include <linux/errno.h> 18112fd2ecSBenoît Thébaudeau #include <asm/io.h> 19112fd2ecSBenoît Thébaudeau #include <asm/arch/clock.h> 20112fd2ecSBenoît Thébaudeau #include <asm/arch/imx-regs.h> 21f8b95731SPeng Fan #include <asm/imx-common/sys_proto.h> 22112fd2ecSBenoît Thébaudeau 23112fd2ecSBenoît Thébaudeau #define BO_CTRL_WR_UNLOCK 16 24112fd2ecSBenoît Thébaudeau #define BM_CTRL_WR_UNLOCK 0xffff0000 25112fd2ecSBenoît Thébaudeau #define BV_CTRL_WR_UNLOCK_KEY 0x3e77 26112fd2ecSBenoît Thébaudeau #define BM_CTRL_ERROR 0x00000200 27112fd2ecSBenoît Thébaudeau #define BM_CTRL_BUSY 0x00000100 28112fd2ecSBenoît Thébaudeau #define BO_CTRL_ADDR 0 2942c91c10SAdrian Alonso #ifdef CONFIG_MX7 3042c91c10SAdrian Alonso #define BM_CTRL_ADDR 0x0000000f 3142c91c10SAdrian Alonso #define BM_CTRL_RELOAD 0x00000400 3242c91c10SAdrian Alonso #else 33112fd2ecSBenoît Thébaudeau #define BM_CTRL_ADDR 0x0000007f 3442c91c10SAdrian Alonso #endif 35112fd2ecSBenoît Thébaudeau 3642c91c10SAdrian Alonso #ifdef CONFIG_MX7 3742c91c10SAdrian Alonso #define BO_TIMING_FSOURCE 12 3842c91c10SAdrian Alonso #define BM_TIMING_FSOURCE 0x0007f000 3942c91c10SAdrian Alonso #define BV_TIMING_FSOURCE_NS 1001 4042c91c10SAdrian Alonso #define BO_TIMING_PROG 0 4142c91c10SAdrian Alonso #define BM_TIMING_PROG 0x00000fff 4242c91c10SAdrian Alonso #define BV_TIMING_PROG_US 10 4342c91c10SAdrian Alonso #else 44112fd2ecSBenoît Thébaudeau #define BO_TIMING_STROBE_READ 16 45112fd2ecSBenoît Thébaudeau #define BM_TIMING_STROBE_READ 0x003f0000 46112fd2ecSBenoît Thébaudeau #define BV_TIMING_STROBE_READ_NS 37 47112fd2ecSBenoît Thébaudeau #define BO_TIMING_RELAX 12 48112fd2ecSBenoît Thébaudeau #define BM_TIMING_RELAX 0x0000f000 49112fd2ecSBenoît Thébaudeau #define BV_TIMING_RELAX_NS 17 50112fd2ecSBenoît Thébaudeau #define BO_TIMING_STROBE_PROG 0 51112fd2ecSBenoît Thébaudeau #define BM_TIMING_STROBE_PROG 0x00000fff 52112fd2ecSBenoît Thébaudeau #define BV_TIMING_STROBE_PROG_US 10 5342c91c10SAdrian Alonso #endif 54112fd2ecSBenoît Thébaudeau 55112fd2ecSBenoît Thébaudeau #define BM_READ_CTRL_READ_FUSE 0x00000001 56112fd2ecSBenoît Thébaudeau 57112fd2ecSBenoît Thébaudeau #define BF(value, field) (((value) << BO_##field) & BM_##field) 58112fd2ecSBenoît Thébaudeau 59112fd2ecSBenoît Thébaudeau #define WRITE_POSTAMBLE_US 2 60112fd2ecSBenoît Thébaudeau 617296a023SPeng Fan #if defined(CONFIG_MX6) || defined(CONFIG_VF610) 627296a023SPeng Fan #define FUSE_BANK_SIZE 0x80 637296a023SPeng Fan #ifdef CONFIG_MX6SL 647296a023SPeng Fan #define FUSE_BANKS 8 65*b2ebdd85SPeng Fan #elif defined(CONFIG_MX6ULL) || defined(CONFIG_MX6SLL) 66f8b95731SPeng Fan #define FUSE_BANKS 9 677296a023SPeng Fan #else 687296a023SPeng Fan #define FUSE_BANKS 16 697296a023SPeng Fan #endif 707296a023SPeng Fan #elif defined CONFIG_MX7 717296a023SPeng Fan #define FUSE_BANK_SIZE 0x40 727296a023SPeng Fan #define FUSE_BANKS 16 737296a023SPeng Fan #else 747296a023SPeng Fan #error "Unsupported architecture\n" 757296a023SPeng Fan #endif 767296a023SPeng Fan 777296a023SPeng Fan #if defined(CONFIG_MX6) 787296a023SPeng Fan 797296a023SPeng Fan /* 807296a023SPeng Fan * There is a hole in shadow registers address map of size 0x100 81f8b95731SPeng Fan * between bank 5 and bank 6 on iMX6QP, iMX6DQ, iMX6SDL, iMX6SX, 82*b2ebdd85SPeng Fan * iMX6UL, i.MX6ULL and i.MX6SLL. 837296a023SPeng Fan * Bank 5 ends at 0x6F0 and Bank 6 starts at 0x800. When reading the fuses, 847296a023SPeng Fan * we should account for this hole in address space. 857296a023SPeng Fan * 867296a023SPeng Fan * Similar hole exists between bank 14 and bank 15 of size 877296a023SPeng Fan * 0x80 on iMX6QP, iMX6DQ, iMX6SDL and iMX6SX. 887296a023SPeng Fan * Note: iMX6SL has only 0-7 banks and there is no hole. 897296a023SPeng Fan * Note: iMX6UL doesn't have this one. 907296a023SPeng Fan * 917296a023SPeng Fan * This function is to covert user input to physical bank index. 927296a023SPeng Fan * Only needed when read fuse, because we use register offset, so 937296a023SPeng Fan * need to calculate real register offset. 947296a023SPeng Fan * When write, no need to consider hole, always use the bank/word 957296a023SPeng Fan * index from fuse map. 967296a023SPeng Fan */ 977296a023SPeng Fan u32 fuse_bank_physical(int index) 987296a023SPeng Fan { 997296a023SPeng Fan u32 phy_index; 1007296a023SPeng Fan 101bff75503SPeng Fan if (is_mx6sl()) { 1027296a023SPeng Fan phy_index = index; 103*b2ebdd85SPeng Fan } else if (is_mx6ul() || is_mx6ull() || is_mx6sll()) { 104*b2ebdd85SPeng Fan if ((is_mx6ull() || is_mx6sll()) && index == 8) 105f8b95731SPeng Fan index = 7; 106f8b95731SPeng Fan 1077296a023SPeng Fan if (index >= 6) 1087296a023SPeng Fan phy_index = fuse_bank_physical(5) + (index - 6) + 3; 1097296a023SPeng Fan else 1107296a023SPeng Fan phy_index = index; 1117296a023SPeng Fan } else { 1127296a023SPeng Fan if (index >= 15) 1137296a023SPeng Fan phy_index = fuse_bank_physical(14) + (index - 15) + 2; 1147296a023SPeng Fan else if (index >= 6) 1157296a023SPeng Fan phy_index = fuse_bank_physical(5) + (index - 6) + 3; 1167296a023SPeng Fan else 1177296a023SPeng Fan phy_index = index; 1187296a023SPeng Fan } 1197296a023SPeng Fan return phy_index; 1207296a023SPeng Fan } 121f8b95731SPeng Fan 122f8b95731SPeng Fan u32 fuse_word_physical(u32 bank, u32 word_index) 123f8b95731SPeng Fan { 124*b2ebdd85SPeng Fan if (is_mx6ull() || is_mx6sll()) { 125f8b95731SPeng Fan if (bank == 8) 126f8b95731SPeng Fan word_index = word_index + 4; 127f8b95731SPeng Fan } 128f8b95731SPeng Fan 129f8b95731SPeng Fan return word_index; 130f8b95731SPeng Fan } 1317296a023SPeng Fan #else 1327296a023SPeng Fan u32 fuse_bank_physical(int index) 1337296a023SPeng Fan { 1347296a023SPeng Fan return index; 1357296a023SPeng Fan } 136f8b95731SPeng Fan 137f8b95731SPeng Fan u32 fuse_word_physical(u32 bank, u32 word_index) 138f8b95731SPeng Fan { 139f8b95731SPeng Fan return word_index; 140f8b95731SPeng Fan } 141f8b95731SPeng Fan 1427296a023SPeng Fan #endif 1437296a023SPeng Fan 144112fd2ecSBenoît Thébaudeau static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us) 145112fd2ecSBenoît Thébaudeau { 146112fd2ecSBenoît Thébaudeau while (readl(®s->ctrl) & BM_CTRL_BUSY) 147112fd2ecSBenoît Thébaudeau udelay(delay_us); 148112fd2ecSBenoît Thébaudeau } 149112fd2ecSBenoît Thébaudeau 150112fd2ecSBenoît Thébaudeau static void clear_error(struct ocotp_regs *regs) 151112fd2ecSBenoît Thébaudeau { 152112fd2ecSBenoît Thébaudeau writel(BM_CTRL_ERROR, ®s->ctrl_clr); 153112fd2ecSBenoît Thébaudeau } 154112fd2ecSBenoît Thébaudeau 155112fd2ecSBenoît Thébaudeau static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word, 156112fd2ecSBenoît Thébaudeau int assert, const char *caller) 157112fd2ecSBenoît Thébaudeau { 158112fd2ecSBenoît Thébaudeau *regs = (struct ocotp_regs *)OCOTP_BASE_ADDR; 159112fd2ecSBenoît Thébaudeau 1607296a023SPeng Fan if (bank >= FUSE_BANKS || 161112fd2ecSBenoît Thébaudeau word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 || 162112fd2ecSBenoît Thébaudeau !assert) { 163112fd2ecSBenoît Thébaudeau printf("mxc_ocotp %s(): Invalid argument\n", caller); 164112fd2ecSBenoît Thébaudeau return -EINVAL; 165112fd2ecSBenoît Thébaudeau } 166112fd2ecSBenoît Thébaudeau 167*b2ebdd85SPeng Fan if (is_mx6ull() || is_mx6sll()) { 168f8b95731SPeng Fan if ((bank == 7 || bank == 8) && 169f8b95731SPeng Fan word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 3) { 170*b2ebdd85SPeng Fan printf("mxc_ocotp %s(): Invalid argument\n", caller); 171f8b95731SPeng Fan return -EINVAL; 172f8b95731SPeng Fan } 173f8b95731SPeng Fan } 174f8b95731SPeng Fan 175112fd2ecSBenoît Thébaudeau enable_ocotp_clk(1); 176112fd2ecSBenoît Thébaudeau 177112fd2ecSBenoît Thébaudeau wait_busy(*regs, 1); 178112fd2ecSBenoît Thébaudeau clear_error(*regs); 179112fd2ecSBenoît Thébaudeau 180112fd2ecSBenoît Thébaudeau return 0; 181112fd2ecSBenoît Thébaudeau } 182112fd2ecSBenoît Thébaudeau 183112fd2ecSBenoît Thébaudeau static int finish_access(struct ocotp_regs *regs, const char *caller) 184112fd2ecSBenoît Thébaudeau { 185112fd2ecSBenoît Thébaudeau u32 err; 186112fd2ecSBenoît Thébaudeau 187112fd2ecSBenoît Thébaudeau err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); 188112fd2ecSBenoît Thébaudeau clear_error(regs); 189112fd2ecSBenoît Thébaudeau 190112fd2ecSBenoît Thébaudeau if (err) { 191112fd2ecSBenoît Thébaudeau printf("mxc_ocotp %s(): Access protect error\n", caller); 192112fd2ecSBenoît Thébaudeau return -EIO; 193112fd2ecSBenoît Thébaudeau } 194112fd2ecSBenoît Thébaudeau 195112fd2ecSBenoît Thébaudeau return 0; 196112fd2ecSBenoît Thébaudeau } 197112fd2ecSBenoît Thébaudeau 198112fd2ecSBenoît Thébaudeau static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val, 199112fd2ecSBenoît Thébaudeau const char *caller) 200112fd2ecSBenoît Thébaudeau { 201112fd2ecSBenoît Thébaudeau return prepare_access(regs, bank, word, val != NULL, caller); 202112fd2ecSBenoît Thébaudeau } 203112fd2ecSBenoît Thébaudeau 204112fd2ecSBenoît Thébaudeau int fuse_read(u32 bank, u32 word, u32 *val) 205112fd2ecSBenoît Thébaudeau { 206112fd2ecSBenoît Thébaudeau struct ocotp_regs *regs; 207112fd2ecSBenoît Thébaudeau int ret; 2087296a023SPeng Fan u32 phy_bank; 209f8b95731SPeng Fan u32 phy_word; 210112fd2ecSBenoît Thébaudeau 211112fd2ecSBenoît Thébaudeau ret = prepare_read(®s, bank, word, val, __func__); 212112fd2ecSBenoît Thébaudeau if (ret) 213112fd2ecSBenoît Thébaudeau return ret; 214112fd2ecSBenoît Thébaudeau 2157296a023SPeng Fan phy_bank = fuse_bank_physical(bank); 216f8b95731SPeng Fan phy_word = fuse_word_physical(bank, word); 2177296a023SPeng Fan 218f8b95731SPeng Fan *val = readl(®s->bank[phy_bank].fuse_regs[phy_word << 2]); 219112fd2ecSBenoît Thébaudeau 220112fd2ecSBenoît Thébaudeau return finish_access(regs, __func__); 221112fd2ecSBenoît Thébaudeau } 222112fd2ecSBenoît Thébaudeau 22342c91c10SAdrian Alonso #ifdef CONFIG_MX7 22442c91c10SAdrian Alonso static void set_timing(struct ocotp_regs *regs) 22542c91c10SAdrian Alonso { 22642c91c10SAdrian Alonso u32 ipg_clk; 22742c91c10SAdrian Alonso u32 fsource, prog; 22842c91c10SAdrian Alonso u32 timing; 22942c91c10SAdrian Alonso 23042c91c10SAdrian Alonso ipg_clk = mxc_get_clock(MXC_IPG_CLK); 23142c91c10SAdrian Alonso 23242c91c10SAdrian Alonso fsource = DIV_ROUND_UP((ipg_clk / 1000) * BV_TIMING_FSOURCE_NS, 23342c91c10SAdrian Alonso + 1000000) + 1; 23442c91c10SAdrian Alonso prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_PROG_US, 1000000) + 1; 23542c91c10SAdrian Alonso 23642c91c10SAdrian Alonso timing = BF(fsource, TIMING_FSOURCE) | BF(prog, TIMING_PROG); 23742c91c10SAdrian Alonso 23842c91c10SAdrian Alonso clrsetbits_le32(®s->timing, BM_TIMING_FSOURCE | BM_TIMING_PROG, 23942c91c10SAdrian Alonso timing); 24042c91c10SAdrian Alonso } 24142c91c10SAdrian Alonso #else 242112fd2ecSBenoît Thébaudeau static void set_timing(struct ocotp_regs *regs) 243112fd2ecSBenoît Thébaudeau { 244112fd2ecSBenoît Thébaudeau u32 ipg_clk; 245112fd2ecSBenoît Thébaudeau u32 relax, strobe_read, strobe_prog; 246112fd2ecSBenoît Thébaudeau u32 timing; 247112fd2ecSBenoît Thébaudeau 248112fd2ecSBenoît Thébaudeau ipg_clk = mxc_get_clock(MXC_IPG_CLK); 249112fd2ecSBenoît Thébaudeau 250112fd2ecSBenoît Thébaudeau relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1; 251112fd2ecSBenoît Thébaudeau strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS, 252112fd2ecSBenoît Thébaudeau 1000000000) + 2 * (relax + 1) - 1; 2534515992fSMasahiro Yamada strobe_prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_STROBE_PROG_US, 2544515992fSMasahiro Yamada 1000000) + 2 * (relax + 1) - 1; 255112fd2ecSBenoît Thébaudeau 256112fd2ecSBenoît Thébaudeau timing = BF(strobe_read, TIMING_STROBE_READ) | 257112fd2ecSBenoît Thébaudeau BF(relax, TIMING_RELAX) | 258112fd2ecSBenoît Thébaudeau BF(strobe_prog, TIMING_STROBE_PROG); 259112fd2ecSBenoît Thébaudeau 260112fd2ecSBenoît Thébaudeau clrsetbits_le32(®s->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX | 261112fd2ecSBenoît Thébaudeau BM_TIMING_STROBE_PROG, timing); 262112fd2ecSBenoît Thébaudeau } 26342c91c10SAdrian Alonso #endif 264112fd2ecSBenoît Thébaudeau 265112fd2ecSBenoît Thébaudeau static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word, 266112fd2ecSBenoît Thébaudeau int write) 267112fd2ecSBenoît Thébaudeau { 268112fd2ecSBenoît Thébaudeau u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0; 26942c91c10SAdrian Alonso #ifdef CONFIG_MX7 27042c91c10SAdrian Alonso u32 addr = bank; 27142c91c10SAdrian Alonso #else 272f8b95731SPeng Fan u32 addr; 273f8b95731SPeng Fan /* Bank 7 and Bank 8 only supports 4 words each for i.MX6ULL */ 274*b2ebdd85SPeng Fan if ((is_mx6ull() || is_mx6sll()) && (bank > 7)) { 275f8b95731SPeng Fan bank = bank - 1; 276f8b95731SPeng Fan word += 4; 277f8b95731SPeng Fan } 278f8b95731SPeng Fan addr = bank << 3 | word; 27942c91c10SAdrian Alonso #endif 280112fd2ecSBenoît Thébaudeau 281112fd2ecSBenoît Thébaudeau set_timing(regs); 282112fd2ecSBenoît Thébaudeau clrsetbits_le32(®s->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR, 283112fd2ecSBenoît Thébaudeau BF(wr_unlock, CTRL_WR_UNLOCK) | 284112fd2ecSBenoît Thébaudeau BF(addr, CTRL_ADDR)); 285112fd2ecSBenoît Thébaudeau } 286112fd2ecSBenoît Thébaudeau 287112fd2ecSBenoît Thébaudeau int fuse_sense(u32 bank, u32 word, u32 *val) 288112fd2ecSBenoît Thébaudeau { 289112fd2ecSBenoît Thébaudeau struct ocotp_regs *regs; 290112fd2ecSBenoît Thébaudeau int ret; 291112fd2ecSBenoît Thébaudeau 292112fd2ecSBenoît Thébaudeau ret = prepare_read(®s, bank, word, val, __func__); 293112fd2ecSBenoît Thébaudeau if (ret) 294112fd2ecSBenoît Thébaudeau return ret; 295112fd2ecSBenoît Thébaudeau 296112fd2ecSBenoît Thébaudeau setup_direct_access(regs, bank, word, false); 297112fd2ecSBenoît Thébaudeau writel(BM_READ_CTRL_READ_FUSE, ®s->read_ctrl); 298112fd2ecSBenoît Thébaudeau wait_busy(regs, 1); 29942c91c10SAdrian Alonso #ifdef CONFIG_MX7 30042c91c10SAdrian Alonso *val = readl((®s->read_fuse_data0) + (word << 2)); 30142c91c10SAdrian Alonso #else 302112fd2ecSBenoît Thébaudeau *val = readl(®s->read_fuse_data); 30342c91c10SAdrian Alonso #endif 304112fd2ecSBenoît Thébaudeau 305112fd2ecSBenoît Thébaudeau return finish_access(regs, __func__); 306112fd2ecSBenoît Thébaudeau } 307112fd2ecSBenoît Thébaudeau 308112fd2ecSBenoît Thébaudeau static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word, 309112fd2ecSBenoît Thébaudeau const char *caller) 310112fd2ecSBenoît Thébaudeau { 311112fd2ecSBenoît Thébaudeau return prepare_access(regs, bank, word, true, caller); 312112fd2ecSBenoît Thébaudeau } 313112fd2ecSBenoît Thébaudeau 314112fd2ecSBenoît Thébaudeau int fuse_prog(u32 bank, u32 word, u32 val) 315112fd2ecSBenoît Thébaudeau { 316112fd2ecSBenoît Thébaudeau struct ocotp_regs *regs; 317112fd2ecSBenoît Thébaudeau int ret; 318112fd2ecSBenoît Thébaudeau 319112fd2ecSBenoît Thébaudeau ret = prepare_write(®s, bank, word, __func__); 320112fd2ecSBenoît Thébaudeau if (ret) 321112fd2ecSBenoît Thébaudeau return ret; 322112fd2ecSBenoît Thébaudeau 323112fd2ecSBenoît Thébaudeau setup_direct_access(regs, bank, word, true); 32442c91c10SAdrian Alonso #ifdef CONFIG_MX7 32542c91c10SAdrian Alonso switch (word) { 32642c91c10SAdrian Alonso case 0: 32742c91c10SAdrian Alonso writel(0, ®s->data1); 32842c91c10SAdrian Alonso writel(0, ®s->data2); 32942c91c10SAdrian Alonso writel(0, ®s->data3); 33042c91c10SAdrian Alonso writel(val, ®s->data0); 33142c91c10SAdrian Alonso break; 33242c91c10SAdrian Alonso case 1: 33342c91c10SAdrian Alonso writel(val, ®s->data1); 33442c91c10SAdrian Alonso writel(0, ®s->data2); 33542c91c10SAdrian Alonso writel(0, ®s->data3); 33642c91c10SAdrian Alonso writel(0, ®s->data0); 33742c91c10SAdrian Alonso break; 33842c91c10SAdrian Alonso case 2: 33942c91c10SAdrian Alonso writel(0, ®s->data1); 34042c91c10SAdrian Alonso writel(val, ®s->data2); 34142c91c10SAdrian Alonso writel(0, ®s->data3); 34242c91c10SAdrian Alonso writel(0, ®s->data0); 34342c91c10SAdrian Alonso break; 34442c91c10SAdrian Alonso case 3: 34542c91c10SAdrian Alonso writel(0, ®s->data1); 34642c91c10SAdrian Alonso writel(0, ®s->data2); 34742c91c10SAdrian Alonso writel(val, ®s->data3); 34842c91c10SAdrian Alonso writel(0, ®s->data0); 34942c91c10SAdrian Alonso break; 35042c91c10SAdrian Alonso } 35142c91c10SAdrian Alonso wait_busy(regs, BV_TIMING_PROG_US); 35242c91c10SAdrian Alonso #else 353112fd2ecSBenoît Thébaudeau writel(val, ®s->data); 354112fd2ecSBenoît Thébaudeau wait_busy(regs, BV_TIMING_STROBE_PROG_US); 35542c91c10SAdrian Alonso #endif 356112fd2ecSBenoît Thébaudeau udelay(WRITE_POSTAMBLE_US); 357112fd2ecSBenoît Thébaudeau 358112fd2ecSBenoît Thébaudeau return finish_access(regs, __func__); 359112fd2ecSBenoît Thébaudeau } 360112fd2ecSBenoît Thébaudeau 361112fd2ecSBenoît Thébaudeau int fuse_override(u32 bank, u32 word, u32 val) 362112fd2ecSBenoît Thébaudeau { 363112fd2ecSBenoît Thébaudeau struct ocotp_regs *regs; 364112fd2ecSBenoît Thébaudeau int ret; 3657296a023SPeng Fan u32 phy_bank; 366f8b95731SPeng Fan u32 phy_word; 367112fd2ecSBenoît Thébaudeau 368112fd2ecSBenoît Thébaudeau ret = prepare_write(®s, bank, word, __func__); 369112fd2ecSBenoît Thébaudeau if (ret) 370112fd2ecSBenoît Thébaudeau return ret; 371112fd2ecSBenoît Thébaudeau 3727296a023SPeng Fan phy_bank = fuse_bank_physical(bank); 373f8b95731SPeng Fan phy_word = fuse_word_physical(bank, word); 3747296a023SPeng Fan 375f8b95731SPeng Fan writel(val, ®s->bank[phy_bank].fuse_regs[phy_word << 2]); 376112fd2ecSBenoît Thébaudeau 377112fd2ecSBenoît Thébaudeau return finish_access(regs, __func__); 378112fd2ecSBenoît Thébaudeau } 379