summaryrefslogtreecommitdiff
path: root/drivers/misc/hwconfig_check
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/misc/hwconfig_check
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/misc/hwconfig_check')
-rw-r--r--drivers/misc/hwconfig_check/Kconfig5
-rw-r--r--drivers/misc/hwconfig_check/Makefile4
-rw-r--r--drivers/misc/hwconfig_check/sprd_hwconfig_check.c645
3 files changed, 654 insertions, 0 deletions
diff --git a/drivers/misc/hwconfig_check/Kconfig b/drivers/misc/hwconfig_check/Kconfig
new file mode 100644
index 00000000..ad383c20
--- /dev/null
+++ b/drivers/misc/hwconfig_check/Kconfig
@@ -0,0 +1,5 @@
+config SPRD_HWCONFIG_CHECK
+ bool "enable hardware configuration check funtion"
+ default n
+ help
+ enable hardware configuration check
diff --git a/drivers/misc/hwconfig_check/Makefile b/drivers/misc/hwconfig_check/Makefile
new file mode 100644
index 00000000..279f443a
--- /dev/null
+++ b/drivers/misc/hwconfig_check/Makefile
@@ -0,0 +1,4 @@
+# Makefile for the benchMark support
+obj-$(CONFIG_SPRD_HWCONFIG_CHECK) += sprd_hwconfig_check.o
+
+
diff --git a/drivers/misc/hwconfig_check/sprd_hwconfig_check.c b/drivers/misc/hwconfig_check/sprd_hwconfig_check.c
new file mode 100644
index 00000000..32001310
--- /dev/null
+++ b/drivers/misc/hwconfig_check/sprd_hwconfig_check.c
@@ -0,0 +1,645 @@
+#include <linux/io.h>
+#include <soc/sprd/gpio.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/pinmap.h>
+#include <linux/init.h>
+#include <linux/module.h> /* Specifically, a modulei */
+#include <linux/kernel.h> /* We're doing kernel work */
+#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
+#include <linux/uaccess.h> /* for copy_from_user */
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/hwconfig_check.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/arch_misc.h>
+#include <linux/regulator/consumer.h>
+#include <linux/suspend.h>
+#include <linux/notifier.h>
+#define PROC_MAX_SIZE 64
+
+
+
+struct hwconfig_check_t {
+const char *name;
+const int size;
+char *buffer;
+bool ready;
+};
+
+enum reg_type {
+normal_type = 0,
+ana_type,
+};
+
+
+
+enum reg_type conti_reg_type;
+enum reg_type sep_reg_type;
+
+unsigned long conti_base_addr;
+unsigned long sep_base_addr;
+unsigned long ana_size;
+
+enum idx {
+idx_conti_reg = 0,
+idx_sleep_conti_reg,
+idx_resume_conti_reg,
+idx_sep_reg,
+idx_sleep_sep_reg,
+idx_resume_sep_reg,
+idx_regu_volt,
+idx_sleep_regu_volt,
+idx_resume_regu_volt,
+idx_regu_volt_setting,
+idx_conti_reg_setting,
+idx_sep_reg_setting,
+};
+
+
+
+#define PROC_NAME "sprd_hwconfig_check"
+#define MAX_REGU_ARRAY_NUM 60
+#define MAX_SEP_REG_NUM 128
+static unsigned long reg_base_addr;
+static unsigned long reg_num;
+
+
+static bool hwconfig_sleep_flag;
+static bool hwconfig_resume_flag;
+static bool hwconfig_sep_sleep_flag;
+static bool hwconfig_sep_resume_flag;
+static bool hwconfig_regu_volt_sleep_flag;
+static bool hwconfig_regu_volt_resume_flag;
+
+static int regu_name_index;
+static int sep_reg_index;
+
+
+static char regu_name_array[MAX_REGU_ARRAY_NUM][64];
+static int sep_reg_array[MAX_SEP_REG_NUM];
+static struct hwconfig_check_t hwconfig_check_array[] = {
+ [0] = {
+ .name = "conti_config",
+ .size = 4096,
+ },
+ [1] = {
+ .name = "conti_sleep_config",
+ .size = 4096,
+ },
+ [2] = {
+ .name = "conti_resume_config",
+ .size = 4096,
+ },
+ [3] = {
+ .name = "sep_config",
+ .size = 2048,
+ },
+ [4] = {
+ .name = "sep_sleep_config",
+ .size = 2048,
+ },
+ [5] = {
+ .name = "sep_resume_config",
+ .size = 2048,
+ },
+ [6] = {
+ .name = "regu_volt",
+ .size = 2048,
+ },
+ [7] = {
+ .name = "sleep_regu_volt",
+ .size = 2048,
+ },
+ [8] = {
+ .name = "resume_regu_volt",
+ .size = 2048,
+ },
+
+ [9] = {
+ .name = "regu_volt_setting",
+ .size = 512,
+ },
+ [10] = {
+ .name = "conti_reg_setting",
+ .size = 128,
+ },
+ [11] = {
+ .name = "sep_reg_setting",
+ .size = 1024,
+ },
+};
+
+
+
+static struct proc_dir_entry *hwconfig_entry;
+/* The buffer used to store character for this module */
+static int hwconfig_open(struct inode *inode, struct file *file);
+static ssize_t hwconfig_read(struct file *file, char __user *buffer,
+ size_t len, loff_t *offset);
+static ssize_t hwconfig_write(struct file *file, const char __user *buffer,
+ size_t len, loff_t *offset);
+
+static const struct file_operations hwconfig_check_fops = {
+ .owner = THIS_MODULE,
+ .read = hwconfig_read,
+ .open = hwconfig_open,
+ .write = hwconfig_write,
+ .release = NULL,
+};
+/* This function is called when the module is loaded */
+
+
+static int hwconfig_open(struct inode *inode, struct file *file)
+{
+ int i = 0;
+
+ char *name = PDE_DATA(inode);
+ for (i = 0; i < ARRAY_SIZE(hwconfig_check_array); i++) {
+ if (!strcmp(name, hwconfig_check_array[i].name)) {
+ file->private_data = (void *)i;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static ssize_t hwconfig_read(struct file *file
+ , char __user *buffer, size_t len, loff_t *offset)
+{
+
+ int idx = (int)file->private_data;
+ /*printk("chip_id 0x%x, ana_chip_id 0x%x",sci_get_chip_id(),
+ sci_get_ana_chip_id());*/
+ if (idx >= ARRAY_SIZE(hwconfig_check_array))
+ pr_debug("[hwconfig] hwconfig_read idx error %d\n", idx);
+
+ if (!hwconfig_check_array[idx].ready) {
+ /*pr_debug("[hwconfig] hwconfig_read
+ idx %ddata not ready\n", idx);*/
+ return 0;
+ }
+ return simple_read_from_buffer(buffer, len, offset,
+ hwconfig_check_array[idx].buffer,
+ hwconfig_check_array[idx].size);
+}
+
+unsigned long hwconfig_read_reg(unsigned long addr)
+{
+ pr_debug("[hwconfig], hwconfig_read_reg 0x%x\n", addr);
+ return readl_relaxed((void __iomem *)addr);
+}
+
+unsigned long hwconfig_read_anareg(unsigned long addr)
+{
+ unsigned long real_addr = (u64)SPRD_ADI_BASE+addr;
+ return sci_adi_read((void __iomem *)real_addr);
+}
+
+static int trigger_conti_reg_dump(int mode)
+{
+ int i = 0;
+ long r = 0;
+ unsigned int reg = 0;
+ int index = mode;
+
+ if (index >= ARRAY_SIZE(hwconfig_check_array))
+ pr_debug("trigger_pinmap_dump index error %d\n", index);
+
+ memset(hwconfig_check_array[index].buffer, 0x0,
+ hwconfig_check_array[index].size);
+
+ for (i = 0; i < reg_num; i++) {
+ char temp[20] = {0};
+ reg = reg_base_addr+(i<<2);
+
+ if (conti_reg_type == normal_type)
+ r = hwconfig_read_reg(conti_base_addr+reg);
+ else
+ r = hwconfig_read_anareg(reg);
+ /*printk("[hwconfig] trigger_conti_reg_dump
+ conti_base_addr 0x%0x, reg0x%0x
+ r 0x%0x\n ", conti_base_addr, reg, r);*/
+ sprintf(temp, "0x%08x,", r);
+
+ strcat(hwconfig_check_array[index].buffer, temp);
+ }
+ hwconfig_check_array[index].ready = true;
+
+ return 0;
+
+}
+
+
+
+bool get_hwconfig_sleep_flag(void)
+{
+ return hwconfig_sleep_flag;
+}
+
+bool get_hwconfig_resume_flag(void)
+{
+ return hwconfig_resume_flag;
+}
+
+int hwconfig_check_sleep_conti_reg(void)
+{
+ /*pr_debug("[hwconfig] hwconfig_check_sleep_
+ conti_reg hwconfig_sleep_flag %d\n", hwconfig_sleep_flag);*/
+ if (hwconfig_sleep_flag) {
+ hwconfig_sleep_flag = false;
+ trigger_conti_reg_dump(idx_sleep_conti_reg);
+ }
+ return 0;
+}
+
+int hwconfig_check_resume_conti_reg(void)
+{
+ if (hwconfig_resume_flag) {
+ hwconfig_resume_flag = false;
+ trigger_conti_reg_dump(idx_resume_conti_reg);
+ }
+ return 0;
+
+}
+
+
+static int trigger_conti_reg_check(char *buffer)
+{
+
+ char *ptr;
+ unsigned long phy_addr;
+ unsigned long size;
+ unsigned long base_addr;
+
+ hwconfig_check_array[idx_conti_reg].ready = false;
+ hwconfig_check_array[idx_sleep_conti_reg].ready = false;
+ hwconfig_check_array[idx_resume_conti_reg].ready = false;
+
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ conti_reg_type = simple_strtoul(ptr, NULL, 10);
+ pr_debug("[hwconfig] conti_reg_type %d \n", conti_reg_type);
+ }
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ phy_addr = simple_strtoull(ptr, NULL, 16);
+ pr_debug("[hwconfig]conti phy 0x%x\n", phy_addr);
+ }
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ size = simple_strtoul(ptr, NULL, 16);
+ pr_debug("[hwconfig]conti size 0x%x\n", size);
+ }
+ if (conti_reg_type == normal_type) {
+ conti_base_addr = ioremap_nocache(phy_addr, size);
+ } else {
+ conti_base_addr = phy_addr;
+ ana_size = size;
+ }
+ pr_debug("[hwconfig]conti addr 0x%x\n", conti_base_addr);
+
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ reg_base_addr = simple_strtoull(ptr, NULL, 16);
+ /*pr_debug("[hwconfig] trigger_
+ conti_reg_check reg_base_addr 0x%xbuffer %s\n"
+ , reg_base_addr, buffer);*/
+ }
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ reg_num = simple_strtoul(ptr, NULL, 10);
+ pr_debug("[hwconfig] conti reg_num %d\n", reg_num);
+ }
+
+ trigger_conti_reg_dump(idx_conti_reg);
+ hwconfig_sleep_flag = true;
+ hwconfig_resume_flag = true;
+
+ return 0;
+}
+
+
+
+
+static int trigger_regu_volt_dump(int mode)
+{
+
+ struct regulator *vdd_dev = NULL;
+ unsigned long volt;
+ int i = 0;
+ int r = 0;
+ int index = mode;
+
+ if (index >= ARRAY_SIZE(hwconfig_check_array))
+ pr_debug("trigger_sep_reg_dump index error %d\n", index);
+ memset(hwconfig_check_array[index].buffer,
+ 0x0, hwconfig_check_array[index].size);
+
+ for (i = 0; i < regu_name_index; i++) {
+ char temp[20] = {0};
+
+ vdd_dev = regulator_get(NULL, regu_name_array[i]);
+ if (IS_ERR(vdd_dev)) {
+ /*pr_debug("[hwconfig] trigger_reg_volt_
+ dump can't find the regu dev %s\n"
+ , regu_name_array[i]);*/
+ sprintf(temp, "%d,", 0);
+ } else {
+ volt = regulator_get_voltage(vdd_dev);
+ /*pr_debug("[hwconfig] trigger_regu_du
+ mp %s, volt %lu\n", regu_name_array[i], volt);*/
+ sprintf(temp, "%d,", volt);
+ }
+ strcat(hwconfig_check_array[index].buffer, temp);
+ }
+
+ hwconfig_check_array[index].ready = true;
+}
+
+int hwconfig_check_sleep_regu_volt(void)
+{
+ if (hwconfig_regu_volt_sleep_flag)
+ trigger_regu_volt_dump(idx_sleep_regu_volt);
+ return 0;
+}
+
+int hwconfig_check_resume_regu_volt(void)
+{
+ if (hwconfig_regu_volt_resume_flag)
+ trigger_regu_volt_dump(idx_resume_regu_volt);
+ return 0;
+}
+
+
+static int trigger_regu_volt_check(char *buffer)
+{
+ char *ptr;
+ char *ptr1;
+ unsigned long regu_num;
+ regu_name_index = 0;
+
+ hwconfig_check_array[idx_regu_volt].ready = false;
+ hwconfig_check_array[idx_sleep_regu_volt].ready = false;
+ hwconfig_check_array[idx_resume_regu_volt].ready = false;
+
+ while ((ptr = strsep(&buffer, ",")) != NULL) {
+ if (regu_name_index < MAX_REGU_ARRAY_NUM) {
+ /*pr_debug("[hwconfig] trigger_regu_volt_
+ check regu_name %s\n", ptr);*/
+ strcpy(regu_name_array[regu_name_index], ptr);
+ regu_name_index++;
+ } else {
+ pr_debug("[hwconfig] trigger_hwconfig_sep_reg_chec!!!\n");
+ break;
+ }
+ }
+ trigger_regu_volt_dump(idx_regu_volt);
+ hwconfig_regu_volt_sleep_flag = true;
+ hwconfig_regu_volt_resume_flag = true;
+}
+
+
+
+static int trigger_sep_reg_dump(int mode)
+{
+ int index = mode;
+ int i = 0;
+
+ if (index >= ARRAY_SIZE(hwconfig_check_array))
+ pr_debug("trigger_sep_reg_dump index error %d\n", index);
+ memset(hwconfig_check_array[index].buffer
+ , 0x0, hwconfig_check_array[index].size);
+
+ for (i = 0; i < sep_reg_index; i++) {
+ char temp[20] = {0};
+ long r;
+ if (sep_reg_type == normal_type)
+ r = hwconfig_read_reg(sep_base_addr+sep_reg_array[i]);
+ else
+ r = hwconfig_read_anareg(sep_reg_array[i]);
+
+ sprintf(temp, "0x%x,", r);
+ strcat(hwconfig_check_array[index].buffer, temp);
+ }
+ pr_debug("[hwconfig] sep %s\n", hwconfig_check_array[index].buffer);
+ hwconfig_check_array[index].ready = true;
+
+ return 0;
+
+}
+
+int hwconfig_check_sleep_sep_reg(void)
+{
+ if (hwconfig_sep_sleep_flag)
+ trigger_sep_reg_dump(idx_sleep_sep_reg);
+ return 0;
+}
+
+int hwconfig_check_resume_sep_reg(void)
+{
+ if (hwconfig_sep_resume_flag)
+ trigger_sep_reg_dump(idx_resume_sep_reg);
+ return 0;
+}
+
+
+
+
+static int trigger_sep_reg_check(char *buffer)
+{
+ char *ptr;
+ char *ptr_addr;
+ char sep_reg[256] = {0};
+ long reg_addr = 0;
+ unsigned long phy_addr;
+ unsigned long size;
+ unsigned long base_addr;
+
+ sep_reg_index = 0;
+ hwconfig_check_array[idx_sep_reg].ready = false;
+ hwconfig_check_array[idx_sleep_sep_reg].ready = false;
+ hwconfig_check_array[idx_resume_sep_reg].ready = false;
+
+ ptr = strsep(&buffer, ",");/*reg type 0:normal;1:ana reg*/
+ if (ptr) {
+ sep_reg_type = simple_strtoul(ptr, NULL, 10);
+ pr_debug("[hwconfig] sep type %d\n", sep_reg_type);
+ }
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ phy_addr = simple_strtoull(ptr, NULL, 16);
+ pr_debug("[hwconfig]sep phy_addr 0x%x\n", phy_addr);
+ }
+ ptr = strsep(&buffer, ",");
+ if (ptr) {
+ size = simple_strtoul(ptr, NULL, 16);
+ pr_debug("[hwconfig]sep size 0x%x\n", size);
+
+ }
+ if (sep_reg_type == normal_type) {
+ sep_base_addr = ioremap_nocache(phy_addr, size);
+ } else {
+ sep_base_addr = phy_addr;
+ ana_size = size;
+ }
+ pr_debug("[hwconfig]sep base 0x%08x\n", sep_base_addr);
+ while ((ptr = strsep(&buffer, ",")) != NULL) {
+ if (sep_reg_index < MAX_SEP_REG_NUM) {
+ reg_addr = simple_strtoul(ptr, NULL, 16);
+ sep_reg_array[sep_reg_index] = reg_addr;
+ sep_reg_index++;
+ } else {
+ pr_debug("[hwconfig]sep too many!!!\n");
+ break;
+ }
+
+ }
+ trigger_sep_reg_dump(idx_sep_reg);
+ hwconfig_sep_sleep_flag = true;
+ hwconfig_sep_resume_flag = true;
+
+ return 0;
+}
+
+static ssize_t hwconfig_write(struct file *file,
+ const char __user *buffer, size_t len, loff_t *offset)
+{
+ int idx = (int)file->private_data;
+ int ret = 0;
+
+ pr_debug("[hwconfig] hwconfig_write idx %d\n", idx);
+ if (idx < idx_regu_volt_setting)
+ return 0;
+
+ if (len > hwconfig_check_array[idx].size) {
+ pr_debug("[hwconfig] hwconfig_write len %d\n", len);
+ return 0;
+ }
+
+ memset(hwconfig_check_array[idx].buffer
+ , '\0', hwconfig_check_array[idx].size);
+ ret = simple_write_to_buffer(hwconfig_check_array[idx].buffer,
+ hwconfig_check_array[idx].size, offset, buffer, len);
+ pr_debug("[hwconfig] write idx %d, %s\n", idx,hwconfig_check_array[idx].buffer);
+ if (idx == idx_conti_reg_setting)
+ trigger_conti_reg_check(hwconfig_check_array[idx].buffer);
+ else if (idx == idx_sep_reg_setting)
+ trigger_sep_reg_check(hwconfig_check_array[idx].buffer);
+ else if (idx == idx_regu_volt_setting)
+ trigger_regu_volt_check(hwconfig_check_array[idx].buffer);
+
+ return ret;
+}
+
+static int chipid_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t chipid_read(struct file *file, char __user *buffer,
+ size_t len, loff_t *offset)
+{
+ char buf[64] = {0};
+ sprintf(buf, "0x%x,0x%x,", sci_get_chip_id(),
+ sci_get_ana_chip_id());
+ pr_debug("[hwconfig], %s\n", buf);
+ return simple_read_from_buffer(buffer, len, offset, buf, 64);
+}
+
+static ssize_t chipid_write(struct file *file, const char __user *buffer,
+ size_t len, loff_t *offset)
+{
+ return 0;
+}
+
+static int battery_notify(struct notifier_block *nb,
+ unsigned long mode, void *_unused)
+{
+ switch (mode) {
+ case PM_SUSPEND_PREPARE:
+ {
+ hwconfig_check_sleep_conti_reg();
+ hwconfig_check_sleep_sep_reg();
+ hwconfig_check_sleep_regu_volt();
+ }
+ return NOTIFY_OK;
+ case PM_POST_SUSPEND:
+ {
+ hwconfig_check_resume_conti_reg();
+ hwconfig_check_resume_sep_reg();
+ hwconfig_check_resume_regu_volt();
+ }
+ return NOTIFY_OK;
+ }
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations chipid_fops = {
+ .owner = THIS_MODULE,
+ .read = chipid_read,
+ .open = chipid_open,
+ .write = chipid_write,
+ .release = NULL,
+};
+
+static struct notifier_block hwconfig_nb;
+static int __init sprd_hwconfig_check_init(void)
+{
+ int i;
+ struct proc_dir_entry *entry;
+
+ hwconfig_entry = proc_mkdir(PROC_NAME, NULL);
+
+ if (!hwconfig_entry) {
+ /*pr_debug(KERN_ALERT "Error: Could not initialize
+ /proc/%s\n", PROC_NAME);*/
+ return -ENOMEM;
+ }
+ for (i = 0; i < ARRAY_SIZE(hwconfig_check_array); i++) {
+ entry = proc_create_data(hwconfig_check_array[i].name,
+ S_IRUGO|S_IWUGO, hwconfig_entry,
+ &hwconfig_check_fops, hwconfig_check_array[i].name);
+ hwconfig_check_array[i].buffer = kmalloc(
+ hwconfig_check_array[i].size, GFP_KERNEL);
+
+ if (NULL == hwconfig_check_array[i].buffer) {
+ pr_debug("[hwconfig] sprd_hwconfig_check_init malloc error\n");
+ return -1;
+ }
+ memset(hwconfig_check_array[i].buffer
+ , '\0', hwconfig_check_array[i].size);
+ hwconfig_check_array[i].ready = false;
+ }
+ proc_create_data("chipid", S_IRUGO|S_IWUGO, hwconfig_entry,
+ &chipid_fops, "chipid");
+ hwconfig_nb.notifier_call = battery_notify;
+ register_pm_notifier(&hwconfig_nb);
+ hwconfig_sleep_flag = false;
+ hwconfig_resume_flag = false;
+ hwconfig_sep_sleep_flag = false;
+ hwconfig_sep_resume_flag = false;
+ hwconfig_regu_volt_sleep_flag = false;
+ hwconfig_regu_volt_resume_flag = false;
+ regu_name_index = 0;
+ sep_reg_index = 0;
+ return 0;
+}
+late_initcall(sprd_hwconfig_check_init);
+
+static void sprd_hwconfig_check_exit(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(hwconfig_check_array); i++) {
+ remove_proc_entry(hwconfig_check_array[i].name, hwconfig_entry);
+ kfree(hwconfig_check_array[i].buffer);
+ }
+ remove_proc_entry(PROC_NAME, NULL);
+ unregister_pm_notifier(&hwconfig_nb);
+ pr_debug(KERN_INFO "/proc/%s removed\n", PROC_NAME);
+
+}
+
+module_exit(sprd_hwconfig_check_exit);
+MODULE_DESCRIPTION("SPREADTRUM HARDWARE CONFIGURATION CHECK");
+MODULE_LICENSE("GPL");