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