From 62f4ae5f7b3d9ae7db4796c3a26d74e45f1fd90f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 24 Nov 2010 14:28:50 -0800 Subject: [PATCH] ARM: tegra: fuse: Remove carriage returns in fuse files Change-Id: If3675c8ee7a0e0a5d3f7bd0c147054bc3796deef Signed-off-by: Colin Cross --- .../arm/mach-tegra/include/mach/tegra2_fuse.h | 208 +-- arch/arm/mach-tegra/tegra2_fuse.c | 1112 ++++++++--------- 2 files changed, 660 insertions(+), 660 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/tegra2_fuse.h b/arch/arm/mach-tegra/include/mach/tegra2_fuse.h index 510a16069a55..b5341b77a360 100644 --- a/arch/arm/mach-tegra/include/mach/tegra2_fuse.h +++ b/arch/arm/mach-tegra/include/mach/tegra2_fuse.h @@ -1,104 +1,104 @@ -/* - * 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 +/* + * 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 diff --git a/arch/arm/mach-tegra/tegra2_fuse.c b/arch/arm/mach-tegra/tegra2_fuse.c index 4af4a526c33a..c2c8ef5356c8 100644 --- a/arch/arm/mach-tegra/tegra2_fuse.c +++ b/arch/arm/mach-tegra/tegra2_fuse.c @@ -1,556 +1,556 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include - -#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); +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#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); -- 2.34.1