--- /dev/null
+/*\r
+ * arch/arm/mach-tegra/include/mach/tegra2_fuse.h\r
+ *\r
+ * Copyright (c) 2010, NVIDIA Corporation.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful, but WITHOUT\r
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\r
+ * more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
+ */\r
+\r
+#ifndef __MACH_TEGRA2_FUSE_H\r
+#define __MACH_TEGRA2_FUSE_H\r
+\r
+#define SBK_DEVKEY_STATUS_SZ sizeof(u32)\r
+\r
+/* fuse io parameters */\r
+enum fuse_io_param {\r
+ DEVKEY,\r
+ JTAG_DIS,\r
+ /*\r
+ * Programming the odm production fuse at the same\r
+ * time as the sbk or dev_key is not allowed as it is not possible to\r
+ * verify that the sbk or dev_key were programmed correctly.\r
+ */\r
+ ODM_PROD_MODE,\r
+ SEC_BOOT_DEV_CFG,\r
+ SEC_BOOT_DEV_SEL,\r
+ SBK,\r
+ SW_RSVD,\r
+ IGNORE_DEV_SEL_STRAPS,\r
+ ODM_RSVD,\r
+ SBK_DEVKEY_STATUS,\r
+ MASTER_ENB,\r
+ _PARAMS_U32 = 0x7FFFFFFF\r
+};\r
+\r
+#define MAX_PARAMS ODM_RSVD\r
+\r
+/* the order of the members is pre-decided. please do not change */\r
+struct fuse_data {\r
+ u32 devkey;\r
+ u32 jtag_dis;\r
+ u32 odm_prod_mode;\r
+ u32 bootdev_cfg;\r
+ u32 bootdev_sel;\r
+ u32 sbk[4];\r
+ u32 sw_rsvd;\r
+ u32 ignore_devsel_straps;\r
+ u32 odm_rsvd[8];\r
+};\r
+\r
+/* secondary boot device options */\r
+enum {\r
+ SECBOOTDEV_SDMMC,\r
+ SECBOOTDEV_NOR,\r
+ SECBOOTDEV_SPI,\r
+ SECBOOTDEV_NAND,\r
+ SECBOOTDEV_LBANAND,\r
+ SECBOOTDEV_MUXONENAND,\r
+ _SECBOOTDEV_MAX,\r
+ _SECBOOTDEV_U32 = 0x7FFFFFFF\r
+};\r
+\r
+/*\r
+ * read the fuse settings\r
+ * @param: io_param_type - param type enum\r
+ * @param: size - read size in bytes\r
+ */\r
+int tegra_fuse_read(u32 io_param_type, u32 *data, int size);\r
+\r
+#define FLAGS_DEVKEY BIT(DEVKEY)\r
+#define FLAGS_JTAG_DIS BIT(JTAG_DIS)\r
+#define FLAGS_SBK_DEVKEY_STATUS BIT(SBK_DEVKEY_STATUS)\r
+#define FLAGS_ODM_PROD_MODE BIT(ODM_PROD_MODE)\r
+#define FLAGS_SEC_BOOT_DEV_CFG BIT(SEC_BOOT_DEV_CFG)\r
+#define FLAGS_SEC_BOOT_DEV_SEL BIT(SEC_BOOT_DEV_SEL)\r
+#define FLAGS_SBK BIT(SBK)\r
+#define FLAGS_SW_RSVD BIT(SW_RSVD)\r
+#define FLAGS_IGNORE_DEV_SEL_STRAPS BIT(IGNORE_DEV_SEL_STRAPS)\r
+#define FLAGS_ODMRSVD BIT(ODM_RSVD)\r
+\r
+/*\r
+ * Prior to invoking this routine, the caller is responsible for supplying\r
+ * valid fuse programming voltage.\r
+ *\r
+ * @param: pgm_data - entire data to be programmed\r
+ * @flags: program flags (e.g. FLAGS_DEVKEY)\r
+ */\r
+int tegra_fuse_program(struct fuse_data *pgm_data, u32 flags);\r
+\r
+/* Disables the fuse programming until the next system reset */\r
+void tegra_fuse_program_disable(void);\r
+\r
+#endif\r
--- /dev/null
+/*\r
+ * arch/arm/mach-tegra/tegra2_fuse.c\r
+ *\r
+ * Copyright (c) 2010, NVIDIA Corporation.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful, but WITHOUT\r
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\r
+ * more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
+ */\r
+\r
+/*\r
+ * Fuses are one time programmable bits on the chip which are used by\r
+ * the chip manufacturer and device manufacturers to store chip/device\r
+ * configurations. The fuse bits are encapsulated in a 32 x 64 array.\r
+ * If a fuse bit is programmed to 1, it cannot be reverted to 0. Either\r
+ * another fuse bit has to be used for the same purpose or a new chip\r
+ * needs to be used.\r
+ *\r
+ * Each and every fuse word has its own shadow word which resides adjacent to\r
+ * a particular fuse word. e.g. Fuse words 0-1 form a fuse-shadow pair.\r
+ * So in theory we have only 32 fuse words to work with.\r
+ * The shadow fuse word is a mirror of the actual fuse word at all times\r
+ * and this is maintained while programming a particular fuse.\r
+ */\r
+\r
+#include <linux/kernel.h>\r
+#include <linux/io.h>\r
+#include <linux/mutex.h>\r
+#include <linux/err.h>\r
+#include <linux/delay.h>\r
+#include <linux/slab.h>\r
+\r
+#include <mach/tegra2_fuse.h>\r
+\r
+#include "fuse.h"\r
+\r
+#define NFUSES 64\r
+#define STATE_IDLE (0x4 << 16)\r
+\r
+/* since fuse burning is irreversible, use this for testing */\r
+#define ENABLE_FUSE_BURNING 1\r
+\r
+/* fuse registers */\r
+#define FUSE_CTRL 0x000\r
+#define FUSE_REG_ADDR 0x004\r
+#define FUSE_REG_READ 0x008\r
+#define FUSE_REG_WRITE 0x00C\r
+#define FUSE_TIME_PGM 0x01C\r
+#define FUSE_PRIV2INTFC 0x020\r
+#define FUSE_DIS_PGM 0x02C\r
+#define FUSE_PWR_GOOD_SW 0x034\r
+\r
+static u32 fuse_pgm_data[NFUSES / 2];\r
+static u32 fuse_pgm_mask[NFUSES / 2];\r
+static u32 tmp_fuse_pgm_data[NFUSES / 2];\r
+static u8 master_enable;\r
+\r
+DEFINE_MUTEX(fuse_lock);\r
+\r
+static struct fuse_data fuse_info;\r
+\r
+struct param_info {\r
+ u32 *addr;\r
+ int sz;\r
+ u32 start_off;\r
+ int start_bit;\r
+ int nbits;\r
+ int data_offset;\r
+};\r
+\r
+static struct param_info fuse_info_tbl[] = {\r
+ [DEVKEY] = {\r
+ .addr = &fuse_info.devkey,\r
+ .sz = sizeof(fuse_info.devkey),\r
+ .start_off = 0x12,\r
+ .start_bit = 8,\r
+ .nbits = 32,\r
+ .data_offset = 0,\r
+ },\r
+ [JTAG_DIS] = {\r
+ .addr = &fuse_info.jtag_dis,\r
+ .sz = sizeof(fuse_info.jtag_dis),\r
+ .start_off = 0x0,\r
+ .start_bit = 24,\r
+ .nbits = 1,\r
+ .data_offset = 1,\r
+ },\r
+ [ODM_PROD_MODE] = {\r
+ .addr = &fuse_info.odm_prod_mode,\r
+ .sz = sizeof(fuse_info.odm_prod_mode),\r
+ .start_off = 0x0,\r
+ .start_bit = 23,\r
+ .nbits = 1,\r
+ .data_offset = 2,\r
+ },\r
+ [SEC_BOOT_DEV_CFG] = {\r
+ .addr = &fuse_info.bootdev_cfg,\r
+ .sz = sizeof(fuse_info.bootdev_cfg),\r
+ .start_off = 0x14,\r
+ .start_bit = 8,\r
+ .nbits = 16,\r
+ .data_offset = 3,\r
+ },\r
+ [SEC_BOOT_DEV_SEL] = {\r
+ .addr = &fuse_info.bootdev_sel,\r
+ .sz = sizeof(fuse_info.bootdev_sel),\r
+ .start_off = 0x14,\r
+ .start_bit = 24,\r
+ .nbits = 3,\r
+ .data_offset = 4,\r
+ },\r
+ [SBK] = {\r
+ .addr = fuse_info.sbk,\r
+ .sz = sizeof(fuse_info.sbk),\r
+ .start_off = 0x0A,\r
+ .start_bit = 8,\r
+ .nbits = 128,\r
+ .data_offset = 5,\r
+ },\r
+ [SW_RSVD] = {\r
+ .addr = &fuse_info.sw_rsvd,\r
+ .sz = sizeof(fuse_info.sw_rsvd),\r
+ .start_off = 0x14,\r
+ .start_bit = 28,\r
+ .nbits = 4,\r
+ .data_offset = 9,\r
+ },\r
+ [IGNORE_DEV_SEL_STRAPS] = {\r
+ .addr = &fuse_info.ignore_devsel_straps,\r
+ .sz = sizeof(fuse_info.ignore_devsel_straps),\r
+ .start_off = 0x14,\r
+ .start_bit = 27,\r
+ .nbits = 1,\r
+ .data_offset = 10,\r
+ },\r
+ [ODM_RSVD] = {\r
+ .addr = &fuse_info.odm_rsvd,\r
+ .sz = sizeof(fuse_info.odm_rsvd),\r
+ .start_off = 0x16,\r
+ .start_bit = 4,\r
+ .nbits = 256,\r
+ .data_offset = 11,\r
+ },\r
+ [SBK_DEVKEY_STATUS] = {\r
+ .sz = SBK_DEVKEY_STATUS_SZ,\r
+ },\r
+ [MASTER_ENB] = {\r
+ .addr = &master_enable,\r
+ .sz = sizeof(u8),\r
+ .start_off = 0x0,\r
+ .start_bit = 0,\r
+ .nbits = 1,\r
+ },\r
+};\r
+\r
+static void wait_for_idle(void)\r
+{\r
+ u32 reg;\r
+\r
+ do {\r
+ reg = tegra_fuse_readl(FUSE_CTRL);\r
+ } while ((reg & (0xF << 16)) != STATE_IDLE);\r
+}\r
+\r
+#define FUSE_READ 0x1\r
+#define FUSE_WRITE 0x2\r
+#define FUSE_SENSE 0x3\r
+#define FUSE_CMD_MASK 0x3\r
+\r
+static u32 fuse_cmd_read(u32 addr)\r
+{\r
+ u32 reg;\r
+\r
+ tegra_fuse_writel(addr, FUSE_REG_ADDR);\r
+ reg = tegra_fuse_readl(FUSE_CTRL);\r
+ reg &= ~FUSE_CMD_MASK;\r
+ reg |= FUSE_READ;\r
+ tegra_fuse_writel(reg, FUSE_CTRL);\r
+ wait_for_idle();\r
+\r
+ reg = tegra_fuse_readl(FUSE_REG_READ);\r
+ return reg;\r
+}\r
+\r
+static void fuse_cmd_write(u32 value, u32 addr)\r
+{\r
+ u32 reg;\r
+\r
+ tegra_fuse_writel(addr, FUSE_REG_ADDR);\r
+ tegra_fuse_writel(value, FUSE_REG_WRITE);\r
+\r
+ reg = tegra_fuse_readl(FUSE_CTRL);\r
+ reg &= ~FUSE_CMD_MASK;\r
+ reg |= FUSE_WRITE;\r
+ tegra_fuse_writel(reg, FUSE_CTRL);\r
+ wait_for_idle();\r
+}\r
+\r
+static void fuse_cmd_sense(void)\r
+{\r
+ u32 reg;\r
+\r
+ reg = tegra_fuse_readl(FUSE_CTRL);\r
+ reg &= ~FUSE_CMD_MASK;\r
+ reg |= FUSE_SENSE;\r
+ tegra_fuse_writel(reg, FUSE_CTRL);\r
+ wait_for_idle();\r
+}\r
+\r
+static void fuse_reg_hide(void)\r
+{\r
+ u32 reg = tegra_fuse_readl(0x48);\r
+ reg &= ~(1 << 28);\r
+ tegra_fuse_writel(reg, 0x48);\r
+}\r
+\r
+static void fuse_reg_unhide(void)\r
+{\r
+ u32 reg = tegra_fuse_readl(0x48);\r
+ reg |= (1 << 28);\r
+ tegra_fuse_writel(reg, 0x48);\r
+}\r
+\r
+static void get_fuse(enum fuse_io_param io_param, u32 *out)\r
+{\r
+ int start_bit = fuse_info_tbl[io_param].start_bit;\r
+ int nbits = fuse_info_tbl[io_param].nbits;\r
+ int offset = fuse_info_tbl[io_param].start_off;\r
+ u32 *dst = fuse_info_tbl[io_param].addr;\r
+ int dst_bit = 0;\r
+ int i;\r
+ u32 val;\r
+ int loops;\r
+\r
+ if (out)\r
+ dst = out;\r
+\r
+ do {\r
+ val = fuse_cmd_read(offset);\r
+ loops = min(nbits, 32 - start_bit);\r
+ for (i = 0; i < loops; i++) {\r
+ if (val & (BIT(start_bit + i)))\r
+ *dst |= BIT(dst_bit);\r
+ else\r
+ *dst &= ~BIT(dst_bit);\r
+ dst_bit++;\r
+ if (dst_bit == 32) {\r
+ dst++;\r
+ dst_bit = 0;\r
+ }\r
+ }\r
+ nbits -= loops;\r
+ offset += 2;\r
+ start_bit = 0;\r
+ } while (nbits > 0);\r
+}\r
+\r
+int tegra_fuse_read(enum fuse_io_param io_param, u32 *data, int size)\r
+{\r
+ int ret = 0, nbits;\r
+ u32 sbk[4], devkey = 0;\r
+\r
+ if (!data)\r
+ return -EINVAL;\r
+\r
+ if (size != fuse_info_tbl[io_param].sz) {\r
+ pr_err("%s: size mismatch(%d), %d vs %d\n", __func__,\r
+ (int)io_param, size, fuse_info_tbl[io_param].sz);\r
+ return -EINVAL;\r
+ }\r
+\r
+ mutex_lock(&fuse_lock);\r
+ fuse_reg_unhide();\r
+ fuse_cmd_sense();\r
+\r
+ if (io_param == SBK_DEVKEY_STATUS) {\r
+ *data = 0;\r
+\r
+ get_fuse(SBK, sbk);\r
+ get_fuse(DEVKEY, &devkey);\r
+ nbits = sizeof(sbk) * BITS_PER_BYTE;\r
+ if (find_first_bit((unsigned long *)sbk, nbits) != nbits)\r
+ *data = 1;\r
+ else if (devkey)\r
+ *data = 1;\r
+ } else {\r
+ get_fuse(io_param, data);\r
+ }\r
+\r
+ fuse_reg_hide();\r
+ mutex_unlock(&fuse_lock);\r
+ return ret;\r
+}\r
+\r
+static bool fuse_odm_prod_mode(void)\r
+{\r
+ u32 odm_prod_mode = 0;\r
+\r
+ get_fuse(ODM_PROD_MODE, &odm_prod_mode);\r
+ return (odm_prod_mode ? true : false);\r
+}\r
+\r
+static void set_fuse(enum fuse_io_param io_param, u32 *data)\r
+{\r
+ int i, start_bit = fuse_info_tbl[io_param].start_bit;\r
+ int nbits = fuse_info_tbl[io_param].nbits, loops;\r
+ int offset = fuse_info_tbl[io_param].start_off >> 1;\r
+ int src_bit = 0;\r
+ u32 val;\r
+\r
+ do {\r
+ val = *data;\r
+ loops = min(nbits, 32 - start_bit);\r
+ for (i = 0; i < loops; i++) {\r
+ fuse_pgm_mask[offset] |= BIT(start_bit + i);\r
+ if (val & BIT(src_bit))\r
+ fuse_pgm_data[offset] |= BIT(start_bit + i);\r
+ else\r
+ fuse_pgm_data[offset] &= ~BIT(start_bit + i);\r
+ src_bit++;\r
+ if (src_bit == 32) {\r
+ data++;\r
+ val = *data;\r
+ src_bit = 0;\r
+ }\r
+ }\r
+ nbits -= loops;\r
+ offset++;\r
+ start_bit = 0;\r
+ } while (nbits > 0);\r
+}\r
+\r
+static void populate_fuse_arrs(struct fuse_data *info, u32 flags)\r
+{\r
+ u32 data = 0;\r
+ u32 *src = (u32 *)info;\r
+ int i;\r
+\r
+ memset(fuse_pgm_data, 0, sizeof(fuse_pgm_data));\r
+ memset(fuse_pgm_mask, 0, sizeof(fuse_pgm_mask));\r
+\r
+ /* enable program bit */\r
+ data = 1;\r
+ set_fuse(MASTER_ENB, &data);\r
+\r
+ if ((flags & FLAGS_ODMRSVD)) {\r
+ set_fuse(ODM_RSVD, info->odm_rsvd);\r
+ flags &= ~FLAGS_ODMRSVD;\r
+ }\r
+\r
+ /* do not burn any more if secure mode is set */\r
+ if (fuse_odm_prod_mode())\r
+ goto out;\r
+\r
+ for_each_set_bit(i, (unsigned long *)&flags, MAX_PARAMS)\r
+ set_fuse(i, src + fuse_info_tbl[i].data_offset);\r
+\r
+out:\r
+ pr_debug("ready to program");\r
+}\r
+\r
+static void fuse_power_enable(void)\r
+{\r
+#if ENABLE_FUSE_BURNING\r
+ tegra_fuse_writel(0x1, FUSE_PWR_GOOD_SW);\r
+ udelay(1);\r
+#endif\r
+}\r
+\r
+static void fuse_power_disable(void)\r
+{\r
+#if ENABLE_FUSE_BURNING\r
+ tegra_fuse_writel(0, FUSE_PWR_GOOD_SW);\r
+ udelay(1);\r
+#endif\r
+}\r
+\r
+static void fuse_program_array(int pgm_cycles)\r
+{\r
+ u32 reg, fuse_val[2];\r
+ u32 *data = tmp_fuse_pgm_data, addr = 0, *mask = fuse_pgm_mask;\r
+ int i = 0;\r
+\r
+ fuse_reg_unhide();\r
+ fuse_cmd_sense();\r
+\r
+ /* get the first 2 fuse bytes */\r
+ fuse_val[0] = fuse_cmd_read(0);\r
+ fuse_val[1] = fuse_cmd_read(1);\r
+\r
+ fuse_power_enable();\r
+\r
+ /*\r
+ * The fuse macro is a high density macro. Fuses are\r
+ * burned using an addressing mechanism, so no need to prepare\r
+ * the full list, but more write to control registers are needed.\r
+ * The only bit that can be written at first is bit 0, a special write\r
+ * protection bit by assumptions all other bits are at 0\r
+ *\r
+ * The programming pulse must have a precise width of\r
+ * [9000, 11000] ns.\r
+ */\r
+ if (pgm_cycles > 0) {\r
+ reg = pgm_cycles;\r
+ tegra_fuse_writel(reg, FUSE_TIME_PGM);\r
+ }\r
+ fuse_val[0] = (0x1 & ~fuse_val[0]);\r
+ fuse_val[1] = (0x1 & ~fuse_val[1]);\r
+ fuse_cmd_write(fuse_val[0], 0);\r
+ fuse_cmd_write(fuse_val[1], 1);\r
+\r
+ fuse_power_disable();\r
+\r
+ /*\r
+ * this will allow programming of other fuses\r
+ * and the reading of the existing fuse values\r
+ */\r
+ fuse_cmd_sense();\r
+\r
+ /* Clear out all bits that have already been burned or masked out */\r
+ memcpy(data, fuse_pgm_data, sizeof(fuse_pgm_data));\r
+\r
+ for (addr = 0; addr < NFUSES; addr += 2, data++, mask++) {\r
+ reg = fuse_cmd_read(addr);\r
+ pr_debug("%d: 0x%x 0x%x 0x%x\n", addr, (u32)(*data),\r
+ ~reg, (u32)(*mask));\r
+ *data = (*data & ~reg) & *mask;\r
+ }\r
+\r
+ fuse_power_enable();\r
+\r
+ /*\r
+ * Finally loop on all fuses, program the non zero ones.\r
+ * Words 0 and 1 are written last and they contain control fuses. We\r
+ * need to invalidate after writing to a control word (with the exception\r
+ * of the master enable). This is also the reason we write them last.\r
+ */\r
+ for (i = ARRAY_SIZE(fuse_pgm_data) - 1; i >= 0; i--) {\r
+ if (tmp_fuse_pgm_data[i]) {\r
+ fuse_cmd_write(tmp_fuse_pgm_data[i], i * 2);\r
+ fuse_cmd_write(tmp_fuse_pgm_data[i], (i * 2) + 1);\r
+ }\r
+\r
+ if (i < 2) {\r
+ fuse_power_disable();\r
+ fuse_cmd_sense();\r
+ fuse_power_enable();\r
+ }\r
+ }\r
+\r
+ /* Read all data into the chip options */\r
+ tegra_fuse_writel(0x1, FUSE_PRIV2INTFC);\r
+ udelay(1);\r
+ tegra_fuse_writel(0, FUSE_PRIV2INTFC);\r
+\r
+ while (!(tegra_fuse_readl(FUSE_CTRL) & (1 << 30)));\r
+\r
+ fuse_reg_hide();\r
+ fuse_power_disable();\r
+}\r
+\r
+static int fuse_set(enum fuse_io_param io_param, u32 *param, int size)\r
+{\r
+ int i, nwords = size / sizeof(u32);\r
+ u32 *data;\r
+\r
+ if (io_param > MAX_PARAMS)\r
+ return -EINVAL;\r
+\r
+ data = (u32*)kzalloc(size, GFP_KERNEL);\r
+ if (!data) {\r
+ pr_err("failed to alloc %d bytes\n", nwords);\r
+ return -ENOMEM;\r
+ }\r
+\r
+ get_fuse(io_param, data);\r
+\r
+ for (i = 0; i < nwords; i++) {\r
+ if ((data[i] | param[i]) != param[i]) {\r
+ pr_info("hw_val: 0x%x, sw_val: 0x%x, final: 0x%x\n",\r
+ data[i], param[i], (data[i] | param[i]));\r
+ param[i] = (data[i] | param[i]);\r
+ }\r
+ }\r
+ kfree(data);\r
+ return 0;\r
+}\r
+\r
+int tegra_fuse_program(struct fuse_data *pgm_data, u32 flags)\r
+{\r
+ u32 reg;\r
+ int i = 0;\r
+\r
+ mutex_lock(&fuse_lock);\r
+ reg = tegra_fuse_readl(FUSE_DIS_PGM);\r
+ mutex_unlock(&fuse_lock);\r
+ if (reg) {\r
+ pr_err("fuse programming disabled");\r
+ return -EACCES;\r
+ }\r
+\r
+ if (fuse_odm_prod_mode() && (flags != FLAGS_ODMRSVD)) {\r
+ pr_err("reserved odm fuses are allowed in secure mode");\r
+ return -EPERM;\r
+ }\r
+\r
+ if ((flags & FLAGS_ODM_PROD_MODE) &&\r
+ (flags & (FLAGS_SBK | FLAGS_DEVKEY))) {\r
+ pr_err("odm production mode and sbk/devkey not allowed");\r
+ return -EPERM;\r
+ }\r
+\r
+ mutex_lock(&fuse_lock);\r
+ memcpy(&fuse_info, pgm_data, sizeof(fuse_info));\r
+ for_each_set_bit(i, (unsigned long *)&flags, MAX_PARAMS) {\r
+ fuse_set((u32)i, fuse_info_tbl[i].addr,\r
+ fuse_info_tbl[i].sz);\r
+ }\r
+\r
+ populate_fuse_arrs(&fuse_info, flags);\r
+ fuse_program_array(0);\r
+\r
+ /* disable program bit */\r
+ reg = 0;\r
+ set_fuse(MASTER_ENB, ®);\r
+\r
+ memset(&fuse_info, 0, sizeof(fuse_info));\r
+ mutex_unlock(&fuse_lock);\r
+\r
+ return 0;\r
+}\r
+\r
+void tegra_fuse_program_disable(void)\r
+{\r
+ mutex_lock(&fuse_lock);\r
+ tegra_fuse_writel(0x1, FUSE_DIS_PGM);\r
+ mutex_unlock(&fuse_lock);\r
+}\r
+
+static int __init tegra_fuse_program_init(void)\r
+{\r
+ mutex_init(&fuse_lock);\r
+ return 0;\r
+}\r
+\r
+module_init(tegra_fuse_program_init);\r