diff --git a/drivers/hisi/mntn/Kconfig b/drivers/hisi/mntn/Kconfig index c925f258e..f022b20c3 100755 --- a/drivers/hisi/mntn/Kconfig +++ b/drivers/hisi/mntn/Kconfig @@ -13,6 +13,7 @@ config HISILICON_PLATFORM_HISI_EASYSHELL source "drivers/hisi/mntn/blackbox/Kconfig" source "drivers/hisi/mntn/hisee/Kconfig" source "drivers/hisi/mntn/bl31/Kconfig" +source "drivers/hisi/mntn/subpmu/Kconfig" config HISI_VIRT_TO_PHYS diff --git a/drivers/hisi/mntn/Makefile b/drivers/hisi/mntn/Makefile index ffe9337be..1a1df2781 100755 --- a/drivers/hisi/mntn/Makefile +++ b/drivers/hisi/mntn/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_HISI_RECORD_SP) += mntn_record_sp.o obj-$(CONFIG_HISI_HW_SP) += mntn_hw_sp.o obj-$(CONFIG_HISI_L3CACHE_ECC) += mntn_l3cache_ecc.o obj-$(CONFIG_HISI_HISEE_MNTN) += hisee/ -obj-$(CONFIG_HISI_BL31_MNTN) += bl31/ \ No newline at end of file +obj-$(CONFIG_HISI_BL31_MNTN) += bl31/ +obj-$(CONFIG_HISI_SUBPMU) += subpmu/ diff --git a/drivers/hisi/mntn/subpmu/Kconfig b/drivers/hisi/mntn/subpmu/Kconfig new file mode 100755 index 000000000..7c11b41ba --- /dev/null +++ b/drivers/hisi/mntn/subpmu/Kconfig @@ -0,0 +1,5 @@ +config HISI_SUBPMU + bool "subpmu module support" + default n + help + Say yes here to support the subpmu module. diff --git a/drivers/hisi/mntn/subpmu/Makefile b/drivers/hisi/mntn/subpmu/Makefile new file mode 100755 index 000000000..64409bd8b --- /dev/null +++ b/drivers/hisi/mntn/subpmu/Makefile @@ -0,0 +1,2 @@ +ccflags-y += -Wall -Werror +obj-$(CONFIG_HISI_SUBPMU) += hisi_subpmu.o diff --git a/drivers/hisi/mntn/subpmu/hisi_subpmu.c b/drivers/hisi/mntn/subpmu/hisi_subpmu.c new file mode 100644 index 000000000..d1f8e1a94 --- /dev/null +++ b/drivers/hisi/mntn/subpmu/hisi_subpmu.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hisi_subpmu.h" + + +#define PMUSSI0_BASE_ADDR (0xFFF34000) +#define PMUSSI_ADDR(ch, offset) ((char *)PMUSSI0_BASE_ADDR + (ch) * 0x2000 + ((offset) << 2)) + +static int hisi_subpmu_read(struct pmu_para *pmu_info, u16 addr, unsigned char *value) +{ + unsigned char slave_id = pmu_info->slave_id; + + if (0 == strncmp(pmu_info->bus_type, "spmi", 4)) { + *value = hisi_pmic_read_sub_pmu(slave_id, addr); + } else if (0 == strncmp(pmu_info->bus_type, "ssi", 3)) { + *value = *(volatile unsigned char*)(PMUSSI_ADDR(slave_id, addr)); + } else { + return -1; + } + + return 0; +} + +static int hisi_subpmu_write(struct pmu_para *pmu_info, u16 addr, unsigned char value) +{ + unsigned char slave_id = pmu_info->slave_id; + + if (0 == strncmp(pmu_info->bus_type, "spmi", 4)) { + hisi_pmic_write_sub_pmu(slave_id, addr, value); + } else if (0 == strncmp(pmu_info->bus_type, "ssi", 3)) { + *(volatile unsigned char*)(PMUSSI_ADDR(slave_id, addr)) = value; + } else { + return -1; + } + + return 0; +} + +static int get_hi6422_dieid(struct pmu_para *pmu_info, unsigned char *die_id) +{ + int i, ret; + unsigned char val = 0, otp_reg = OTP_REG_BEGIN, id_reg = OTP_119_112, rst_reg = OTP_CLK_CTRL; + + /*step1 : set register-controlled read mode*/ + ret = hisi_subpmu_read(pmu_info, OTP_CTRL1, &val); + val = (val & AND_VALUE4) | 0x02; + ret += hisi_subpmu_write(pmu_info, OTP_CTRL1, val); + + /*step2 : open clock*/ + ret += hisi_subpmu_write(pmu_info, OTP_CLK_CTRL, OPEN_CLK_VALUE); + + /*step3 : set Normal Read mode*/ + ret += hisi_subpmu_read(pmu_info, OTP_CTRL2, &val); + val = val & AND_VALUE5; + ret += hisi_subpmu_write(pmu_info, OTP_CTRL2, val); + + /*step4 : set address in Normal Read mode from bit[119:112] to [175:168]*/ + for (i = 0; i < HI6422_OTP_REG_NUM; i++) { + ret += hisi_subpmu_read(pmu_info, OTP_CTRL2, &val); + val = (val & AND_VALUE6) | otp_reg; + ret += hisi_subpmu_write(pmu_info, OTP_CTRL2, val); + otp_reg = otp_reg + 2; + + /*step5 : enable read*/ + ret += hisi_subpmu_read(pmu_info, OTP_CTRL0, &val); + val = (val & AND_VALUE2) | 0x04; + ret += hisi_subpmu_write(pmu_info, OTP_CTRL0, val); + + /*step6 : rescind read_enable signal*/ + udelay(DELAY_TIME); + ret += hisi_subpmu_read(pmu_info, OTP_CTRL0, &val); + val = val & AND_VALUE2; + ret += hisi_subpmu_write(pmu_info, OTP_CTRL0, val); + } + + /*step7 : check out data*/ + for (i = 0; i < HI6422_OTP_REG_NUM; i++) { + ret += hisi_subpmu_read(pmu_info, id_reg, die_id); + id_reg++; + die_id++; + } + + /*step8 : reset registers to default value*/ + for (i = 0; i < RST_REG_NUM; i++) { + ret += hisi_subpmu_write(pmu_info, rst_reg, RESET_VALUE); + rst_reg++; + } + + return ret; +} + +static int get_hi6423_dieid(struct pmu_para *pmu_info, unsigned char *die_id) +{ + int i, ret; + unsigned char val = 0, id_reg = ID_OTP_0; + + /*clock open */ + ret = hisi_subpmu_write(pmu_info, ID_OTP_CLK_CTRL, OPEN_CLK_VALUE); + + /* step1 : set id_otp_ptm = 2'b00, id_otp_pprog = 1'b0, enter in read mode*/ + ret += hisi_subpmu_read(pmu_info, ID_OTP_CTRL1, &val); + val = val & AND_VALUE1; + ret += hisi_subpmu_write(pmu_info, ID_OTP_CTRL1, val); + + /* step2 : set id_otp_pa = 2'b00, address writed to register*/ + ret += hisi_subpmu_read(pmu_info, ID_OTP_CTRL1, &val); + val = val & AND_VALUE3; + ret += hisi_subpmu_write(pmu_info, ID_OTP_CTRL1, val); + + /* step3 : set id_por_int = 1'b1, enable read mode*/ + ret += hisi_subpmu_read(pmu_info, ID_OTP_CTRL0, &val); + val = (val & AND_VALUE2) | 0x4; + ret += hisi_subpmu_write(pmu_info, ID_OTP_CTRL0, val); + + /* step4 : delay 30us, then set id_otp_por_int = 1'b0 to cancel the read mode enable signal */ + udelay(DELAY_TIME); + ret += hisi_subpmu_read(pmu_info, ID_OTP_CTRL0, &val); + val = val & AND_VALUE2; + ret += hisi_subpmu_write(pmu_info, ID_OTP_CTRL0, val); + + /* step5 : read from registers ID_OTP_0~3*/ + for (i = 0; i < HI6423_READ_TIMES; i++) { + ret += hisi_subpmu_read(pmu_info, id_reg, die_id); + id_reg++; + die_id++; + } + + /*clock close */ + ret += hisi_subpmu_write(pmu_info, ID_OTP_CLK_CTRL, RESET_VALUE); + + return ret; +} + + +static int get_subpmu_tree_data(struct device_node *root, struct hisi_subpmu_dieid *subpmu) +{ + int ret = 0, i = 0; + struct device_node *child = NULL; + + subpmu->dieid_num = of_get_child_count(root); + subpmu->pmu_info = kzalloc(sizeof(struct pmu_para) * (subpmu->dieid_num), GFP_KERNEL); + if (!subpmu->pmu_info) { + pr_err("%s: alloc subpmu->pmu_info fail\n", __func__); + return -ENOMEM; + } + + while (NULL != (child = of_get_next_child(root, child))) { + ret = of_property_read_string(child, "pmu-type", &(subpmu->pmu_info[i].pmu_type)); + if (ret) { + pr_err("%s:no %s pmu-type property set!\n", __func__, child->name); + ret = -ENODEV; + goto err_out; + } + + ret = of_property_read_string(child, "bus-type", &(subpmu->pmu_info[i].bus_type)); + if (ret) { + pr_err("%s:no %s bus-type property set!\n", __func__, child->name); + ret = -ENODEV; + goto err_out; + } + + ret = of_property_read_u32(child, "slave-id", &(subpmu->pmu_info[i].slave_id)); + if (ret) { + pr_err("%s:no %s slave-id property set!\n", __func__, child->name); + ret = -ENODEV; + goto err_out; + } + + i++; + } + + return 0; + +err_out: + kfree(subpmu->pmu_info); + subpmu->pmu_info = NULL; + return ret; +} + +struct pmu_dieid_func_def pmu_dieid_func[] = { + {"Hi6422v300", (pmu_func_t)get_hi6422_dieid}, + {"Hi6423v100", (pmu_func_t)get_hi6423_dieid}, +}; + +static int get_dieid(struct pmu_para *pmu_info, unsigned char *ip) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < sizeof(pmu_dieid_func)/sizeof(pmu_dieid_func[0]); i++) { + if (0 == strncmp(pmu_info->pmu_type, pmu_dieid_func[i].pmu_type, strlen(pmu_info->pmu_type))) { + ret = (*(pmu_dieid_func[i].pmu_func))(pmu_info, ip); + if (ret) + pr_err("%s:get_%s_dieid_fail!\n", __func__, pmu_info->pmu_type); + + return ret; + } + } + + pr_err("%s: %s no pmu_type matched!\n", __func__, pmu_info->pmu_type); + return -1; +} + +static int dieid_package(struct pmu_para *pmu_info, int idx, char *pmu_buf) +{ + int i, ret; + char tmp[HISI_PMU_TMP_BUF] = {0}; + unsigned char ip[HISI_PMU_IP_SIZE] = {0}; + + ret = snprintf(pmu_buf, HISI_PMU_BUF_SIZE, "\r\n%s_PMU%d:0x", pmu_info->pmu_type, idx);/* [false alarm]:snprintf is safe *//* unsafe_function_ignore: snprintf */ + if (ret < 0) { + pr_err("%s:snprintf error!\n", __func__); + return ret; + } + + ret = get_dieid(pmu_info, ip); + if (ret) + return -1; + + for (i = HISI_PMU_IP_SIZE - 1; i >= 0; i--) { + ret = snprintf(tmp, sizeof(tmp), "%02x", ip[i]);/* unsafe_function_ignore: snprintf */ + if (ret < 0) { + pr_err("%s:snprintf error!\n", __func__); + return ret; + } + strncat(pmu_buf, tmp, strlen(tmp));/* unsafe_function_ignore: strncat */ + memset(tmp, 0, strlen(tmp));/* unsafe_function_ignore: memset */ + } + + strncat(pmu_buf, "\r\n", strlen("\r\n"));/* unsafe_function_ignore: strncat */ + return 0; +} + +int hisi_subpmu_get_dieid(char *dieid, unsigned int len) +{ + int ret, pmu_idx = 2; + unsigned int i; + char pmu_buf[HISI_PMU_TMP_BUF]={0}; + char dieid_buf[HISI_PMU_BUF_SIZE] = {0}; + struct device_node *root = NULL; + struct hisi_subpmu_dieid subpmu; + + if (NULL == dieid) { + pr_err("%s:dieid is NULL!\n", __func__); + return -ENOMEM; + } + + root = of_find_compatible_node(NULL, NULL, "hisilicon,subpmu-dieid"); + if (!root) { + pr_err("%s:no hisi-subpmu-dieid root node!\n", __func__); + return -ENODEV; + } + + if (!of_device_is_available(root)) { + pr_err("%s:no dieid!\n", __func__); + return -1; + } + + ret = get_subpmu_tree_data(root, &subpmu); + if (ret) { + pr_err("%s:Error reading subpmu_dieid dts!\n", __func__); + return ret; + } + + for (i = 0; i < subpmu.dieid_num; i++) { + ret = dieid_package(&(subpmu.pmu_info[i]), pmu_idx, pmu_buf); + if (ret < 0) { + ret = -EINVAL; + goto out; + } + + strncat(dieid_buf, pmu_buf, strlen(pmu_buf));/* unsafe_function_ignore: strncat */ + memset(pmu_buf, 0, HISI_PMU_TMP_BUF);/* unsafe_function_ignore: memset */ + pmu_idx++; + } + + if(len >= strlen(dieid_buf)) { + strncpy(dieid, dieid_buf, strlen(dieid_buf));/* unsafe_function_ignore: strncpy */ + ret = 0; + }else{ + pr_err("%s:dieid length is not enough!\n", __func__); + ret = strlen(dieid_buf); + } + +out: + kfree(subpmu.pmu_info); + subpmu.pmu_info = NULL; + return ret; +} diff --git a/drivers/hisi/mntn/subpmu/hisi_subpmu.h b/drivers/hisi/mntn/subpmu/hisi_subpmu.h new file mode 100644 index 000000000..30d9d95ee --- /dev/null +++ b/drivers/hisi/mntn/subpmu/hisi_subpmu.h @@ -0,0 +1,58 @@ +#ifndef __HISI_SUBPMU_H__ +#define __HISI_SUBPMU_H__ + +#ifdef CONFIG_HISI_DIEID +#define ID_OTP_CLK_CTRL 0x54 +#define ID_OTP_CTRL0 0x51 +#define ID_OTP_CTRL1 0x52 +#define ID_OTP_0 0x4d +#define HI6423_READ_TIMES 4 + +/*define hi6423v100 & hi6422v300 commen para*/ +#define OPEN_CLK_VALUE 0x5a +#define RESET_VALUE 0x00 +#define DELAY_TIME 30 +#define AND_VALUE1 0xe3 +#define AND_VALUE2 0xfb +#define AND_VALUE3 0xfc +#define AND_VALUE4 0xfd +#define AND_VALUE5 0x3f +#define AND_VALUE6 0xc0 + +#define HISI_PMU_TMP_BUF 50 +#define HISI_PMU_IP_SIZE 8 +#define HISI_PMU_BUF_SIZE 100 + +/*define hi6422v300 register*/ +#define OTP_119_112 0xf6 +#define OTP_REG_BEGIN 0x0c +#define HI6422_OTP_REG_NUM 8 +#define RST_REG_NUM 4 + +enum hi6422v300_reg { + OTP_CLK_CTRL = 0xC0, + OTP_CTRL0, + OTP_CTRL1, + OTP_CTRL2, +}; + +struct pmu_para { + const char *pmu_type; + const char *bus_type; + unsigned int slave_id; +}; + +struct hisi_subpmu_dieid { + unsigned int dieid_num; + struct pmu_para *pmu_info; +}; + +typedef int (*pmu_func_t)(struct pmu_para *pmu_info, unsigned char *die_id); +struct pmu_dieid_func_def { + const char *pmu_type; + pmu_func_t pmu_func; +}; + +int hisi_subpmu_get_dieid(char *dieid, unsigned int len); +#endif +#endif