Add device support for reading OTP.
Creates a /proc/otp/islocked pseudo-file, which contains '1'
if the device is locked, and '0' if it is not.
Change-Id: I394baa3aae6d585f13c3c9a0c9bd228109fb752a
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/otp.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/otp.h
new file mode 100644
index 0000000..24c10da
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/otp.h
@@ -0,0 +1,53 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/comcerto-2000/otp.h
+ *
+ * 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
+ */
+
+#ifndef __COMCERTO_OTP_H__
+#define __COMCERTO_OTP_H__
+
+#include <mach/comcerto-2000.h>
+
+#define OTP_SIZE 8 * 1024
+
+#define OTP_DELAY 1000
+
+#define OTP_CONFIG_LOCK_0 APB_VADDR(COMCERTO_APB_OTP_BASE + 0x00)
+#define OTP_CONFIG_LOCK_1 APB_VADDR(COMCERTO_APB_OTP_BASE + 0x04)
+#define OTP_CEB_SEQUENCE_LOCKS APB_VADDR(COMCERTO_APB_OTP_BASE + 0x08)
+#define OTP_CEB_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x0C)
+#define OTP_RSTB_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x10)
+#define OTP_ADDR_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x14)
+#define OTP_READEN_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x18)
+#define OTP_DATA_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x1C)
+#define OTP_DLE_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x20)
+#define OTP_WEB_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x24)
+#define OTP_WEB_COUNTER APB_VADDR(COMCERTO_APB_OTP_BASE + 0x28)
+#define OTP_PGMEN_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x2C)
+#define OTP_PGM2CPUMP_COUNTER APB_VADDR(COMCERTO_APB_OTP_BASE + 0x30)
+#define OTP_CPUMPEN_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x34)
+#define OTP_CPUMP2WEB_COUNTER APB_VADDR(COMCERTO_APB_OTP_BASE + 0x38)
+#define OTP_WEB2CPUMP_COUNTER APB_VADDR(COMCERTO_APB_OTP_BASE + 0x3C)
+#define OTP_CPUMP2PGM_COUNTER APB_VADDR(COMCERTO_APB_OTP_BASE + 0x40)
+#define OTP_CLE_INPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x44)
+#define OTP_SECURE_LOCK_OUTPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x48)
+#define OTP_DATA_OUT_COUNTER APB_VADDR(COMCERTO_APB_OTP_BASE + 0x4C)
+#define OTP_DATA_OUTPUT APB_VADDR(COMCERTO_APB_OTP_BASE + 0x50)
+#define OTP_HW_SEC_MODE_STATUS APB_VADDR(COMCERTO_APB_OTP_BASE + 0x54)
+
+#define DOUT_COUNTER_VALUE 0x1F
+
+#endif
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 4364303..0b5e94b 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -627,5 +627,7 @@
device appear much like a simple EEPROM, and knows
how to partition a single ROM for multiple purposes.
+source "drivers/char/comcerto_otp/Kconfig"
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 32762ba..c76fa8c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -65,3 +65,5 @@
js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o
+
+obj-$(CONFIG_COMCERTO_OTP) += comcerto_otp/
diff --git a/drivers/char/comcerto_otp/Kconfig b/drivers/char/comcerto_otp/Kconfig
new file mode 100644
index 0000000..48459f2
--- /dev/null
+++ b/drivers/char/comcerto_otp/Kconfig
@@ -0,0 +1,11 @@
+config COMCERTO_OTP
+ tristate "Comcerto On-Chip OTP Memory Support"
+ depends on ARCH_COMCERTO
+ default y
+ help
+ If you say Y here, you will get support for a proc file,
+ /proc/otp/islocked, that reports whether the Comcerto
+ One-Time Programmable (and thus the device) is locked or
+ not.
+
+ If unsure, it is safe to say Y.
diff --git a/drivers/char/comcerto_otp/Makefile b/drivers/char/comcerto_otp/Makefile
new file mode 100644
index 0000000..f5e1eb8
--- /dev/null
+++ b/drivers/char/comcerto_otp/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_COMCERTO_OTP) += comcerto_otp_m.o
+
+comcerto_otp_m-y := comcerto_otp.o otp.o
diff --git a/drivers/char/comcerto_otp/comcerto_otp.c b/drivers/char/comcerto_otp/comcerto_otp.c
new file mode 100644
index 0000000..48fb664
--- /dev/null
+++ b/drivers/char/comcerto_otp/comcerto_otp.c
@@ -0,0 +1,88 @@
+/*
+ * Comcerto OTP access
+ *
+ * (C) Copyright 2014 Google Inc.
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+extern int otp_read(u32 offset, u8* read_data, int size);
+
+static int islocked_proc_show(struct seq_file *m, void *v)
+{
+ char status_buf[1];
+
+ if (!otp_read(8, status_buf, 1)) {
+ printk(KERN_ERR "comcerto_otp: Unable to read from OTP!\n");
+ return -1;
+ }
+
+ seq_printf(m, "%d\n", (*status_buf & 0x2) == 0x2);
+ return 0;
+}
+
+static int islocked_proc_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, islocked_proc_show, NULL);
+}
+
+static struct file_operations islocked_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = islocked_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct proc_dir_entry *otp_dir;
+
+static int __init comcerto_otp_init(void)
+{
+ struct proc_dir_entry *islocked_proc_file;
+
+ otp_dir = proc_mkdir("otp", NULL);
+ if (otp_dir == NULL) {
+ printk(KERN_ERR "comcerto_otp: Unable to create/proc/otp directory\n");
+ return -ENOMEM;
+ }
+
+ islocked_proc_file = proc_create("islocked", 0, otp_dir, &islocked_proc_fops);
+ if (islocked_proc_file == NULL) {
+ printk(KERN_ERR "comcerto_otp: Unable to create /proc/otp/islocked\n");
+ remove_proc_entry("otp", NULL);
+ return -ENOMEM;
+ }
+
+ printk(KERN_INFO "comcerto_otp: Created /proc/otp/islocked\n");
+ return 0;
+}
+
+static void __exit comcerto_otp_exit(void)
+{
+ remove_proc_entry("islocked", otp_dir);
+ remove_proc_entry("otp", NULL);
+}
+
+module_init(comcerto_otp_init);
+module_exit(comcerto_otp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stephen McGruer <smcgruer@google.com>");
+MODULE_DESCRIPTION("Comcerto OTP driver");
diff --git a/drivers/char/comcerto_otp/otp.c b/drivers/char/comcerto_otp/otp.c
new file mode 100644
index 0000000..7ae410f
--- /dev/null
+++ b/drivers/char/comcerto_otp/otp.c
@@ -0,0 +1,69 @@
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <mach/comcerto-2000/otp.h>
+
+/* Taken from repo://barebox/mindspeed/drivers/otp/c2k_otp.c */
+int otp_read(u32 offset, u8 *read_data, int size)
+{
+ int i;
+ u32 read_tmp = 0;
+
+ if (NULL == read_data)
+ return 1;
+
+ if (size <= 0)
+ return 1;
+
+ /* configure the OTP_DATA_OUT_COUNTER for read operation.
+ 70 nsec is needed except for blank check test, in which 1.5 usec is needed.*/
+ writel(DOUT_COUNTER_VALUE, OTP_DATA_OUT_COUNTER);
+
+ /* Unlock the write protection. */
+ writel(0xEBCF0000, OTP_CONFIG_LOCK_0); /* config lock0 */
+ writel(0xEBCF1111, OTP_CONFIG_LOCK_1); /* config lock1 */
+ writel(0x0, OTP_CEB_INPUT);
+
+ /* rstb drive 0 */
+ writel(0x0, OTP_RSTB_INPUT);
+ /* Wait for at least 20nsec */
+ udelay(OTP_DELAY);
+ /* rstb drive 1 to have pulse */
+ writel(0x1, OTP_RSTB_INPUT);
+ /* Wait for at least 1usec */
+ udelay(OTP_DELAY);
+
+ /* Write the desired address to the ADDR register */
+ writel(offset, OTP_ADDR_INPUT);
+ /* read_enable drive */
+ writel(0x1, OTP_READEN_INPUT);
+ /* Wait for at least 70nsec/1.5usec depends on operation type */
+ udelay(OTP_DELAY);
+
+ /* Read First Byte */
+ read_tmp = readl(OTP_DATA_OUTPUT);
+ *read_data = read_tmp & 0xFF;
+
+ /* For consecutive read */
+ for(i = 1 ; i < size ; i++)
+ {
+ offset = offset + 8;
+
+ /* start reading from data out register */
+ writel(offset, OTP_ADDR_INPUT);
+ /* Wait for at least 70nsec/1.5usec depends on operation type */
+ udelay(OTP_DELAY);
+
+ read_tmp = readl(OTP_DATA_OUTPUT);
+ *(read_data + i) = read_tmp & 0xFF;
+ }
+
+ /* reading is done make the read_enable low */
+ writel(0x0, OTP_READEN_INPUT);
+
+ /* lock CEB register, return to standby mode */
+ writel(0x1, OTP_CEB_INPUT);
+
+ return 1;
+}
+