Merge "Add device support for reading OTP."
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;
+}
+