diff --git a/drivers/soc/tegra/fuse/Makefile b/drivers/soc/tegra/fuse/Makefile index b7d0c930..990f0c14 100644 --- a/drivers/soc/tegra/fuse/Makefile +++ b/drivers/soc/tegra/fuse/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. -obj-m += fuse-burn.o obj-m += kfuse.o diff --git a/drivers/soc/tegra/fuse/fuse-burn.c b/drivers/soc/tegra/fuse/fuse-burn.c deleted file mode 100644 index 425765c2..00000000 --- a/drivers/soc/tegra/fuse/fuse-burn.c +++ /dev/null @@ -1,1145 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#include -#include -#include - -#define TEGRA_FUSE_CTRL 0x0 -#define TEGRA_FUSE_CTRL_CMD_READ 0x1 -#define TEGRA_FUSE_CTRL_CMD_WRITE 0x2 -#define TEGRA_FUSE_CTRL_CMD_SENSE 0x3 -#define TEGRA_FUSE_CTRL_CMD_MASK 0x3 -#define TEGRA_FUSE_CTRL_STATE_IDLE 0x4 -#define TEGRA_FUSE_CTRL_STATE_MASK 0x1f -#define TEGRA_FUSE_CTRL_STATE_SHIFT 16 -#define TEGRA_FUSE_CTRL_PD BIT(26) -#define TEGRA_FUSE_CTRL_SENSE_DONE BIT(30) -#define TEGRA_FUSE_ADDR 0x4 -#define TEGRA_FUSE_RDATA 0x8 -#define TEGRA_FUSE_WDATA 0xc -#define TEGRA_FUSE_TIME_PGM2 0x1c -#define TEGRA_FUSE_PRIV2INTFC_START 0x20 -#define TEGRA_FUSE_PRIV2INTFC_SDATA 0x1 -#define TEGRA_FUSE_PRIV2INTFC_SKIP_RECORDS 0x2 -#define TEGRA_FUSE_DISABLE_REG_PROG 0x2c -#define TEGRA_FUSE_WRITE_ACCESS_SW 0x30 -#define TEGRA_FUSE_OPT_TPC_DISABLE 0x20c -#define TEGRA_FUSE_SLAM 0x84 -#define TEGRA_FUSE_SLAM_LOCK (0x1 << 31) - -#define TEGRA_FUSE_ENABLE_PRGM_OFFSET 0 -#define TEGRA_FUSE_ENABLE_PRGM_REDUND_OFFSET 1 -#define TEGRA_FUSE_BURN_MAX_FUSES 30 - -#define TEGRA_FUSE_ODM_PRODUCTION_MODE 0xa0 -#define H2_START_MACRO_BIT_INDEX 2167 -#define H2_END_MACRO_BIT_INDEX 3326 - -#define FPERM_R 0440 -#define FPERM_RW 0660 - -#define TEGRA_FUSE_SHUTDOWN_LIMIT_MODIFIER 2000 - -#define PMC_FUSE_CTRL_ENABLE_REDIRECTION (1 << 0) -#define PMC_FUSE_CTRL_PS18_LATCH_SET (1 << 8) -#define PMC_FUSE_CTRL_PS18_LATCH_CLEAR (1 << 9) - -struct fuse_burn_data { - char *name; - u32 start_offset; - u32 start_bit; - u32 size_bits; - u32 reg_offset; - bool is_redundant; - bool is_big_endian; - bool redundant_war; - struct device_attribute attr; -}; - -struct tegra_fuse_hw_feature { - bool power_down_mode; - bool mirroring_support; - bool skip_fuse_mirroring_logic; - bool hw_mutex_support; - bool has_power_switch; - int pgm_time; - int chip_id; - uint32_t fuse_ctrl_reg; - struct fuse_burn_data burn_data[TEGRA_FUSE_BURN_MAX_FUSES]; -}; - -struct tegra_fuse_burn_dev { - struct device *dev; - void __iomem *pmc_misc_reg; - void __iomem *fuse_base_reg; - struct tegra_fuse_hw_feature *hw; - struct clk *pgm_clk; - struct clk *fuse_clk; - struct tegra_bpmp *bpmp; - u32 pgm_width; - struct thermal_zone_device *tz; - s32 min_temp; - s32 max_temp; - u32 thermal_zone; -}; - -static DEFINE_MUTEX(fuse_lock); - -static u64 chip_uid; - -static struct tegra_fuse_burn_dev *s_fuse_dev; - -/******* MISC Register configuration */ -static u32 tegra_pmc_misc_readl(struct tegra_fuse_burn_dev *fuse_dev, unsigned long offset) -{ - return readl(fuse_dev->pmc_misc_reg + offset); -} - -static void tegra_pmc_misc_writel(struct tegra_fuse_burn_dev *fuse_dev, u32 value, - unsigned long offset) -{ - writel(value, fuse_dev->pmc_misc_reg + offset + offset); -} - -static void tegra_pmc_misc_fuse_disable_mirroring(struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 val; - - if (fuse_dev->hw->skip_fuse_mirroring_logic) - return; - - val = tegra_pmc_misc_readl(fuse_dev, fuse_dev->hw->fuse_ctrl_reg); - if (val & PMC_FUSE_CTRL_ENABLE_REDIRECTION) { - val &= ~PMC_FUSE_CTRL_ENABLE_REDIRECTION; - tegra_pmc_misc_writel(fuse_dev, val, - fuse_dev->hw->fuse_ctrl_reg); - } -} - -static void tegra_pmc_misc_fuse_enable_mirroring(struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 val; - - if (fuse_dev->hw->skip_fuse_mirroring_logic) - return; - - val = tegra_pmc_misc_readl(fuse_dev, fuse_dev->hw->fuse_ctrl_reg); - if (!(val & PMC_FUSE_CTRL_ENABLE_REDIRECTION)) { - val |= PMC_FUSE_CTRL_ENABLE_REDIRECTION; - tegra_pmc_misc_writel(fuse_dev, val, fuse_dev->hw->fuse_ctrl_reg); - } -} - -static void tegra_pmc_misc_fuse_control_ps18_latch_set(struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 val; - - val = tegra_pmc_misc_readl(fuse_dev, fuse_dev->hw->fuse_ctrl_reg); - val &= ~(PMC_FUSE_CTRL_PS18_LATCH_CLEAR); - tegra_pmc_misc_writel(fuse_dev, val, fuse_dev->hw->fuse_ctrl_reg); - mdelay(1); - val |= PMC_FUSE_CTRL_PS18_LATCH_SET; - tegra_pmc_misc_writel(fuse_dev, val, fuse_dev->hw->fuse_ctrl_reg); - - mdelay(1); -} - -static void tegra_pmc_misc_fuse_control_ps18_latch_clear(struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 val; - - val = tegra_pmc_misc_readl(fuse_dev, fuse_dev->hw->fuse_ctrl_reg); - val &= ~(PMC_FUSE_CTRL_PS18_LATCH_SET); - tegra_pmc_misc_writel(fuse_dev, val, fuse_dev->hw->fuse_ctrl_reg); - mdelay(1); - val |= PMC_FUSE_CTRL_PS18_LATCH_CLEAR; - tegra_pmc_misc_writel(fuse_dev, val, fuse_dev->hw->fuse_ctrl_reg); - mdelay(1); -} - -static u32 tegra_fuse_control_read(unsigned int offset, u32 *value) -{ - int err; - - err = clk_prepare_enable(s_fuse_dev->fuse_clk); - if (err < 0) { - dev_err(s_fuse_dev->dev, "failed to enable FUSE clock: %d\n", err); - return 0; - } - - *value = readl_relaxed(s_fuse_dev->fuse_base_reg + offset); - - clk_disable_unprepare(s_fuse_dev->fuse_clk); - - return *value; -} - -static int tegra_fuse_control_write(u32 value, unsigned int offset) -{ - int err; - - err = clk_prepare_enable(s_fuse_dev->fuse_clk); - if (err < 0) { - dev_err(s_fuse_dev->dev, "failed to enable FUSE clock: %d\n", err); - return -EIO; - } - - writel(value, s_fuse_dev->fuse_base_reg + offset); - - clk_disable_unprepare(s_fuse_dev->fuse_clk); - - return 0; -} - -static void fuse_state_wait_for_idle(void) -{ - u32 reg; - u32 idle; - - do { - tegra_fuse_control_read(TEGRA_FUSE_CTRL, ®); - idle = reg & (TEGRA_FUSE_CTRL_STATE_MASK - << TEGRA_FUSE_CTRL_STATE_SHIFT); - udelay(1); - } while (idle != (TEGRA_FUSE_CTRL_STATE_IDLE - << TEGRA_FUSE_CTRL_STATE_SHIFT)); -} - -static u32 fuse_cmd_read(u32 addr) -{ - u32 reg; - - fuse_state_wait_for_idle(); - tegra_fuse_control_write(addr, TEGRA_FUSE_ADDR); - tegra_fuse_control_read(TEGRA_FUSE_CTRL, ®); - reg &= ~TEGRA_FUSE_CTRL_CMD_MASK; - reg |= TEGRA_FUSE_CTRL_CMD_READ; - tegra_fuse_control_write(reg, TEGRA_FUSE_CTRL); - fuse_state_wait_for_idle(); - tegra_fuse_control_read(TEGRA_FUSE_RDATA, ®); - - return reg; -} - -static void fuse_cmd_write(u32 value, u32 addr) -{ - u32 reg; - - fuse_state_wait_for_idle(); - tegra_fuse_control_write(addr, TEGRA_FUSE_ADDR); - tegra_fuse_control_write(value, TEGRA_FUSE_WDATA); - - tegra_fuse_control_read(TEGRA_FUSE_CTRL, ®); - reg &= ~TEGRA_FUSE_CTRL_CMD_MASK; - reg |= TEGRA_FUSE_CTRL_CMD_WRITE; - tegra_fuse_control_write(reg, TEGRA_FUSE_CTRL); - fuse_state_wait_for_idle(); - fuse_cmd_read(addr); -} - -static inline int tegra_fuse_acquire_burn_lock( - struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 reg; - int num_retries = 3; - - while (num_retries--) { - tegra_fuse_control_read(TEGRA_FUSE_SLAM, ®); - - if (reg & TEGRA_FUSE_SLAM_LOCK) { - /* If mutex is still acquired after 3 retries. - * Return -EPERM. - */ - if (!num_retries) { - dev_err(fuse_dev->dev, - "fuse burn already in progress.\n"); - return -EPERM; - } - udelay(10); - continue; - } - break; - } - - /* Acquire mutex by writing 1 into the LOCK bit. */ - tegra_fuse_control_write(reg | TEGRA_FUSE_SLAM_LOCK, TEGRA_FUSE_SLAM); - dev_info(fuse_dev->dev, "acquired write lock\n"); - - return 0; -} - -static inline void tegra_fuse_release_burn_lock( - struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 reg; - - tegra_fuse_control_read(TEGRA_FUSE_SLAM, ®); - - /* Release the mutex by clearing LOCK bit. */ - if (reg & TEGRA_FUSE_SLAM_LOCK) - tegra_fuse_control_write(reg & ~TEGRA_FUSE_SLAM_LOCK, - TEGRA_FUSE_SLAM); - - dev_info(fuse_dev->dev, "released write lock\n"); -} - -static bool tegra_fuse_is_fuse_burn_allowed(struct fuse_burn_data *data) -{ - u32 reg = 0; - int ret; - - /* If odm_production_mode(security mode) fuse is burnt, then - * only allow odm reserved/lock to burn - */ - ret = tegra_fuse_readl(TEGRA_FUSE_ODM_PRODUCTION_MODE, ®); - if (!ret) { - if (reg) { - if (!strncmp(data->name, "reserved_odm", 12)) - return true; - if (!strcmp(data->name, "odm_lock")) - return true; - return false; - } - } - - return true; -} - -static int tegra_fuse_form_burn_data(struct fuse_burn_data *data, - u32 *input_data, u32 *burn_data, u32 *burn_mask) -{ - int nbits = data->size_bits; - int start_bit = data->start_bit; - int i, offset, loops; - int src_bit = 0; - u32 val; - - for (offset = 0, loops = 0; nbits > 0; offset++, nbits -= loops) { - val = *input_data; - loops = min(nbits, 32 - start_bit); - for (i = 0; i < loops; i++) { - burn_mask[offset] |= BIT(start_bit + i); - if (val & BIT(src_bit)) - burn_data[offset] |= BIT(start_bit + i); - else - burn_data[offset] &= ~BIT(start_bit + i); - src_bit++; - if (src_bit == 32) { - input_data++; - val = *input_data; - src_bit = 0; - } - } - start_bit = 0; - } - - return offset; -} - -static int tegra_fuse_get_shutdown_limit(struct tegra_fuse_burn_dev *fuse_dev, - int *shutdown_limit) -{ - struct mrq_thermal_host_to_bpmp_request req; - union mrq_thermal_bpmp_to_host_response reply; - struct tegra_bpmp_message msg; - int err = 0; - - memset(&req, 0, sizeof(req)); - memset(&reply, 0, sizeof(reply)); - - req.type = CMD_THERMAL_GET_THERMTRIP; - req.get_thermtrip.zone = fuse_dev->thermal_zone; - - memset(&msg, 0, sizeof(msg)); - msg.mrq = MRQ_THERMAL; - msg.tx.data = &req; - msg.tx.size = sizeof(req); - msg.rx.data = &reply; - msg.rx.size = sizeof(reply); - err = tegra_bpmp_transfer(fuse_dev->bpmp, &msg); - if (err) - goto out; - - *shutdown_limit = reply.get_thermtrip.thermtrip; -out: - return err; -} - -static int tegra_fuse_is_temp_under_range(struct tegra_fuse_burn_dev *fuse_dev) -{ - int temp, ret = 0; - int shutdown_limit = 0; - - /* Check if temperature is under permissible range */ - ret = thermal_zone_get_temp(fuse_dev->tz, &temp); - if (ret) - goto out; - - if (temp < fuse_dev->min_temp || - temp > fuse_dev->max_temp) { - dev_err(fuse_dev->dev, "temp-%d is not under range\n", - temp); - ret = -EPERM; - goto out; - } - - if (!fuse_dev->thermal_zone) - goto out; - - ret = tegra_fuse_get_shutdown_limit(fuse_dev, &shutdown_limit); - if (ret) { - dev_err(fuse_dev->dev, "unable to get shutdown limit: %d\n", - ret); - ret = -EPERM; - goto out; - } - - /* Check if current temperature is 2C degrees below shutdown limit*/ - if (temp > (shutdown_limit - TEGRA_FUSE_SHUTDOWN_LIMIT_MODIFIER)) { - dev_err(fuse_dev->dev, "temp-%d close to shutdown limit\n", - temp); - ret = -EPERM; - } -out: - return ret; -} - -static void tegra_fuse_set_pd(bool pd) -{ - u32 reg; - - tegra_fuse_control_read(TEGRA_FUSE_CTRL, ®); - - if (pd && !(reg & TEGRA_FUSE_CTRL_PD)) { - udelay(1); - reg |= TEGRA_FUSE_CTRL_PD; - tegra_fuse_control_write(reg, TEGRA_FUSE_CTRL); - } else if (!pd && (reg & TEGRA_FUSE_CTRL_PD)) { - reg &= ~TEGRA_FUSE_CTRL_PD; - tegra_fuse_control_write(reg, TEGRA_FUSE_CTRL); - tegra_fuse_control_read(TEGRA_FUSE_CTRL, ®); - udelay(1); - } -} - -static int tegra_fuse_pre_burn_process(struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 off_0_val, off_1_val, reg; - int ret; - - if (fuse_dev->tz) { - ret = tegra_fuse_is_temp_under_range(fuse_dev); - if (ret) - return ret; - } - - /* Check if fuse burn is disabled */ - reg = tegra_fuse_control_read(TEGRA_FUSE_DISABLE_REG_PROG, ®); - if (reg) { - dev_err(fuse_dev->dev, "Fuse register programming disabled\n"); - return -EIO; - } - - if (fuse_dev->hw->hw_mutex_support) { - ret = tegra_fuse_acquire_burn_lock(fuse_dev); - if (ret) - return ret; - } - - /* Enable fuse register write access */ - tegra_fuse_control_write(0, TEGRA_FUSE_WRITE_ACCESS_SW); - - /* Disable power down mode */ - if (fuse_dev->hw->power_down_mode) - tegra_fuse_set_pd(false); - - if (fuse_dev->pgm_width) - tegra_fuse_control_write(fuse_dev->pgm_width, - TEGRA_FUSE_TIME_PGM2); - - if (fuse_dev->hw->mirroring_support) - tegra_pmc_misc_fuse_disable_mirroring(fuse_dev); - - if (fuse_dev->hw->has_power_switch) - tegra_pmc_misc_fuse_control_ps18_latch_set(fuse_dev); - - /* Enable fuse program */ - off_0_val = fuse_cmd_read(TEGRA_FUSE_ENABLE_PRGM_OFFSET); - off_1_val = fuse_cmd_read(TEGRA_FUSE_ENABLE_PRGM_REDUND_OFFSET); - off_0_val = 0x1 & ~off_0_val; - off_1_val = 0x1 & ~off_1_val; - fuse_cmd_write(off_0_val, TEGRA_FUSE_ENABLE_PRGM_OFFSET); - fuse_cmd_write(off_1_val, TEGRA_FUSE_ENABLE_PRGM_REDUND_OFFSET); - - return 0; -} - -static void tegra_fuse_post_burn_process(struct tegra_fuse_burn_dev *fuse_dev) -{ - u32 reg; - u32 sense_done; - - /* burned fuse values can take effect without reset by below steps*/ - reg = TEGRA_FUSE_PRIV2INTFC_SDATA | TEGRA_FUSE_PRIV2INTFC_SKIP_RECORDS; - tegra_fuse_control_write(reg, TEGRA_FUSE_PRIV2INTFC_START); - fuse_state_wait_for_idle(); - do { - udelay(1); - tegra_fuse_control_read(TEGRA_FUSE_CTRL, ®); - sense_done = reg & TEGRA_FUSE_CTRL_SENSE_DONE; - } while (!sense_done); - - /* Enable power down mode */ - if (fuse_dev->hw->power_down_mode) - tegra_fuse_set_pd(true); - - if (fuse_dev->hw->has_power_switch) - tegra_pmc_misc_fuse_control_ps18_latch_clear(fuse_dev); - - if (fuse_dev->hw->mirroring_support) - tegra_pmc_misc_fuse_enable_mirroring(fuse_dev); - - if (fuse_dev->hw->hw_mutex_support) - tegra_fuse_release_burn_lock(fuse_dev); - - /* Disable fuse register write access */ - tegra_fuse_control_write(1, TEGRA_FUSE_WRITE_ACCESS_SW); -} - -static int tegra_fuse_burn_fuse(struct tegra_fuse_burn_dev *fuse_dev, - struct fuse_burn_data *fuse_data, u32 *input_data) -{ - u32 reg, burn_data[17] = {0}, burn_mask[17] = {0}; - int fuse_addr = fuse_data->start_offset; - int is_redundant = fuse_data->is_redundant; - int i; - int num_words; - int ret; - - ret = tegra_fuse_pre_burn_process(fuse_dev); - if (ret) - return ret; - - /* Form burn data */ - num_words = tegra_fuse_form_burn_data(fuse_data, input_data, - burn_data, burn_mask); - - /* Burn the fuse */ - for (i = 0; i < num_words; i++) { - reg = fuse_cmd_read(fuse_addr); - burn_data[i] = (burn_data[i] & ~reg) & burn_mask[i]; - if (burn_data[i]) { - fuse_cmd_write(burn_data[i], fuse_addr); - if (is_redundant) - fuse_cmd_write(burn_data[i], fuse_addr + 1); - } - if (is_redundant) - fuse_addr += 2; - else - fuse_addr += 1; - } - - tegra_fuse_post_burn_process(fuse_dev); - - return 0; -} - -static void tegra_fuse_get_fuse(struct tegra_fuse_burn_dev *fuse_dev, - struct fuse_burn_data *data, u32 *macro_buf) -{ - int start_bit = data->start_bit; - int nbits = data->size_bits; - int offset = data->start_offset; - bool is_redundant = data->is_redundant; - bool redundant_war = data->redundant_war; - int bit_position = 0; - int i, loops; - u32 actual_val, redun_val = 0; - - /* Disable power down mode */ - if (fuse_dev->hw->power_down_mode) - tegra_fuse_set_pd(false); - - do { - actual_val = fuse_cmd_read(offset); - if (is_redundant) - redun_val = fuse_cmd_read(offset + 1); - loops = min(nbits, 32 - start_bit); - for (i = 0; i < loops; i++) { - if (actual_val & (BIT(start_bit + i))) - *macro_buf |= BIT(bit_position); - /* If redundant WAR enable, skip redun_val */ - if (is_redundant && !redundant_war) { - if (redun_val & (BIT(start_bit + i))) - *macro_buf |= BIT(bit_position); - } - bit_position++; - if (bit_position == 32) { - macro_buf++; - bit_position = 0; - } - } - nbits -= loops; - if (is_redundant) - offset += 2; - else - offset += 1; - start_bit = 0; - } while (nbits > 0); - - /* Enable power down mode */ - if (fuse_dev->hw->power_down_mode) - tegra_fuse_set_pd(true); -} - -static ssize_t tegra_fuse_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct platform_device *pdev = container_of(dev, - struct platform_device, dev); - struct tegra_fuse_burn_dev *fuse_dev = platform_get_drvdata(pdev); - struct fuse_burn_data *data; - char str[9]; - u32 *macro_buf; - int num_words, i; - u32 val; - - data = container_of(attr, struct fuse_burn_data, attr); - num_words = DIV_ROUND_UP(data->size_bits, 32); - macro_buf = kcalloc(num_words, sizeof(*macro_buf), GFP_KERNEL); - if (!macro_buf) { - dev_err(dev, "buffer allocation failed\n"); - return -ENOMEM; - } - - mutex_lock(&fuse_lock); - tegra_fuse_get_fuse(fuse_dev, data, macro_buf); - mutex_unlock(&fuse_lock); - strcpy(buf, "0x"); - if (data->is_big_endian) { - for (i = 0; i < num_words; i++) { - val = cpu_to_be32(macro_buf[i]); - sprintf(str, "%08x", val); - strcat(buf, str); - } - } else { - while (num_words--) { - sprintf(str, "%08x", macro_buf[num_words]); - strcat(buf, str); - } - } - strcat(buf, "\n"); - kfree(macro_buf); - - return strlen(buf); -} - -static ssize_t tegra_fuse_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct platform_device *pdev = container_of(dev, - struct platform_device, dev); - struct tegra_fuse_burn_dev *fuse_dev = platform_get_drvdata(pdev); - struct fuse_burn_data *fuse_data; - int len = count; - int num_nibbles; - u32 input_data[17] = {0}; - u32 temp_data[17] = {0}; - char str[9] = {0}; - int copy_cnt, copy_idx; - int burn_idx = 0, idx; - int ret; - - fuse_data = container_of(attr, struct fuse_burn_data, attr); - if (!tegra_fuse_is_fuse_burn_allowed(fuse_data)) { - dev_err(dev, "security mode fuse is burnt, burn not allowed\n"); - return -EPERM; - } - - num_nibbles = DIV_ROUND_UP(fuse_data->size_bits, 4); - - if (*buf == 'x') { - len--; - buf++; - } - if (*buf == '0' && (*(buf + 1) == 'x' || *(buf + 1) == 'X')) { - len -= 2; - buf += 2; - } - len--; - if (len > num_nibbles) { - dev_err(dev, "input data too long, max is %d characters\n", - num_nibbles); - return -EINVAL; - } - - for (burn_idx = 0; len; burn_idx++) { - copy_idx = len > 8 ? (len - 8) : 0; - copy_cnt = len > 8 ? 8 : len; - memset(str, 0, sizeof(str)); - strncpy(str, buf + copy_idx, copy_cnt); - ret = kstrtouint(str, 16, &input_data[burn_idx]); - if (ret) - return ret; - len -= copy_cnt; - } - - if (fuse_data->is_big_endian) { - for (idx = --burn_idx, copy_cnt = 0; idx >= 0; - idx--, copy_cnt++) - temp_data[copy_cnt] = cpu_to_be32(input_data[idx]); - memcpy(input_data, temp_data, sizeof(input_data)); - } - - pm_stay_awake(fuse_dev->dev); - mutex_lock(&fuse_lock); - ret = tegra_fuse_burn_fuse(fuse_dev, fuse_data, input_data); - mutex_unlock(&fuse_lock); - pm_relax(fuse_dev->dev); - if (ret) - return ret; - - return count; -} - -#define TEGRA210_INT_CID 5 -#define TEGRA186_INT_CID 6 -#define TEGRA194_INT_CID 7 -#define TEGRA234_INT_CID 8 - -#define FUSE_OPT_VENDOR_CODE 0x100 -#define FUSE_OPT_VENDOR_CODE_MASK 0xf -#define FUSE_OPT_FAB_CODE 0x104 -#define FUSE_OPT_FAB_CODE_MASK 0x3f -#define FUSE_OPT_LOT_CODE_0 0x108 -#define FUSE_OPT_LOT_CODE_1 0x10c -#define FUSE_OPT_WAFER_ID 0x110 -#define FUSE_OPT_WAFER_ID_MASK 0x3f -#define FUSE_OPT_X_COORDINATE 0x114 -#define FUSE_OPT_X_COORDINATE_MASK 0x1ff -#define FUSE_OPT_Y_COORDINATE 0x118 -#define FUSE_OPT_Y_COORDINATE_MASK 0x1ff - -unsigned long long tegra_chip_uid(void) -{ - - u64 uid = 0ull; - u32 reg; - u32 cid; - u32 vendor; - u32 fab; - u32 lot; - u32 wafer; - u32 x; - u32 y; - u32 i; - - /* - * This used to be so much easier in prior chips. Unfortunately, there - * is no one-stop shopping for the unique id anymore. It must be - * constructed from various bits of information burned into the fuses - * during the manufacturing process. The 64-bit unique id is formed - * by concatenating several bit fields. The notation used for the - * various fields is with the UID composed - * thusly: - * - * - * - * Where: - * - * Field Bits Position Data - * ------- ---- -------- ---------------------------------------- - * CID 4 60 Chip id - * VENDOR 4 56 Vendor code - * FAB 6 50 FAB code - * LOT 26 24 Lot code (5-digit base-36-coded-decimal, - * re-encoded to 26 bits binary) - * WAFER 6 18 Wafer id - * X 9 9 Wafer X-coordinate - * Y 9 0 Wafer Y-coordinate - * ------- ---- - * Total 64 - */ - - reg = s_fuse_dev->hw->chip_id; - switch (reg) { - case TEGRA210: - cid = TEGRA210_INT_CID; - break; - case TEGRA186: - cid = TEGRA186_INT_CID; - break; - case TEGRA194: - cid = TEGRA194_INT_CID; - break; - case TEGRA234: - cid = TEGRA234_INT_CID; - break; - default: - cid = 0; - break; - }; - - tegra_fuse_readl(FUSE_OPT_VENDOR_CODE, ®); - vendor = reg & FUSE_OPT_VENDOR_CODE_MASK; - tegra_fuse_readl(FUSE_OPT_FAB_CODE, ®); - fab = reg & FUSE_OPT_FAB_CODE_MASK; - - /* Lot code must be re-encoded from a 5 digit base-36 'BCD' number - * to a binary number. - */ - lot = 0; - tegra_fuse_readl(FUSE_OPT_LOT_CODE_0, ®); - reg = reg << 2; - - for (i = 0; i < 5; ++i) { - u32 digit = (reg & 0xFC000000) >> 26; - - WARN_ON(digit >= 36); - lot *= 36; - lot += digit; - reg <<= 6; - } - - tegra_fuse_readl(FUSE_OPT_WAFER_ID, ®); - wafer = reg & FUSE_OPT_WAFER_ID_MASK; - tegra_fuse_readl(FUSE_OPT_X_COORDINATE, ®); - x = reg & FUSE_OPT_X_COORDINATE_MASK; - tegra_fuse_readl(FUSE_OPT_Y_COORDINATE, ®); - y = reg & FUSE_OPT_Y_COORDINATE_MASK; - - uid = ((unsigned long long)cid << 60ull) - | ((unsigned long long)vendor << 56ull) - | ((unsigned long long)fab << 50ull) - | ((unsigned long long)lot << 24ull) - | ((unsigned long long)wafer << 18ull) - | ((unsigned long long)x << 9ull) - | ((unsigned long long)y << 0ull); - return uid; -} - -static ssize_t tegra_fuse_read_ecid(struct device *dev, - struct device_attribute *attr, char *buf) -{ - sprintf(buf, "%llu\n", tegra_chip_uid()); - return strlen(buf); -} - -static ssize_t tegra_fuse_read_opt_tpc_disable(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u32 reg = 0; - int retval; - - retval = tegra_fuse_readl(TEGRA_FUSE_OPT_TPC_DISABLE, ®); - - if (unlikely(retval)) { - dev_err(dev, "sysfs read failed\n"); - return -EINVAL; - } - - sprintf(buf, "0x%x\n", reg); - return strlen(buf); -} - -#define FUSE_BURN_DATA(fname, m_off, sbit, size, c_off, is_red, is_be) \ - { \ - .name = #fname, \ - .start_offset = m_off, \ - .start_bit = sbit, \ - .size_bits = size, \ - .reg_offset = c_off, \ - .is_redundant = is_red, \ - .is_big_endian = is_be, \ - .redundant_war = false, \ - .attr.show = tegra_fuse_show, \ - .attr.store = tegra_fuse_store, \ - .attr.attr.name = #fname, \ - .attr.attr.mode = 0660, \ - } -#define FUSE_SYSFS_DATA(fname, show_func, store_func, _mode) \ - { \ - .name = #fname, \ - .attr.show = show_func, \ - .attr.store = store_func, \ - .attr.attr.name = #fname, \ - .attr.attr.mode = _mode, \ - } - -static struct tegra_fuse_hw_feature tegra194_fuse_chip_data = { - .power_down_mode = true, - .mirroring_support = true, - .skip_fuse_mirroring_logic = false, - .fuse_ctrl_reg = 0x10, - .hw_mutex_support = false, - .has_power_switch = true, - .pgm_time = 5, - .chip_id = TEGRA194, - .burn_data = { - FUSE_BURN_DATA(reserved_odm0, 0x2, 2, 32, 0xc8, true, false), - FUSE_BURN_DATA(reserved_odm1, 0x4, 2, 32, 0xcc, true, false), - FUSE_BURN_DATA(reserved_odm2, 0x6, 2, 32, 0xd0, true, false), - FUSE_BURN_DATA(reserved_odm3, 0x8, 2, 32, 0xd4, true, false), - FUSE_BURN_DATA(reserved_odm4, 0xa, 2, 32, 0xd8, true, false), - FUSE_BURN_DATA(reserved_odm5, 0xc, 2, 32, 0xdc, true, false), - FUSE_BURN_DATA(reserved_odm6, 0xe, 2, 32, 0xe0, true, false), - FUSE_BURN_DATA(reserved_odm7, 0x10, 2, 32, 0xe4, true, false), - FUSE_BURN_DATA(reserved_odm8, 0x16, 26, 32, 0x420, true, false), - FUSE_BURN_DATA(reserved_odm9, 0x18, 26, 32, 0x424, true, false), - FUSE_BURN_DATA(reserved_odm10, 0x1a, 26, 32, 0x428, true, false), - FUSE_BURN_DATA(reserved_odm11, 0x1c, 26, 32, 0x42c, true, false), - FUSE_BURN_DATA(odm_lock, 0, 6, 4, 0x8, true, false), - FUSE_BURN_DATA(arm_jtag_disable, 0x0, 12, 1, 0xb8, true, false), - FUSE_BURN_DATA(odm_production_mode, 0, 11, 1, 0xa0, true, false), - FUSE_BURN_DATA(secure_boot_key, 0x61, 1, 128, 0xa4, true, true), - FUSE_BURN_DATA(public_key, 0x59, 1, 256, 0x64, true, true), - FUSE_BURN_DATA(boot_security_info, 0x66, 21, 16, 0x168, true, false), - FUSE_BURN_DATA(debug_authentication, 0, 20, 5, 0x1e4, true, false), - FUSE_BURN_DATA(odm_info, 0x67, 5, 16, 0x19c, false, false), - FUSE_BURN_DATA(pdi, 0x40, 17, 64, 0x300, false, false), - FUSE_BURN_DATA(opt_customer_optin_fuse, 0x7e, 6, 1, 0x4a8, - false, false), - FUSE_BURN_DATA(odmid, 0x7b, 30, 64, 0x308, false, false), - FUSE_BURN_DATA(kek0, 0x6f, 30, 128, 0x2c0, false, true), - FUSE_BURN_DATA(kek1, 0x73, 30, 128, 0x2d0, false, true), - FUSE_BURN_DATA(kek2, 0x77, 30, 128, 0x2e0, false, true), - FUSE_SYSFS_DATA(ecid, tegra_fuse_read_ecid, NULL, FPERM_R), - FUSE_SYSFS_DATA(opt_tpc_disable, - tegra_fuse_read_opt_tpc_disable, NULL, FPERM_R), - {}, - }, -}; - -static struct tegra_fuse_hw_feature tegra234_fuse_chip_data = { - .power_down_mode = true, - .mirroring_support = true, - .skip_fuse_mirroring_logic = true, - .fuse_ctrl_reg = 0x10, - .hw_mutex_support = true, - .has_power_switch = true, - .pgm_time = 5, - .chip_id = TEGRA234, - .burn_data = { - FUSE_BURN_DATA(reserved_odm0, 0x2, 2, 32, 0xc8, true, false), - FUSE_BURN_DATA(reserved_odm1, 0x4, 2, 32, 0xcc, true, false), - FUSE_BURN_DATA(reserved_odm2, 0x6, 2, 32, 0xd0, true, false), - FUSE_BURN_DATA(reserved_odm3, 0x10, 0, 32, 0xd4, true, false), - FUSE_BURN_DATA(reserved_odm4, 0xc, 0, 32, 0xd8, true, false), - FUSE_BURN_DATA(reserved_odm5, 0xe, 0, 32, 0xdc, true, false), - FUSE_BURN_DATA(reserved_odm6, 0xe, 2, 32, 0xe0, true, false), - FUSE_BURN_DATA(reserved_odm7, 0x10, 2, 32, 0xe4, true, false), - FUSE_BURN_DATA(odm_lock, 0, 5, 4, 0x8, true, false), - FUSE_BURN_DATA(public_key, 0xbc, 21, 512, 0x64, false, true), - FUSE_BURN_DATA(boot_security_info, 0xc7, 0, 32, 0x168, false, false), - FUSE_BURN_DATA(debug_authentication, 0, 16, 5, 0x1e4, true, false), - FUSE_BURN_DATA(odm_info, 0xc7, 9, 16, 0x19c, false, false), - FUSE_BURN_DATA(pdi, 0x62, 29, 64, 0x300, false, false), - FUSE_BURN_DATA(opt_customer_optin_fuse, 0xca, 7, 1, 0x4a8, - false, false), - FUSE_BURN_DATA(odmid, 0xc9, 0, 64, 0x308, false, false), - FUSE_SYSFS_DATA(ecid, tegra_fuse_read_ecid, NULL, FPERM_R), - FUSE_SYSFS_DATA(opt_tpc_disable, - tegra_fuse_read_opt_tpc_disable, NULL, FPERM_R), - {}, - }, -}; - -static const struct of_device_id tegra_fuse_burn_match[] = { - { - .compatible = "nvidia,tegra194-efuse-burn", - .data = &tegra194_fuse_chip_data, - }, { - .compatible = "nvidia,tegra234-efuse-burn", - .data = &tegra234_fuse_chip_data, - }, {}, -}; - -static void tegra_fuse_parse_dt(struct tegra_fuse_burn_dev *fuse_dev, - struct device_node *np) -{ - int n_entries; - - n_entries = of_property_count_u32_elems(np, "nvidia,temp-range"); - if (n_entries == 2) { - of_property_read_u32_index(np, "nvidia,temp-range", - 0, &fuse_dev->min_temp); - of_property_read_u32_index(np, "nvidia,temp-range", - 1, &fuse_dev->max_temp); - } else { - dev_dbg(fuse_dev->dev, "invalid fuse-temp range entries\n"); - } -} - -static int tegra_fuse_burn_probe(struct platform_device *pdev) -{ - struct tegra_fuse_burn_dev *fuse_dev; - struct device_node *np = pdev->dev.of_node; - struct resource *res_misc_reg; - struct resource *fuse_base_reg; - const char *tz_type; - int i, ret; - - fuse_dev = devm_kzalloc(&pdev->dev, sizeof(*fuse_dev), GFP_KERNEL); - if (!fuse_dev) - return -ENOMEM; - - fuse_dev->hw = (struct tegra_fuse_hw_feature *)of_device_get_match_data( - &pdev->dev); - if (!fuse_dev->hw) { - dev_err(&pdev->dev, "No hw data provided\n"); - return -EINVAL; - } - - res_misc_reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res_misc_reg) { - dev_err(&pdev->dev, "MISC Register not found\n"); - return -ENOENT; - } - - fuse_base_reg = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!fuse_base_reg) { - dev_err(&pdev->dev, "FUSE Base Register not found\n"); - return -ENOENT; - } - - fuse_dev->pmc_misc_reg = devm_ioremap_resource(&pdev->dev, res_misc_reg); - if (IS_ERR(fuse_dev->pmc_misc_reg)) { - dev_err(&pdev->dev, "Cannot request memregion/iomap misc_reg\n"); - return PTR_ERR(fuse_dev->pmc_misc_reg); - } - - fuse_dev->fuse_base_reg = devm_ioremap_resource(&pdev->dev, fuse_base_reg); - if (IS_ERR(fuse_dev->fuse_base_reg)) { - dev_err(&pdev->dev, "Cannot request memregion/iomap fuse base reg\n"); - return PTR_ERR(fuse_dev->fuse_base_reg); - } - - fuse_dev->fuse_clk = devm_clk_get(&pdev->dev, "fuse-clk"); - if (IS_ERR(fuse_dev->fuse_clk)) { - if (PTR_ERR(fuse_dev->fuse_clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get fuse-clk err\n"); - return PTR_ERR(fuse_dev->fuse_clk); - } - - fuse_dev->bpmp = tegra_bpmp_get(&pdev->dev); - if (IS_ERR(fuse_dev->bpmp)) { - dev_err(&pdev->dev, "Not able to get BPMP handle\n"); - return PTR_ERR(fuse_dev->bpmp); - } - - /* Since T210, we support the bit offset and we will have redundant fuse - * for some of the fuse. But one exception(AID fuse) is not redundant. - * Unfortunately, some legacy kernel(eg. Kernel v3.10) will assume the - * AID fuse as redundant and read the fuse value in redundant way, from - * address X and address X+2, which should be address X and address X+1 - * instead. - * To align the platform we release with legacy kernel and client, add - * the "redundant-aid-war" for reading the same value as in the past. - * Or the inconsistent value may cause an issue in some case. - */ - if (of_property_read_bool(np, "nvidia,redundant-aid-war")) { - for (i = 0; i < ARRAY_SIZE(fuse_dev->hw->burn_data) && - fuse_dev->hw->burn_data[i].name != NULL; i++) - if (!strcmp(fuse_dev->hw->burn_data[i].name, "aid")) { - fuse_dev->hw->burn_data[i].is_redundant = true; - fuse_dev->hw->burn_data[i].redundant_war = - true; - } - } - - fuse_dev->pgm_clk = devm_clk_get(&pdev->dev, "pgm-clk"); - if (IS_ERR(fuse_dev->pgm_clk)) { - if (PTR_ERR(fuse_dev->pgm_clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get clk_m err\n"); - ret = PTR_ERR(fuse_dev->pgm_clk); - goto bpmp_put; - } - fuse_dev->pgm_width = DIV_ROUND_UP( - clk_get_rate(fuse_dev->pgm_clk) * - fuse_dev->hw->pgm_time, - 1000 * 1000); - - fuse_dev->dev = &pdev->dev; - platform_set_drvdata(pdev, fuse_dev); - mutex_init(&fuse_lock); - for (i = 0; i < ARRAY_SIZE(fuse_dev->hw->burn_data) && - fuse_dev->hw->burn_data[i].name != NULL; i++) { - ret = sysfs_create_file(&pdev->dev.kobj, - &fuse_dev->hw->burn_data[i].attr.attr); - if (ret) { - dev_err(&pdev->dev, "sysfs create failed %d\n", ret); - goto bpmp_put; - } - } - WARN(sysfs_create_link(&platform_bus.kobj, &pdev->dev.kobj, - "tegra-fuse"), "Unable to create symlink\n"); - - device_init_wakeup(fuse_dev->dev, true); - - if (of_property_read_u32(np, "thermal-zone", &fuse_dev->thermal_zone)) - dev_info(fuse_dev->dev, "shutdown limit check disabled\n"); - - ret = of_property_read_string(np, "thermal-zone-type", &tz_type); - if (!ret) { - fuse_dev->tz = thermal_zone_get_zone_by_name(tz_type); - if (IS_ERR(fuse_dev->tz)) - dev_dbg(&pdev->dev, "temp zone type %s not available\n", tz_type); - else - tegra_fuse_parse_dt(fuse_dev, np); - } - - dev_info(&pdev->dev, "Fuse burn driver initialized\n"); - - s_fuse_dev = fuse_dev; - - return 0; - -bpmp_put: - tegra_bpmp_put(fuse_dev->bpmp); - return ret; -} - -static struct platform_driver tegra_fuse_burn_driver = { - .driver = { - .name = "tegra-fuse-burn", - .of_match_table = tegra_fuse_burn_match, - }, - .probe = tegra_fuse_burn_probe, -}; -module_platform_driver(tegra_fuse_burn_driver); - -static int get_chip_uid(char *val, const struct kernel_param *kp) -{ - chip_uid = tegra_chip_uid(); - return param_get_ulong(val, kp); -} - -static struct kernel_param_ops tegra_chip_uid_ops = { - .get = get_chip_uid, -}; -module_param_cb(tegra_chip_uid, &tegra_chip_uid_ops, &chip_uid, 0444); - -MODULE_AUTHOR("Laxman Dewangan "); -MODULE_DESCRIPTION("NVIDIA Tegra Fuse burn driver"); -MODULE_LICENSE("GPL v2"); -