diff options
author | Ian Wisbon <ian.wisbon@timesys.com> | 2011-02-10 17:15:15 -0500 |
---|---|---|
committer | Ian Wisbon <ian.wisbon@timesys.com> | 2011-02-14 14:29:06 -0500 |
commit | 14a4057959f8ee0a2249eb2abd64fd6b1f571d98 (patch) | |
tree | 6df655a665a5a56bd6b7cae5e2689fc5a1b6c965 /arch/arm/mach-mx23 | |
parent | 0eb553bf96e2c990d3bfccaa07da0863624c89ab (diff) |
Digi Release Code - 02142011 Missing Files Fix2.6.31-digi-201102141503
Diffstat (limited to 'arch/arm/mach-mx23')
-rw-r--r-- | arch/arm/mach-mx23/include/mach/regs-ocotp.h | 311 | ||||
-rw-r--r-- | arch/arm/mach-mx23/otp.c | 437 |
2 files changed, 748 insertions, 0 deletions
diff --git a/arch/arm/mach-mx23/include/mach/regs-ocotp.h b/arch/arm/mach-mx23/include/mach/regs-ocotp.h new file mode 100644 index 000000000000..b0313dd67f93 --- /dev/null +++ b/arch/arm/mach-mx23/include/mach/regs-ocotp.h @@ -0,0 +1,311 @@ +/* + * Freescale OCOTP Register Definitions + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This file is created by xml file. Don't Edit it. + * + * Xml Revision: 1.21 + * Template revision: 26195 + */ + +#ifndef __ARCH_ARM___OCOTP_H +#define __ARCH_ARM___OCOTP_H + + +#define HW_OCOTP_CTRL (0x00000000) +#define HW_OCOTP_CTRL_SET (0x00000004) +#define HW_OCOTP_CTRL_CLR (0x00000008) +#define HW_OCOTP_CTRL_TOG (0x0000000c) + +#define BP_OCOTP_CTRL_WR_UNLOCK 16 +#define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000 +#define BF_OCOTP_CTRL_WR_UNLOCK(v) \ + (((v) << 16) & BM_OCOTP_CTRL_WR_UNLOCK) +#define BV_OCOTP_CTRL_WR_UNLOCK__KEY 0x3E77 +#define BP_OCOTP_CTRL_RSRVD2 14 +#define BM_OCOTP_CTRL_RSRVD2 0x0000C000 +#define BF_OCOTP_CTRL_RSRVD2(v) \ + (((v) << 14) & BM_OCOTP_CTRL_RSRVD2) +#define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00002000 +#define BM_OCOTP_CTRL_RD_BANK_OPEN 0x00001000 +#define BP_OCOTP_CTRL_RSRVD1 10 +#define BM_OCOTP_CTRL_RSRVD1 0x00000C00 +#define BF_OCOTP_CTRL_RSRVD1(v) \ + (((v) << 10) & BM_OCOTP_CTRL_RSRVD1) +#define BM_OCOTP_CTRL_ERROR 0x00000200 +#define BM_OCOTP_CTRL_BUSY 0x00000100 +#define BP_OCOTP_CTRL_RSRVD0 5 +#define BM_OCOTP_CTRL_RSRVD0 0x000000E0 +#define BF_OCOTP_CTRL_RSRVD0(v) \ + (((v) << 5) & BM_OCOTP_CTRL_RSRVD0) +#define BP_OCOTP_CTRL_ADDR 0 +#define BM_OCOTP_CTRL_ADDR 0x0000001F +#define BF_OCOTP_CTRL_ADDR(v) \ + (((v) << 0) & BM_OCOTP_CTRL_ADDR) + +#define HW_OCOTP_DATA (0x00000010) + +#define BP_OCOTP_DATA_DATA 0 +#define BM_OCOTP_DATA_DATA 0xFFFFFFFF +#define BF_OCOTP_DATA_DATA(v) (v) + +/* + * multi-register-define name HW_OCOTP_CUSTn + * base 0x00000020 + * count 4 + * offset 0x10 + */ +#define HW_OCOTP_CUSTn(n) (0x00000020 + (n) * 0x10) +#define BP_OCOTP_CUSTn_BITS 0 +#define BM_OCOTP_CUSTn_BITS 0xFFFFFFFF +#define BF_OCOTP_CUSTn_BITS(v) (v) + +/* + * multi-register-define name HW_OCOTP_CRYPTOn + * base 0x00000060 + * count 4 + * offset 0x10 + */ +#define HW_OCOTP_CRYPTOn(n) (0x00000060 + (n) * 0x10) +#define BP_OCOTP_CRYPTOn_BITS 0 +#define BM_OCOTP_CRYPTOn_BITS 0xFFFFFFFF +#define BF_OCOTP_CRYPTOn_BITS(v) (v) + +/* + * multi-register-define name HW_OCOTP_HWCAPn + * base 0x000000A0 + * count 6 + * offset 0x10 + */ +#define HW_OCOTP_HWCAPn(n) (0x000000a0 + (n) * 0x10) +#define BP_OCOTP_HWCAPn_BITS 0 +#define BM_OCOTP_HWCAPn_BITS 0xFFFFFFFF +#define BF_OCOTP_HWCAPn_BITS(v) (v) + +#define HW_OCOTP_SWCAP (0x00000100) + +#define BP_OCOTP_SWCAP_BITS 0 +#define BM_OCOTP_SWCAP_BITS 0xFFFFFFFF +#define BF_OCOTP_SWCAP_BITS(v) (v) + +#define HW_OCOTP_CUSTCAP (0x00000110) + +#define BM_OCOTP_CUSTCAP_CUST_DISABLE_WMADRM9 0x80000000 +#define BM_OCOTP_CUSTCAP_CUST_DISABLE_JANUSDRM10 0x40000000 +#define BP_OCOTP_CUSTCAP_RSRVD1 5 +#define BM_OCOTP_CUSTCAP_RSRVD1 0x3FFFFFE0 +#define BF_OCOTP_CUSTCAP_RSRVD1(v) \ + (((v) << 5) & BM_OCOTP_CUSTCAP_RSRVD1) +#define BM_OCOTP_CUSTCAP_ENABLE_SJTAG_12MA_DRIVE 0x00000010 +#define BM_OCOTP_CUSTCAP_USE_PARALLEL_JTAG 0x00000008 +#define BM_OCOTP_CUSTCAP_RTC_XTAL_32768_PRESENT 0x00000004 +#define BM_OCOTP_CUSTCAP_RTC_XTAL_32000_PRESENT 0x00000002 +#define BM_OCOTP_CUSTCAP_RSRVD0 0x00000001 + +#define HW_OCOTP_LOCK (0x00000120) + +#define BM_OCOTP_LOCK_ROM7 0x80000000 +#define BM_OCOTP_LOCK_ROM6 0x40000000 +#define BM_OCOTP_LOCK_ROM5 0x20000000 +#define BM_OCOTP_LOCK_ROM4 0x10000000 +#define BM_OCOTP_LOCK_ROM3 0x08000000 +#define BM_OCOTP_LOCK_ROM2 0x04000000 +#define BM_OCOTP_LOCK_ROM1 0x02000000 +#define BM_OCOTP_LOCK_ROM0 0x01000000 +#define BM_OCOTP_LOCK_HWSW_SHADOW_ALT 0x00800000 +#define BM_OCOTP_LOCK_CRYPTODCP_ALT 0x00400000 +#define BM_OCOTP_LOCK_CRYPTOKEY_ALT 0x00200000 +#define BM_OCOTP_LOCK_PIN 0x00100000 +#define BM_OCOTP_LOCK_OPS 0x00080000 +#define BM_OCOTP_LOCK_UN2 0x00040000 +#define BM_OCOTP_LOCK_UN1 0x00020000 +#define BM_OCOTP_LOCK_UN0 0x00010000 +#define BP_OCOTP_LOCK_UNALLOCATED 11 +#define BM_OCOTP_LOCK_UNALLOCATED 0x0000F800 +#define BF_OCOTP_LOCK_UNALLOCATED(v) \ + (((v) << 11) & BM_OCOTP_LOCK_UNALLOCATED) +#define BM_OCOTP_LOCK_ROM_SHADOW 0x00000400 +#define BM_OCOTP_LOCK_CUSTCAP 0x00000200 +#define BM_OCOTP_LOCK_HWSW 0x00000100 +#define BM_OCOTP_LOCK_CUSTCAP_SHADOW 0x00000080 +#define BM_OCOTP_LOCK_HWSW_SHADOW 0x00000040 +#define BM_OCOTP_LOCK_CRYPTODCP 0x00000020 +#define BM_OCOTP_LOCK_CRYPTOKEY 0x00000010 +#define BM_OCOTP_LOCK_CUST3 0x00000008 +#define BM_OCOTP_LOCK_CUST2 0x00000004 +#define BM_OCOTP_LOCK_CUST1 0x00000002 +#define BM_OCOTP_LOCK_CUST0 0x00000001 + +/* + * multi-register-define name HW_OCOTP_OPSn + * base 0x00000130 + * count 4 + * offset 0x10 + */ +#define HW_OCOTP_OPSn(n) (0x00000130 + (n) * 0x10) +#define BP_OCOTP_OPSn_BITS 0 +#define BM_OCOTP_OPSn_BITS 0xFFFFFFFF +#define BF_OCOTP_OPSn_BITS(v) (v) + +/* + * multi-register-define name HW_OCOTP_UNn + * base 0x00000170 + * count 3 + * offset 0x10 + */ +#define HW_OCOTP_UNn(n) (0x00000170 + (n) * 0x10) +#define BP_OCOTP_UNn_BITS 0 +#define BM_OCOTP_UNn_BITS 0xFFFFFFFF +#define BF_OCOTP_UNn_BITS(v) (v) + +#define HW_OCOTP_ROM0 (0x000001a0) + +#define BP_OCOTP_ROM0_BOOT_MODE 24 +#define BM_OCOTP_ROM0_BOOT_MODE 0xFF000000 +#define BF_OCOTP_ROM0_BOOT_MODE(v) \ + (((v) << 24) & BM_OCOTP_ROM0_BOOT_MODE) +#define BM_OCOTP_ROM0_ENABLE_PJTAG_12MA_DRIVE 0x00800000 +#define BM_OCOTP_ROM0_USE_PARALLEL_JTAG 0x00400000 +#define BP_OCOTP_ROM0_SD_POWER_GATE_GPIO 20 +#define BM_OCOTP_ROM0_SD_POWER_GATE_GPIO 0x00300000 +#define BF_OCOTP_ROM0_SD_POWER_GATE_GPIO(v) \ + (((v) << 20) & BM_OCOTP_ROM0_SD_POWER_GATE_GPIO) +#define BP_OCOTP_ROM0_SD_POWER_UP_DELAY 14 +#define BM_OCOTP_ROM0_SD_POWER_UP_DELAY 0x000FC000 +#define BF_OCOTP_ROM0_SD_POWER_UP_DELAY(v) \ + (((v) << 14) & BM_OCOTP_ROM0_SD_POWER_UP_DELAY) +#define BP_OCOTP_ROM0_SD_BUS_WIDTH 12 +#define BM_OCOTP_ROM0_SD_BUS_WIDTH 0x00003000 +#define BF_OCOTP_ROM0_SD_BUS_WIDTH(v) \ + (((v) << 12) & BM_OCOTP_ROM0_SD_BUS_WIDTH) +#define BP_OCOTP_ROM0_SSP_SCK_INDEX 8 +#define BM_OCOTP_ROM0_SSP_SCK_INDEX 0x00000F00 +#define BF_OCOTP_ROM0_SSP_SCK_INDEX(v) \ + (((v) << 8) & BM_OCOTP_ROM0_SSP_SCK_INDEX) +#define BM_OCOTP_ROM0_RSRVD3 0x00000080 +#define BM_OCOTP_ROM0_DISABLE_SPI_NOR_FAST_ READ 0x00000040 +#define BM_OCOTP_ROM0_ENABLE_USB_BOOT_SERIAL_NUM 0x00000020 +#define BM_OCOTP_ROM0_ENABLE_UNENCRYPTED_ BOOT 0x00000010 +#define BM_OCOTP_ROM0_SD_MBR_BOOT 0x00000008 +#define BM_OCOTP_ROM0_RSRVD2 0x00000004 +#define BM_OCOTP_ROM0_RSRVD1 0x00000002 +#define BM_OCOTP_ROM0_RSRVD0 0x00000001 + +#define HW_OCOTP_ROM1 (0x000001b0) + +#define BP_OCOTP_ROM1_RSRVD1 30 +#define BM_OCOTP_ROM1_RSRVD1 0xC0000000 +#define BF_OCOTP_ROM1_RSRVD1(v) \ + (((v) << 30) & BM_OCOTP_ROM1_RSRVD1) +#define BP_OCOTP_ROM1_USE_ALT_GPMI_RDY3 28 +#define BM_OCOTP_ROM1_USE_ALT_GPMI_RDY3 0x30000000 +#define BF_OCOTP_ROM1_USE_ALT_GPMI_RDY3(v) \ + (((v) << 28) & BM_OCOTP_ROM1_USE_ALT_GPMI_RDY3) +#define BP_OCOTP_ROM1_USE_ALT_GPMI_CE3 26 +#define BM_OCOTP_ROM1_USE_ALT_GPMI_CE3 0x0C000000 +#define BF_OCOTP_ROM1_USE_ALT_GPMI_CE3(v) \ + (((v) << 26) & BM_OCOTP_ROM1_USE_ALT_GPMI_CE3) +#define BM_OCOTP_ROM1_USE_ALT_GPMI_RDY2 0x02000000 +#define BM_OCOTP_ROM1_USE_ALT_GPMI_CE2 0x01000000 +#define BM_OCOTP_ROM1_ENABLE_NAND3_CE_RDY_PULLUP 0x00800000 +#define BM_OCOTP_ROM1_ENABLE_NAND2_CE_RDY_PULLUP 0x00400000 +#define BM_OCOTP_ROM1_ENABLE_NAND1_CE_RDY_PULLUP 0x00200000 +#define BM_OCOTP_ROM1_ENABLE_NAND0_CE_RDY_PULLUP 0x00100000 +#define BM_OCOTP_ROM1_UNTOUCH_INTERNAL_SSP_PULLUP 0x00080000 +#define BM_OCOTP_ROM1_SSP2_EXT_PULLUP 0x00040000 +#define BM_OCOTP_ROM1_SSP1_EXT_PULLUP 0x00020000 +#define BM_OCOTP_ROM1_SD_INCREASE_INIT_SEQ_TIME 0x00010000 +#define BM_OCOTP_ROM1_SD_INIT_SEQ_2_ENABLE 0x00008000 +#define BM_OCOTP_ROM1_SD_CMD0_DISABLE 0x00004000 +#define BM_OCOTP_ROM1_SD_INIT_SEQ_1_DISABLE 0x00002000 +#define BM_OCOTP_ROM1_USE_ALT_SSP1_DATA4_7 0x00001000 +#define BP_OCOTP_ROM1_BOOT_SEARCH_COUNT 8 +#define BM_OCOTP_ROM1_BOOT_SEARCH_COUNT 0x00000F00 +#define BF_OCOTP_ROM1_BOOT_SEARCH_COUNT(v) \ + (((v) << 8) & BM_OCOTP_ROM1_BOOT_SEARCH_COUNT) +#define BP_OCOTP_ROM1_RSRVD0 3 +#define BM_OCOTP_ROM1_RSRVD0 0x000000F8 +#define BF_OCOTP_ROM1_RSRVD0(v) \ + (((v) << 3) & BM_OCOTP_ROM1_RSRVD0) +#define BP_OCOTP_ROM1_NUMBER_OF_NANDS 0 +#define BM_OCOTP_ROM1_NUMBER_OF_NANDS 0x00000007 +#define BF_OCOTP_ROM1_NUMBER_OF_NANDS(v) \ + (((v) << 0) & BM_OCOTP_ROM1_NUMBER_OF_NANDS) + +#define HW_OCOTP_ROM2 (0x000001c0) + +#define BP_OCOTP_ROM2_USB_VID 16 +#define BM_OCOTP_ROM2_USB_VID 0xFFFF0000 +#define BF_OCOTP_ROM2_USB_VID(v) \ + (((v) << 16) & BM_OCOTP_ROM2_USB_VID) +#define BP_OCOTP_ROM2_USB_PID 0 +#define BM_OCOTP_ROM2_USB_PID 0x0000FFFF +#define BF_OCOTP_ROM2_USB_PID(v) \ + (((v) << 0) & BM_OCOTP_ROM2_USB_PID) + +#define HW_OCOTP_ROM3 (0x000001d0) + +#define BP_OCOTP_ROM3_RSRVD1 10 +#define BM_OCOTP_ROM3_RSRVD1 0xFFFFFC00 +#define BF_OCOTP_ROM3_RSRVD1(v) \ + (((v) << 10) & BM_OCOTP_ROM3_RSRVD1) +#define BP_OCOTP_ROM3_RSRVD0 0 +#define BM_OCOTP_ROM3_RSRVD0 0x000003FF +#define BF_OCOTP_ROM3_RSRVD0(v) \ + (((v) << 0) & BM_OCOTP_ROM3_RSRVD0) + +#define HW_OCOTP_ROM4 (0x000001e0) + +#define BP_OCOTP_ROM4_BITS 0 +#define BM_OCOTP_ROM4_BITS 0xFFFFFFFF +#define BF_OCOTP_ROM4_BITS(v) (v) + +#define HW_OCOTP_ROM5 (0x000001f0) + +#define BP_OCOTP_ROM5_BITS 0 +#define BM_OCOTP_ROM5_BITS 0xFFFFFFFF +#define BF_OCOTP_ROM5_BITS(v) (v) + +#define HW_OCOTP_ROM6 (0x00000200) + +#define BP_OCOTP_ROM6_BITS 0 +#define BM_OCOTP_ROM6_BITS 0xFFFFFFFF +#define BF_OCOTP_ROM6_BITS(v) (v) + +#define HW_OCOTP_ROM7 (0x00000210) + +#define BP_OCOTP_ROM7_BITS 0 +#define BM_OCOTP_ROM7_BITS 0xFFFFFFFF +#define BF_OCOTP_ROM7_BITS(v) (v) + +#define HW_OCOTP_VERSION (0x00000220) + +#define BP_OCOTP_VERSION_MAJOR 24 +#define BM_OCOTP_VERSION_MAJOR 0xFF000000 +#define BF_OCOTP_VERSION_MAJOR(v) \ + (((v) << 24) & BM_OCOTP_VERSION_MAJOR) +#define BP_OCOTP_VERSION_MINOR 16 +#define BM_OCOTP_VERSION_MINOR 0x00FF0000 +#define BF_OCOTP_VERSION_MINOR(v) \ + (((v) << 16) & BM_OCOTP_VERSION_MINOR) +#define BP_OCOTP_VERSION_STEP 0 +#define BM_OCOTP_VERSION_STEP 0x0000FFFF +#define BF_OCOTP_VERSION_STEP(v) \ + (((v) << 0) & BM_OCOTP_VERSION_STEP) +#endif /* __ARCH_ARM___OCOTP_H */ diff --git a/arch/arm/mach-mx23/otp.c b/arch/arm/mach-mx23/otp.c new file mode 100644 index 000000000000..7bec45f3754c --- /dev/null +++ b/arch/arm/mach-mx23/otp.c @@ -0,0 +1,437 @@ +/* + * Unique ID manipulation: Freescale STMP378X OTP bits read/write procedures + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kobject.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/fcntl.h> +#include <linux/mutex.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/unique-id.h> +#include <mach/regs-ocotp.h> +#include <mach/regs-power.h> +#include <mach/mx23.h> + +static DEFINE_MUTEX(otp_mutex); +static unsigned otp_mode; +static unsigned long otp_hclk_saved; +static u32 otp_voltage_saved; + +static int otp_full; /* = 0. By default, show/set only customer bits */ +#define OTP_USER_OFFSET 0 +#define OTP_USER_SIZE 4 + +#define REGS_OCOTP_BASE (IO_ADDRESS(OCOTP_PHYS_ADDR)) +#define BF(value, field) (((value) << BP_##field) & BM_##field) +/** + * otp_wait_busy - wait for completion of operation + * + * @flags: flags that should be clear in addition to _BUSY and _ERROR + * + * Returns 0 on success or -ETIMEDOUT on error + **/ +static int otp_wait_busy(u32 flags) +{ + int count; + u32 c; + + for (count = 10000; count >= 0; count--) { + c = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL); + if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags))) + break; + cpu_relax(); + } + if (count < 0) + return -ETIMEDOUT; + return 0; +} + +/** + * otp_open - open OTP bits for read or write access + * + * @mode: either O_RDONLY or O_WRONLY + * + * Returns 0 on success, error code otherwise + **/ +static int otp_open(int mode) +{ + int r; + struct clk *hclk; + int err; + + if (!mutex_trylock(&otp_mutex)) { + printk(KERN_ERR"%s: already opened\n", __func__); + return -EAGAIN; + } + + if (mode == O_RDONLY) { + pr_debug("%s: read-only mode\n", __func__); + + r = otp_wait_busy(0); + if (r) { + err = -ETIMEDOUT; + goto out; + } + + /* 2. Set RD_BANK_OPEN */ + __raw_writel(BM_OCOTP_CTRL_RD_BANK_OPEN, REGS_OCOTP_BASE + HW_OCOTP_CTRL_SET); + udelay(10); + + otp_wait_busy(0); + } + + else if (mode == O_WRONLY) { + pr_debug("%s: write-only mode\n", __func__); + hclk = clk_get(NULL, "hclk"); + if (IS_ERR(hclk)) { + err = PTR_ERR(hclk); + goto out; + } + + /* + WARNING ACHTUNG UWAGA + + the code below changes HCLK clock rate to 24M. This is + required to write OTP bits (7.2.2 in STMP378x Target + Specification), and might affect LCD operation, for example. + Moreover, this hacky code changes VDDIO to 2.8V; and resto- + res it only on otp_close(). This may affect... anything. + + You are warned now. + */ + otp_hclk_saved = clk_get_rate(hclk); + clk_set_rate(hclk, 24000); + /* Set the voltage to 2.8V */ + otp_voltage_saved = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + __raw_writel( + (otp_voltage_saved & ~BM_POWER_VDDIOCTRL_TRG) | 0x00, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + r = otp_wait_busy(BM_OCOTP_CTRL_RD_BANK_OPEN); + if (r < 0) { + __raw_writel(otp_voltage_saved, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + clk_set_rate(hclk, otp_hclk_saved); + clk_put(hclk); + err = -ETIMEDOUT; + goto out; + } + + clk_put(hclk); + } + + else { + pr_debug("%s: unknown mode '%d'\n", __func__, mode); + err = -EINVAL; + goto out; + } + + otp_mode = mode; + return 0; +out: + mutex_unlock(&otp_mutex); + pr_debug("%s: status %d\n", __func__, err); + return err; +} + +/** + * otp_close - close the OTP bits after opening by otp_open + **/ +static void otp_close(void) +{ + struct clk *hclk; + + if (!mutex_is_locked(&otp_mutex)) { + printk(KERN_ERR"%s: wasn't opened\n", __func__); + return; + } + + if (otp_mode == O_RDONLY) { + /* 5. clear RD_BANK_OPEN */ + __raw_writel(BM_OCOTP_CTRL_RD_BANK_OPEN, REGS_OCOTP_BASE + HW_OCOTP_CTRL_CLR); + } + + else if (otp_mode == O_WRONLY) { + hclk = clk_get(NULL, "hclk"); + clk_set_rate(hclk, otp_hclk_saved); + __raw_writel(otp_voltage_saved, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + otp_wait_busy(0); + __raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS, REGS_OCOTP_BASE + HW_OCOTP_CTRL_SET); + otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS); + } + + else { + return; /* -EINVAL. Who does really check close? */ + } + + otp_mode = 0; + mutex_unlock(&otp_mutex); +} + +/** + * otp_read_bits - read the content of OTP + * + * @start: offset from 0, in u32's + * @len: number of OTP u32's to read + * @bits: caller-allocated buffer to save bits + * @size: size of @bits + * + * Returns number of u32's saved to buffer + **/ +static size_t otp_read_bits(int start, int len, u32 *bits, size_t size) +{ + int ofs; + + BUG_ON(!mutex_is_locked(&otp_mutex)); + + /* read all stuff that caller needs */ + if (start + len > 4 * 8) /* 4 banks, 8 registers each */ + len = 4 * 8 - start; + + for (ofs = start; ofs < len; ofs++) { + if (size/sizeof(*bits) <= 0) /* we drained out the buffer */ + break; + *bits = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CUSTn(ofs)); + bits++; + size -= sizeof(*bits); + } + + return ofs - start; /* number of u32's that we saved to buffer */ +} + +/** + * otp_write_bits - store OTP bits + * + * @offset: offset from 0, in u32's + * @data: the u32 to write + * @magic: the magic value to be stored in UNLOCK field + * + **/ +static int otp_write_bits(int offset, u32 data, u32 magic) +{ + u32 c; + int r; + + BUG_ON(!mutex_is_locked(&otp_mutex)); + + if (offset < 0 || offset > 0x1F) + return -EINVAL; + + c = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL); + c &= ~BM_OCOTP_CTRL_ADDR; + c |= BF(offset, OCOTP_CTRL_ADDR); + c |= BF(magic, OCOTP_CTRL_WR_UNLOCK); + __raw_writel(c, REGS_OCOTP_BASE + HW_OCOTP_CTRL); + + __raw_writel(data, REGS_OCOTP_BASE + HW_OCOTP_DATA); + + r = otp_wait_busy(0); + if (r < 0) + return r; + + udelay(2); + return 0; +} + +static ssize_t otp_id_show(void *context, char *page, int ascii) +{ + char s[60]; + int ret; + int n, i, j, r; + u32 otp_bits[4 * 8]; + + r = otp_open(O_RDONLY); + if (r < 0) + return 0; + n = otp_read_bits(0, 4 * 8, otp_bits, sizeof(otp_bits)); + otp_close(); + + ret = 0; + + + if (ascii) { + + strcpy(page, ""); + ret = 0; + + if (otp_full) { + for (i = 0; i < 4; i++) { + + ret += sprintf(s, "Bank %d: ", i); + strcat(page, s); + + for (j = 0; j < 8; j++) { + + if (i * 4 + j > n) + break; + ret += sprintf(s, "%08X ", + otp_bits[i * 4 + j]); + strcat(page, s); + } + + strcat(page, "\n"); + ret++; + } + } else { + for (i = 0; i < OTP_USER_SIZE; i++) { + ret += sprintf(s, "%08X ", + otp_bits[i + OTP_USER_OFFSET]); + strcat(page, s); + } + strcat(page, "\n"); + ret++; + } + } else { + + if (otp_full) { + memcpy(page, otp_bits, sizeof(otp_bits)); + ret = sizeof(otp_bits); + } else { + memcpy(page, otp_bits + OTP_USER_OFFSET, + OTP_USER_SIZE * sizeof(u32)); + ret = OTP_USER_SIZE * sizeof(u32); + } + } + + return ret; +} + +static int otp_check_dry_run(const char *page, size_t count) +{ + if (count >= 3 && memcmp(page, "+++", 3) == 0) + return 3; + return 0; +} + +static ssize_t otp_id_store(void *context, const char *page, + size_t count, int ascii) +{ + int r = 0; + const char *p, *cp, *d; + unsigned long index, value; + char tmps[20]; /* subject of strtoul */ + int dry_run; + + r = otp_open(O_WRONLY); + if (r < 0) { + printk(KERN_ERR"Cannot open OTP in WRITE mode\n"); + return r; + } + + if (ascii) { + + dry_run = otp_check_dry_run(page, count); + if (dry_run > 0) + page += dry_run; + + index = 0; + cp = page; + + memset(tmps, 0, sizeof(tmps)); + + for (index = 0, cp = page; cp != NULL; index++) { + p = strchr(cp, ','); + + d = strchr(cp, ':'); + if (d && (!p || d < p)) { + strncpy(tmps, cp, + min_t(int, d - cp, sizeof(tmps) - 1)); + r = strict_strtoul(tmps, 0, &index); + if (r < 0) { + pr_debug("Cannot parse '%s'\n", tmps); + break; + } + cp = d + 1; + } + + memset(tmps, 0, sizeof(tmps)); + + if (!p) + strncpy(tmps, cp, sizeof(tmps)); + else + strncpy(tmps, cp, + min_t(int, p - cp, sizeof(tmps) - 1)); + r = strict_strtoul(tmps, 0, &value); + if (r < 0) { + pr_debug("Cannot parse '%s'\n", tmps); + break; + } + + memset(tmps, 0, sizeof(tmps)); + + cp = p ? ++p : NULL; + + if (!otp_full) { + index += OTP_USER_OFFSET; + if (index > OTP_USER_SIZE) { + printk(KERN_ERR"Cannot write at " + "offset %ld\n", index); + continue; + } + } + + r = 0; + if (!dry_run) { + pr_debug("Index %ld, value 0x%08lx\n", + index, value); + r = otp_write_bits(index, value, 0x3e77); + } else + printk(KERN_NOTICE + "Dry-run: writing 0x%08lX => [%ld]\n", + value, index); + if (r < 0) + break; + } + } else { + printk(KERN_ERR"Binary write is not supported\n"); + r = -ENOSYS; + } + otp_close(); + return (r >= 0) ? count : r; +} + +static struct uid_ops otp_ops = { + .id_show = otp_id_show, + .id_store = otp_id_store, +}; + +static int __init_or_module otp_init(void) +{ + void *p; + + mutex_init(&otp_mutex); + p = uid_provider_init("otp", &otp_ops, NULL); + if (IS_ERR(p)) + return PTR_ERR(p); + return 0; +} + +static void __exit otp_remove(void) +{ + uid_provider_remove("otp"); +} + +module_param(otp_full, int, 0600); +module_init(otp_init); +module_exit(otp_remove); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>"); +MODULE_DESCRIPTION("Unique ID: OTP"); |