ARM: tegra: Add NCT access api and sysfs interface
Joshua Cha [Wed, 13 Mar 2013 01:54:55 +0000 (10:54 +0900)]
NCT is the acronym for Nvidia Configuration Table.
This change provides API to read NCT items from NCT carveout region
and create sysfs interface for userspace to read items.

Bug 1223662

Change-Id: Id6f887fd9c458f4f9c3dfe27f6a95fbe930cfb00
Signed-off-by: Joshua Cha <joshuac@nvidia.com>
Reviewed-on: http://git-master/r/208680
(cherry picked from commit d6234478cd3e92b8c739e806998b9f75e732d714)
Reviewed-on: http://git-master/r/226216
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>

arch/arm/mach-tegra/Kconfig
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/board.h
arch/arm/mach-tegra/common.c
arch/arm/mach-tegra/include/mach/nct.h [new file with mode: 0644]
arch/arm/mach-tegra/nct.c [new file with mode: 0644]
arch/arm/mach-tegra/nct_sysfs.c [new file with mode: 0644]

index 81bb68d..c5db899 100644 (file)
@@ -768,4 +768,8 @@ config TEGRA_MC_DOMAINS
        help
          When enabled, clock gates MC when it's not needed.
 
+config TEGRA_USE_NCT
+       bool "Enable NCT partition access"
+       help
+         When enabled, we can read non-volatile items from NCT partition.
 endif
index e62adca..5b0d484 100644 (file)
@@ -63,6 +63,7 @@ obj-$(CONFIG_PM_SLEEP)                  += pm-irq.o
 obj-y                                   += gic.o
 
 obj-y                                   += sleep.o
+obj-$(CONFIG_TEGRA_USE_NCT)             += nct.o nct_sysfs.o
 
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
index 597b113..765a068 100644 (file)
@@ -2,7 +2,7 @@
  * arch/arm/mach-tegra/board.h
  *
  * Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2011-2013 NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2011-2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * Author:
  *     Colin Cross <ccross@google.com>
@@ -141,6 +141,11 @@ extern unsigned long nvdumper_reserved;
 #endif
 extern bool tegra_lp0_vec_relocate;
 extern unsigned long tegra_grhost_aperture;
+#ifdef CONFIG_TEGRA_USE_NCT
+/* info for NCK(NCT for Kernel) carveout area */
+extern unsigned long tegra_nck_start;
+extern unsigned long tegra_nck_size;
+#endif
 
 void tegra_init_late(void);
 
index fec7f1b..21455e8 100644 (file)
@@ -46,6 +46,7 @@
 #include <mach/powergate.h>
 #include <mach/tegra_smmu.h>
 #include <mach/gpio-tegra.h>
+#include <mach/nct.h>
 
 #include "apbio.h"
 #include "board.h"
@@ -138,6 +139,10 @@ static struct board_info button_board_info;
 static struct board_info joystick_board_info;
 static struct board_info rightspeaker_board_info;
 static struct board_info leftspeaker_board_info;
+#ifdef CONFIG_TEGRA_USE_NCT
+unsigned long tegra_nck_start;
+unsigned long tegra_nck_size;
+#endif
 
 static int pmu_core_edp;
 static int board_panel_type;
@@ -922,6 +927,24 @@ static int __init tegra_tzram_arg(char *options)
 early_param("tzram", tegra_tzram_arg);
 #endif
 
+#ifdef CONFIG_TEGRA_USE_NCT
+static int __init tegra_nck_arg(char *options)
+{
+       char *p = options;
+
+       tegra_nck_size = memparse(p, &p);
+       if (*p == '@')
+               tegra_nck_start = memparse(p+1, &p);
+       if (!tegra_nck_size || !tegra_nck_start) {
+               tegra_nck_size = 0;
+               tegra_nck_start = 0;
+       }
+
+       return 0;
+}
+early_param("nck", tegra_nck_arg);
+#endif /* CONFIG_TEGRA_USE_NCT */
+
 enum panel_type get_panel_type(void)
 {
        return board_panel_type;
@@ -1594,6 +1617,18 @@ void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size,
        }
 #endif
 
+#ifdef CONFIG_TEGRA_USE_NCT
+       if (tegra_nck_size &&
+          (tegra_nck_start < memblock_end_of_DRAM())) {
+               if (memblock_reserve(tegra_nck_start, tegra_nck_size)) {
+                       pr_err("Failed to reserve nck %08lx@%08lx\n",
+                               tegra_nck_size, tegra_nck_start);
+                       tegra_nck_start = 0;
+                       tegra_nck_size = 0;
+               }
+       }
+#endif
+
        /*
         * We copy the bootloader's framebuffer to the framebuffer allocated
         * above, and then free this one.
@@ -1683,6 +1718,15 @@ void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size,
                tegra_tzram_size ?
                        tegra_tzram_start + tegra_tzram_size - 1 : 0);
 #endif
+
+#ifdef CONFIG_TEGRA_USE_NCT
+       if (tegra_nck_size) {
+               pr_info("Nck:                    %08lx - %08lx\n",
+                       tegra_nck_start,
+                       tegra_nck_size ?
+                               tegra_nck_start + tegra_nck_size - 1 : 0);
+       }
+#endif
 }
 
 #ifdef CONFIG_PSTORE_RAM
diff --git a/arch/arm/mach-tegra/include/mach/nct.h b/arch/arm/mach-tegra/include/mach/nct.h
new file mode 100644 (file)
index 0000000..f6f1889
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * arch/arm/mach-tegra/include/mach/nct.h
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_NCT_H
+#define __MACH_TEGRA_NCT_H
+
+#define NCT_MAGIC_ID           0x7443566E /* "nVCt" */
+
+#define NCT_FORMAT_VERSION     0x00010000 /* 0xMMMMNNNN (VMMMM.NNNN) */
+
+#define NCT_ENTRY_OFFSET       0x4000
+#define MAX_NCT_ENTRY          512
+#define MAX_NCT_DATA_SIZE      1024
+
+#define NVIDIA_OUI     0x00044B
+
+enum nct_id_type {
+       NCT_ID_START = 0,
+       NCT_ID_SERIAL_NUMBER = NCT_ID_START,
+       NCT_ID_WIFI_MAC_ADDR,
+       NCT_ID_BT_ADDR,
+       NCT_ID_CM_ID,
+       NCT_ID_LBH_ID,
+       NCT_ID_FACTORY_MODE,
+       NCT_ID_RAMDUMP,
+       NCT_ID_TEST,
+       NCT_ID_BOARD_INFO,
+       NCT_ID_END = NCT_ID_BOARD_INFO,
+       NCT_ID_DISABLED = 0xEEEE,
+       NCT_ID_MAX = 0xFFFF
+};
+
+struct nct_serial_number_type {
+       char sn[30];
+};
+
+struct nct_wifi_mac_addr_type {
+       u8 addr[6];
+};
+
+struct nct_bt_addr_type {
+       u8 addr[6];
+};
+
+struct nct_cm_id_type {
+       u16 id;
+};
+
+struct nct_lbh_id_type {
+       u16 id;
+};
+
+struct nct_factory_mode_type {
+       u32 flag;
+};
+
+struct nct_ramdump_type {
+       u32 flag;
+};
+
+struct nct_board_info_type {
+       u16 board_id;
+       u16 sku;
+       u16 fab;
+       u16 major_revision;
+       u16 minor_revision;
+};
+
+union nct_item_type {
+       struct nct_serial_number_type   serial_number;
+       struct nct_wifi_mac_addr_type   wifi_mac_addr;
+       struct nct_bt_addr_type         bt_addr;
+       struct nct_cm_id_type           cm_id;
+       struct nct_lbh_id_type          lbh_id;
+       struct nct_factory_mode_type    factory_mode;
+       struct nct_ramdump_type         ramdump;
+       struct nct_board_info_type      board_info;
+       u8      u8[MAX_NCT_DATA_SIZE];
+       u16     u16[MAX_NCT_DATA_SIZE/2];
+       u32     u32[MAX_NCT_DATA_SIZE/4];
+} __packed;
+
+struct nct_entry_type {
+       u32 index;
+       u32 reserved[2];
+       union nct_item_type data;
+       u32 checksum;
+};
+
+struct nct_part_head_type {
+       u32 magicId;
+       u32 vendorId;
+       u32 productId;
+       u32 version;
+       u32 revision;
+};
+
+extern int tegra_nct_read_item(u32 index, union nct_item_type *buf);
+extern int tegra_nct_is_init(void);
+
+#endif /* __MACH_TEGRA_NCT_H */
diff --git a/arch/arm/mach-tegra/nct.c b/arch/arm/mach-tegra/nct.c
new file mode 100644 (file)
index 0000000..c5e5094
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * arch/arm/mach-tegra/nct.c
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/sysfs.h>
+
+#include <mach/nct.h>
+#include "board.h"
+
+
+#define USE_CRC32_IN_NCT       1
+
+static bool tegra_nct_initialized;
+static struct nct_part_head_type nct_head;
+static void __iomem *nct_ptr;
+
+
+int tegra_nct_read_item(u32 index, union nct_item_type *buf)
+{
+       struct nct_entry_type *entry = NULL;
+       u8 *nct;
+#if USE_CRC32_IN_NCT
+       u32 crc = 0;
+#endif
+
+       if (!tegra_nct_initialized)
+               return -EPERM;
+
+       entry = kmalloc(sizeof(struct nct_entry_type), GFP_KERNEL);
+       if (!entry) {
+               pr_err("%s: failed to allocate buffer\n", __func__);
+               return -ENOMEM;
+       }
+
+       nct = (u8 *)(nct_ptr + NCT_ENTRY_OFFSET +
+                       (index * sizeof(*entry)));
+       memcpy((u8 *)entry, nct, sizeof(*entry));
+
+       /* check CRC integrity */
+#if USE_CRC32_IN_NCT
+       /* last checksum field of entry is not included in CRC calculation */
+       crc = crc32_le(~0, (u8 *)entry, sizeof(*entry) -
+                       sizeof(entry->checksum)) ^ ~0;
+       if (crc != entry->checksum) {
+               pr_err("%s: checksum err(0x%x/0x%x)\n", __func__,
+                       crc, entry->checksum);
+               kfree(entry);
+               return -EINVAL;
+       }
+#endif
+       /* check index integrity */
+       if (index != entry->index) {
+               pr_err("%s: index err(0x%x/0x%x)\n", __func__,
+                       index, entry->index);
+               kfree(entry);
+               return -EINVAL;
+       }
+
+       memcpy(buf, &entry->data, sizeof(*buf));
+       kfree(entry);
+
+       return 0;
+}
+
+int tegra_nct_is_init(void)
+{
+       return tegra_nct_initialized;
+}
+
+static int __init tegra_nct_init(void)
+{
+       if (tegra_nct_initialized)
+               return 0;
+
+       if ((tegra_nck_start == 0) || (tegra_nck_size == 0)) {
+               pr_err("tegra_nct: not configured\n");
+               return -ENOTSUPP;
+       }
+
+       nct_ptr = ioremap_nocache(tegra_nck_start,
+                               tegra_nck_size);
+       if (!nct_ptr) {
+               pr_err("tegra_nct: failed to ioremap memory at 0x%08lx\n",
+                       tegra_nck_start);
+               return -EIO;
+       }
+
+       memcpy(&nct_head, nct_ptr, sizeof(nct_head));
+
+       pr_info("%s: magic(0x%x),vid(0x%x),pid(0x%x),ver(V%x.%x),rev(%d)\n",
+               __func__,
+               nct_head.magicId,
+               nct_head.vendorId,
+               nct_head.productId,
+               (nct_head.version >> 16) & 0xFFFF,
+               (nct_head.version & 0xFFFF),
+               nct_head.revision);
+
+       if (nct_head.magicId != NCT_MAGIC_ID) {
+               pr_err("%s: magic ID error (0x%x/0x%x)\n", __func__,
+                       nct_head.magicId, NCT_MAGIC_ID);
+               BUG();
+       }
+
+       tegra_nct_initialized = true;
+
+       return 0;
+}
+
+early_initcall(tegra_nct_init);
diff --git a/arch/arm/mach-tegra/nct_sysfs.c b/arch/arm/mach-tegra/nct_sysfs.c
new file mode 100644 (file)
index 0000000..1f71961
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * arch/arm/mach-tegra/nct_sysfs.c
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#include <mach/nct.h>
+#include "board.h"
+
+static struct kobject *nct_kobj;
+static ssize_t nct_item_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf);
+
+
+static const struct kobj_attribute serial_number_attr =
+       __ATTR(serial_number, 0444, nct_item_show, 0);
+static const struct kobj_attribute wifi_mac_addr_attr =
+       __ATTR(wifi_mac_addr, 0444, nct_item_show, 0);
+static const struct kobj_attribute bt_addr_attr =
+       __ATTR(bt_addr, 0444, nct_item_show, 0);
+static const struct kobj_attribute cm_id_attr =
+       __ATTR(cm_id, 0444, nct_item_show, 0);
+static const struct kobj_attribute lbh_id_attr =
+       __ATTR(lbh_id, 0444, nct_item_show, 0);
+
+static const struct attribute *nct_item_attrs[] = {
+       &serial_number_attr.attr,
+       &wifi_mac_addr_attr.attr,
+       &bt_addr_attr.attr,
+       &cm_id_attr.attr,
+       &lbh_id_attr.attr,
+       NULL
+};
+
+static ssize_t nct_item_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       ssize_t rval = 0;
+       union nct_item_type item;
+       int err;
+
+       if (attr == &serial_number_attr) {
+               err = tegra_nct_read_item(NCT_ID_SERIAL_NUMBER, &item);
+               if (err < 0)
+                       return 0;
+               rval = sprintf(buf, "%s\n", item.serial_number.sn);
+       } else if (attr == &wifi_mac_addr_attr) {
+               err = tegra_nct_read_item(NCT_ID_WIFI_MAC_ADDR, &item);
+               if (err < 0)
+                       return 0;
+               rval = sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                       item.wifi_mac_addr.addr[0],
+                       item.wifi_mac_addr.addr[1],
+                       item.wifi_mac_addr.addr[2],
+                       item.wifi_mac_addr.addr[3],
+                       item.wifi_mac_addr.addr[4],
+                       item.wifi_mac_addr.addr[5]);
+       } else if (attr == &bt_addr_attr) {
+               err = tegra_nct_read_item(NCT_ID_BT_ADDR, &item);
+               if (err < 0)
+                       return 0;
+               rval = sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                       item.bt_addr.addr[0],
+                       item.bt_addr.addr[1],
+                       item.bt_addr.addr[2],
+                       item.bt_addr.addr[3],
+                       item.bt_addr.addr[4],
+                       item.bt_addr.addr[5]);
+       } else if (attr == &cm_id_attr) {
+               err = tegra_nct_read_item(NCT_ID_CM_ID, &item);
+               if (err < 0)
+                       return 0;
+               rval = sprintf(buf, "%04d\n", item.cm_id.id);
+       } else if (attr == &lbh_id_attr) {
+               err = tegra_nct_read_item(NCT_ID_LBH_ID, &item);
+               if (err < 0)
+                       return 0;
+               rval = sprintf(buf, "%04d\n", item.lbh_id.id);
+       }
+
+       return rval;
+}
+
+static int __init tegra_nct_sysfs_init(void)
+{
+       if (!tegra_nct_is_init()) {
+               pr_err("tegra_nct: not initialized\n");
+               return 0;
+       }
+
+       nct_kobj = kobject_create_and_add("tegra_nct", kernel_kobj);
+       if (!nct_kobj) {
+               pr_err("tegra_nct: failed to create sysfs nct object\n");
+               return 0;
+       }
+
+       if (sysfs_create_files(nct_kobj, nct_item_attrs)) {
+               pr_err("%s: failed to create nct item sysfs files\n", __func__);
+               kobject_del(nct_kobj);
+               nct_kobj = 0;
+       }
+
+       return 0;
+}
+
+late_initcall(tegra_nct_sysfs_init);