[ARM] tegra: Add ioctls to tegra2_fuse driver
authorGreg Meiste <w30289@motorola.com>
Tue, 4 Jan 2011 20:51:46 +0000 (14:51 -0600)
committerRebecca Schultz Zavin <rebecca@android.com>
Mon, 10 Jan 2011 23:27:36 +0000 (15:27 -0800)
Exposing the fuse API to userspace for factory.

Submitted on behalf of Eyob Tesfu <eyob.tesfu@motorola.com>

Change-Id: Ib2a03c49bde7990d89165db24db41f1ea832a5b7
Signed-off-by: Greg Meiste <w30289@motorola.com>
arch/arm/mach-tegra/include/mach/tegra2_fuse.h
arch/arm/mach-tegra/tegra2_fuse.c

index b5341b77a3601c21da81f9df759f2bca3769b90b..3935ec75cbbeb873ca770f3a9fb4e8b0adab8694 100644 (file)
 #ifndef __MACH_TEGRA2_FUSE_H
 #define __MACH_TEGRA2_FUSE_H
 
+#include <linux/ioctl.h>
+
 #define SBK_DEVKEY_STATUS_SZ   sizeof(u32)
 
+#define TEGRA_FUSE_IOCTL_PROGRAM_SBK            _IOWR(0x99, 100, int*)
+#define TEGRA_FUSE_IOCTL_GET_UID                _IOWR(0x99, 101, int*)
+#define TEGRA_FUSE_IOCTL_PROGRAM_SDMMC          _IOWR(0x99, 102, int)
+#define TEGRA_FUSE_IOCTL_VERIFY_SBK             _IOWR(0x99, 103, int)
+#define TEGRA_FUSE_IOCTL_VERIFY_PROD_ODM        _IOWR(0x99, 104, int)
+
+#define SIZE_OF_UID            16
+#define SIZE_OF_SBK            16
+
 /* fuse io parameters */
 enum fuse_io_param {
        DEVKEY,
index a488a392716cb6b4755e2f21fd20a3cfd2d7f7a5..037ac2bfd0ad2d09bd5bbfbd71dfa0ef0b0c90f5 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/fs.h>
 #include <linux/io.h>
+#include <linux/miscdevice.h>
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 
 #include <mach/tegra2_fuse.h>
 
@@ -60,6 +63,9 @@
 #define FUSE_DIS_PGM           0x02C
 #define FUSE_PWR_GOOD_SW       0x034
 
+#define BOOT_DEVICE_INFO_EMMC           17
+#define USE_FUSE_TO_SELECT_BOOT_DEVICE  1
+
 static u32 fuse_pgm_data[NFUSES / 2];
 static u32 fuse_pgm_mask[NFUSES / 2];
 static u32 tmp_fuse_pgm_data[NFUSES / 2];
@@ -547,9 +553,156 @@ void tegra_fuse_program_disable(void)
        mutex_unlock(&fuse_lock);
 }
 
+static int tegra_fuse_program_sdmmc(void)
+{
+       int ret_val                 = 0;
+       u32 boot_cfg_value          = 0;
+       u32 boot_strap_value        = 0;
+
+       struct fuse_data emmc_data;
+
+       emmc_data.ignore_devsel_straps = USE_FUSE_TO_SELECT_BOOT_DEVICE;
+       emmc_data.bootdev_cfg = BOOT_DEVICE_INFO_EMMC;
+
+       tegra_fuse_read(IGNORE_DEV_SEL_STRAPS, &boot_strap_value,
+                       sizeof(boot_strap_value));
+       tegra_fuse_read(SEC_BOOT_DEV_CFG, &boot_cfg_value,
+                       sizeof(boot_cfg_value));
+       if ((boot_strap_value == USE_FUSE_TO_SELECT_BOOT_DEVICE) &&
+           (boot_cfg_value == BOOT_DEVICE_INFO_EMMC)) {
+               pr_err("Boot info fuses already programmed!\n");
+               return ret_val;
+       }
+       ret_val = tegra_fuse_program(&emmc_data,
+                               FLAGS_SEC_BOOT_DEV_CFG |
+                               FLAGS_IGNORE_DEV_SEL_STRAPS);
+       if (ret_val < 0)
+               pr_err("Unable to program emmc fuse\n");
+       return ret_val;
+}
+
+static int tegra_fuse_verify_odm_production(void)
+{
+       int ret_val = 0;
+       u32 fuse = 0;
+       if (tegra_fuse_read(ODM_PROD_MODE, &fuse, sizeof(fuse)) == 0) {
+               pr_info("%s: Production ODM FUSE value:  %x\n", __func__, fuse);
+               ret_val = (int)fuse;
+       }
+       return ret_val;
+}
+
+static int tegra_fuse_verify_sbk(void)
+{
+       int ret_val = 0;
+       u32 fuse = 0;
+       if (tegra_fuse_read(SBK_DEVKEY_STATUS, (void *)&fuse,
+               SBK_DEVKEY_STATUS_SZ) == 0) {
+               pr_info("%s: SBK_DEVKEY_FUSE value: %x\n",
+                       __func__, fuse);
+               ret_val = (int)fuse;
+       }
+       pr_info("SBK_DEVKEY_FUSE verify is done\n");
+       return ret_val;
+}
+
+static int tegra_fuse_get_uid(void *data)
+{
+       u64 uid = 0ULL;
+       u32 ub, lb;
+
+       uid = tegra_chip_uid();
+       lb = uid & 0xFFFFFFFF;
+       ub = uid >> 32;
+       snprintf((char *)data, SIZE_OF_UID, "%08X%08X", ub, lb);
+       return 0;
+}
+
+
+static int tegra_fuse_program_sbk_fuse(void *data)
+{
+       int ret_val = 0;
+       struct fuse_data sbk_req_data;
+
+       memcpy(sbk_req_data.sbk, data, SIZE_OF_SBK);
+
+       ret_val = tegra_fuse_verify_sbk();
+       if (ret_val > 0) {
+               pr_err("SBK has been programmed already!\n");
+               return ret_val;
+       }
+
+       ret_val = tegra_fuse_program(&sbk_req_data, FLAGS_SBK);
+       pr_info("tegra_fuse_read return value %d\n ", ret_val);
+       return ret_val;
+}
+
+long tegra_fuse_ioctl(struct file *file, unsigned int ioctl_num,
+                     unsigned long ioctl_param)
+{
+       int ret_val = 0;
+       u32 sbk_buff[4];
+       u32 uid_buff[4];
+
+       switch (ioctl_num) {
+       case TEGRA_FUSE_IOCTL_PROGRAM_SBK:
+               if (copy_from_user((void *)sbk_buff,
+                                  (void __user *)ioctl_param,
+                                  SIZE_OF_SBK)) {
+                       return -EFAULT;
+               }
+               ret_val = tegra_fuse_program_sbk_fuse(sbk_buff);
+               break;
+
+       case TEGRA_FUSE_IOCTL_GET_UID:
+               tegra_fuse_get_uid(uid_buff);
+               if (copy_to_user((void *)uid_buff,
+                                (void __user *)ioctl_param, SIZE_OF_UID)) {
+                       ret_val = -EFAULT;
+               }
+               break;
+
+       case TEGRA_FUSE_IOCTL_PROGRAM_SDMMC:
+               ret_val = tegra_fuse_program_sdmmc();
+               break;
+
+       case TEGRA_FUSE_IOCTL_VERIFY_SBK:
+               ret_val = tegra_fuse_verify_sbk();
+               break;
+
+       case TEGRA_FUSE_IOCTL_VERIFY_PROD_ODM:
+               ret_val = tegra_fuse_verify_odm_production();
+               break;
+
+       default:
+               ret_val = -EFAULT;
+               break;
+       }
+       return ret_val;
+}
+
+static const struct file_operations tegra_fuse_fops = {
+       .unlocked_ioctl = tegra_fuse_ioctl,
+};
+
+static struct miscdevice tegra_fuse_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "tegra_fuse",
+       .fops = &tegra_fuse_fops,
+};
+
 static int __init tegra_fuse_program_init(void)
 {
+       int rtn;
+
        mutex_init(&fuse_lock);
+
+       rtn = misc_register(&tegra_fuse_device);
+       if (rtn < 0) {
+               pr_err("Failed to register misc device %d\n", rtn);
+               return rtn;
+       }
+
        return 0;
 }