From eb8ab7c349632add37514b09e4761bdce379cead Mon Sep 17 00:00:00 2001 From: yangwei1 Date: Thu, 23 May 2024 15:15:28 +0800 Subject: [PATCH 019/219] feat:support mpq8785 to set npu voltage Changelogs: --- arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts | 13 +- arch/riscv/boot/dts/eswin/eic7700-evb.dts | 11 +- arch/riscv/configs/win2030_defconfig | 1 + drivers/regulator/Kconfig | 6 + drivers/regulator/Makefile | 2 +- drivers/regulator/mpq8785.c | 1217 ++++++++++++++++++ 6 files changed, 1239 insertions(+), 11 deletions(-) create mode 100644 drivers/regulator/mpq8785.c diff --git a/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts b/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts index 00154660ab7c..cddfc166e851 100644 --- a/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts +++ b/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts @@ -724,18 +724,19 @@ d0_codec1_endpoint: endpoint { &d0_i2c1 { /* mpq8785 */ status = "okay"; - eswin,syscfg = <&d0_sys_con 0x3C0 15>; - iic_hold_time = <0x40>; mpq8785@10 { compatible = "mps,mpq8785"; reg = <0x10>; + eswin,regulator_default-microvolt=<1000000>; + eswin,regulator_label = "supply vdd1", "npu vdd1", "npu current1", "npu temperature1"; regulators{ npu_vcc1:npu_svcc{ regulator-name="NPU_SVCC"; - regulator-min-microvolt=<100000>; - regulator-max-microvolt=<1600000>; - regulator-min-microamp=<50000000>; - regulator-max-microamp=<90000000>; + regulator-min-microvolt=<700000>; + regulator-max-microvolt=<1100000>; + regulator-min-microamp=<20000000>; + regulator-max-microamp=<40000000>; + regulator-ov-protection-microvolt=<1100000>; regulator-always-on; }; }; diff --git a/arch/riscv/boot/dts/eswin/eic7700-evb.dts b/arch/riscv/boot/dts/eswin/eic7700-evb.dts index 7a9883a89dc3..34bc531d58f9 100644 --- a/arch/riscv/boot/dts/eswin/eic7700-evb.dts +++ b/arch/riscv/boot/dts/eswin/eic7700-evb.dts @@ -760,13 +760,16 @@ &d0_aon_i2c1 { mpq8785@10 { compatible = "mps,mpq8785"; reg = <0x10>; + eswin,regulator_default-microvolt=<1000000>; + eswin,regulator_label = "supply vdd1", "npu vdd1", "npu current1", "npu temperature1"; regulators{ npu_vcc1:npu_svcc{ regulator-name="NPU_SVCC"; - regulator-min-microvolt=<100000>; - regulator-max-microvolt=<1600000>; - regulator-min-microamp=<50000000>; - regulator-max-microamp=<90000000>; + regulator-min-microvolt=<700000>; + regulator-max-microvolt=<1100000>; + regulator-min-microamp=<20000000>; + regulator-max-microamp=<40000000>; + regulator-ov-protection-microvolt=<1100000>; regulator-always-on; }; }; diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig index 5d25e67b935d..7f8075d825bc 100644 --- a/arch/riscv/configs/win2030_defconfig +++ b/arch/riscv/configs/win2030_defconfig @@ -150,6 +150,7 @@ CONFIG_SENSORS_INA2XX=y CONFIG_WATCHDOG=y CONFIG_DW_WATCHDOG=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_MPQ8785=y # CONFIG_MEDIA_CEC_SUPPORT is not set CONFIG_MEDIA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 965d4f0c18a6..512ca2e53f1b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -795,6 +795,12 @@ config REGULATOR_MPQ7920 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_MPQ8785 + tristate "mps,mpq8785" + help + This driver provides support for the voltage regulators on the + eswin evb. + config REGULATOR_MT6311 tristate "MediaTek MT6311 PMIC" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 23074714a81a..a0b29a60d7f8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -195,5 +195,5 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o - +obj-$(CONFIG_REGULATOR_MPQ8785) += mpq8785.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/mpq8785.c b/drivers/regulator/mpq8785.c new file mode 100644 index 000000000000..60c48ae47d31 --- /dev/null +++ b/drivers/regulator/mpq8785.c @@ -0,0 +1,1217 @@ + +// SPDX-License-Identifier: GPL-2.0 +/* + * eswin Specific Glue layer + * + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights + * reserved. SPDX-License-Identifier: GPL-2.0 + * + * 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, version 2. + * + * 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, see . + * + * Authors: Yang Wei + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPQ8785_CMD_PAGE 0x0 +#define MPQ8785_CMD_OPERATION 0x1 +#define MPQ8785_CMD_ON_OFF_CONFIG 0x2 +#define MPQ8785_CMD_CLEAR_FAULT 0x3 +#define MPQ8785_CMD_CLEAR_LAST_FAULT 0x8 +#define MPQ8785_CMD_LAST_FAULT_RESTORE 0xc +#define MPQ8785_CMD_WRITE_PROTECTION 0x10 +#define MPQ8785_CMD_STORE_ALL 0x15 +#define MPQ8785_CMD_RESTORE_ALL 0x16 +#define MPQ8785_CMD_CAPABILITY 0x19 +#define MPQ8785_CMD_PMBUS_PS_NUM 0x1C +#define MPQ8785_CMD_VOUT_MODE 0x20 +#define MPQ8785_CMD_VOUT_COMMAND 0x21 +#define MPQ8785_CMD_VOUT_MAX 0x24 +#define MPQ8785_CMD_VOUT_MARGIN_HIGH 0x25 +#define MPQ8785_CMD_VOUT_MARGIN_LOW 0x26 +#define MPQ8785_CMD_VOUT_SCALE_LOOP 0x29 +#define MPQ8785_CMD_VOUT_MIN 0x2B +#define MPQ8785_CMD_COEFFICIENT 0x30 +#define MPQ8785_CMD_VIN_ON 0x35 +#define MPQ8785_CMD_VIN_OFF 0x36 +#define MPQ8785_CMD_IOUT_CAL_GAIN 0x38 +#define MPQ8785_CMD_IOUT_CAL_OFFSET 0x39 +#define MPQ8785_CMD_IOUT_OC_FAULT_LIMIT 0x46 +#define MPQ8785_CMD_IOUT_OC_WARN_LIMIT 0x4A +#define MPQ8785_CMD_VBOOT_SET_FOR_XOh_ADDR 0x4D +#define MPQ8785_CMD_OT_FAULT_LIMIT 0x4F +#define MPQ8785_CMD_OT_WARN_LIMIT 0x51 +#define MPQ8785_CMD_VIN_OV_FAULT_LIMIT 0x55 +#define MPQ8785_CMD_VIN_OV_WARN_LIMIT 0x57 +#define MPQ8785_CMD_VBOOT_SET_FOR_X4h_ADDR 0x5E +#define MPQ8785_CMD_VBOOT_SET_FOR_X8h_ADDR 0x5F +#define MPQ8785_CMD_TON_DELAY 0x60 +#define MPQ8785_CMD_TON_RISE 0x61 +#define MPQ8785_CMD_TOFF_DELAY 0x64 +#define MPQ8785_CMD_TOFF_FALL 0x65 +#define MPQ8785_CMD_VBOOT_SET_FOR_XEh_ADDR 0x6A +#define MPQ8785_CMD_STATUS_WORD 0x79 +#define MPQ8785_CMD_STATUS_VOUT 0x7A +#define MPQ8785_CMD_STATUS_IOUT 0x7B +#define MPQ8785_CMD_STATUS_INPUT 0x7C +#define MPQ8785_CMD_STATUS_TEMPERATURE 0x7D +#define MPQ8785_CMD_STATUS_CML 0x7E +#define MPQ8785_CMD_REV_ID 0x80 +#define MPQ8785_CMD_READ_VIN 0x88 +#define MPQ8785_CMD_READ_VOUT 0x8B +#define MPQ8785_CMD_READ_IOUT 0x8C +#define MPQ8785_CMD_READ_TEMPERATURE 0x8D +#define MPQ8785_CMD_PMBUS_REV_CONST 0x98 +#define MPQ8785_CMD_MFR_ID 0x99 +#define MPQ8785_CMD_MFR_REVISION 0x9B +#define MPQ8785_CMD_MFR_CONFIG_ID 0xC0 +#define MPQ8785_CMD_MFR_CONFIG_CODE_REV 0xC1 +#define MPQ8785_CMD_MFR_PRODUCT_REV_USER 0xC2 +#define MPQ8785_CMD_MFR_SILICON_REV 0xC3 +#define MPQ8785_CMD_MFR_APS_LEVEL 0xC5 +#define MPQ8785_CMD_MFR_CONFIG_A 0xD0 +#define MPQ8785_CMD_MFR_FS_CFG 0xD1 +#define MPQ8785_CMD_MFR_ADDR_PMBUS 0xD2 +#define MPQ8785_CMD_MFR_VOUT_RATE 0xD3 +#define MPQ8785_CMD_MFR_PWM_TIME_CFG 0xD4 +#define MPQ8785_CMD_MFR_PWM_TIME_CFG2 0xD5 +#define MPQ8785_CMD_MFR_PHASE_BLANK_TIME 0xD6 +#define MPQ8785_CMD_MFR_PHASE_SLOPE_BLANK_TIME 0xD7 +#define MPQ8785_CMD_MFR_SLOPE_BLANK_TIME 0xD8 +#define MPQ8785_CMD_MFR_BLANK_TIME_LV 0xD9 +#define MPQ8785_CMD_MFR_SLOPE_CNT_DCM 0xDA +#define MPQ8785_CMD_MFR_SLOPE_SR_DCM 0xDB +#define MPQ8785_CMD_MFR_SW_BLOCK_LIMIT 0xDC +#define MPQ8785_CMD_MFR_VCOMP 0xDD +#define MPQ8785_CMD_MFR_DROOP_CFG 0xDE +#define MPQ8785_CMD_MFR_CONFIG_B 0xDF +#define MPQ8785_CMD_MFR_DC_LOOP_CTRL 0xE0 +#define MPQ8785_CMD_MFR_CB_LOOP_CTRL 0xE1 +#define MPQ8785_CMD_MFR_FS_LOOP_CTRL 0xE2 +#define MPQ8785_CMD_MFR_VIN_CFG 0xE3 +#define MPQ8785_CMD_MFR_VIN_SCALE 0xE4 +#define MPQ8785_CMD_MFR_TEMP_TUNE 0xE5 +#define MPQ8785_CMD_MFR_PROTECT_CFG 0xE6 +#define MPQ8785_CMD_MFR_PROTECT_LEVEL 0xE7 +#define MPQ8785_CMD_MFR_PRT_DELAY 0xE8 +#define MPQ8785_CMD_SMBALERT_MASK 0xE9 +#define MPQ8785_CMD_MFR_NOCP_OCP_SET 0xEA +#define MPQ8785_CMD_MFR_LEVEL_SEL2 0xEB +#define MPQ8785_CMD_MFR_PG_CFG 0xEC +#define MPQ8785_CMD_MFR_PS_CTRL 0xED +#define MPQ8785_CMD_MFR_PMBUS_LOCK 0xEE +#define MPQ8785_CMD_MFR_SET_SYNC_CFG 0xEF +#define MPQ8785_CMD_MFR_SLAVE_PROTECT 0xF0 +#define MPQ8785_CMD_MFR_CTRL 0xF1 +#define MPQ8785_CMD_MFR_AUTO_SLOPE_CFG 0xF2 +#define MPQ8785_CMD_MFR_SLOPE_DELTA_LIMIT 0xF3 +#define MPQ8785_CMD_MFR_RETRY_TIMES 0xF4 +#define MPQ8785_CMD_MFR_CFG_EXT 0xF5 +#define MPQ8785_CMD_MFR_CDROOP_SET 0xF6 +#define MPQ8785_CMD_MFR_CFG_BACKUP 0xF7 +#define MPQ8785_CMD_CHECK_SUM_FUNC 0xF8 +#define MPQ8785_CMD_PROTECTION 0xFA +#define MPQ8785_CMD_PROTECTION_LAST 0xFB +#define MPQ8785_CMD_MFR_VBOOT_CFG 0xFC +#define MPQ8785_CMD_CLEAR_NVM_FAULT 0xFE + +#define MPQ8785_PMBUS_EXTRA_READ_FLAG (1 << 7) +#define MPQ8785_LABEL_CNT 4 + +struct MPQ8785_DRIVER_DATA +{ + u32 volt_format; + u32 volt_numerator; + struct regulator_dev *rdev; + struct regulator_desc *dev_desc; + struct i2c_client *client; + struct mutex config_lock; + char mpq8785_label[MPQ8785_LABEL_CNT][16]; +}; + +#define MPQ8785_MASK_OPERATION_ENABLE 0X80 +#define MPQ8785_MASK_VIN_OV_FAULT 0x7f +#define MPQ8785_MASK_VOUT_LIMIT 0xFFF +#define MPQ8785_MASK_VOUT_VALUE 0xFFF +#define MPQ8785_MASK_IOUT_LIMIT 0x3FF +#define MPQ8785_MASK_TOUT_LIMIT 0xFF +#define MPQ8785_MASK_SW_FREQ_FREQ 0x1FF + +// Voltage LSB= {125/64, 125/80, 125/80, +// 125/80}; 1.953125mV/1.5625mV/1.5625mV/1.5625mV +#define MPQ8785_VOLT_DENOMINATOR 125 +#define MPQ8785_VOLT_DENOMINATOR_HALF (MPQ8785_VOLT_DENOMINATOR >> 1) +// Voltage_in LSB=200mV +#define MPQ8785_VOLT_IN_LSB 200 +#define MPQ8785_VOLT_IN_LSB_HALF (MPQ8785_VOLT_IN_LSB >> 1) + +// sense_Voltage_in LSB=25mV +#define MPQ8785_VOLTE_IN_SENSE_LSB 25 + +/* current*2 LSB=125mA */ +#define MPQ8785_CURRENT_LSB_DENOMINATOR 125 +#define MPQ8785_CURRENT_LSB_DENOMINATOR_HALF (MPQ8785_CURRENT_LSB_DENOMINATOR >> 1) +#define MPQ8785_CURRENT_LSB_NUMERATOR_BIT 1 // 2 + +#define MPQ8785_REGULATOT_CURRENT_LSB 1000000 /*1uA*/ +#define MPQ8785_CURRENT_OUT_LSB 1000 /*1mA*/ + +/*mini sw frequency is 300kHz, frequency lsb = 10kHz */ +#define MPQ8785_FREQUENCY_LSB 10 +#define MPQ8785_FREQUENCY_BASE_MINI 300 /* 300kHz=30*10kHz */ +#define MPQ8785_FREQUENCY_BASE_MAX 2000 + +static u32 garr_volt_numerator[] = {64, 80, 80, 80}; +static char garr_bool_string[][2] = {"N", "Y"}; +static char garr_speed_string[][8] = {"100kHz", "400kHz", "1MHz", "resv"}; + +static struct of_regulator_match mpq8785_matches[] = { + { + .name = "npu_svcc", + }, +}; + +static inline u32 mpq8785_volt2reg(u32 vlot, u32 volt_numerator) +{ + u32 value = 0; + + value = (vlot * volt_numerator + MPQ8785_VOLT_DENOMINATOR_HALF) / + MPQ8785_VOLT_DENOMINATOR; + return value; +} + +static inline u32 mpq8785_reg2volt(u32 value, u32 volt_numerator) +{ + u32 vlot = 0; + + vlot = value * MPQ8785_VOLT_DENOMINATOR / volt_numerator; + + return vlot; +} + +static inline s32 mpq8785_str2ul(const char *buf, u32 *value) +{ + unsigned long cache = 0; + int ret = 0; + + if (NULL == strstr(buf, "0x")) + { + ret = kstrtoul(buf, 10, &cache); + } + else + { + ret = kstrtoul(buf, 16, &cache); + } + *value = cache; + + return ret; +} + +static u8 mpq8785_read_byte(struct MPQ8785_DRIVER_DATA *data, u8 command) +{ + int ret = 0; + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_byte_data(data->client, command); + mutex_unlock(&data->config_lock); + if (ret < 0) + { + dev_err(&data->client->dev, "get command:0x%x value error:%d\n", command, + ret); + return 0xff; + } + return (u8)ret; +} + +static s32 mpq8785_write_byte(struct MPQ8785_DRIVER_DATA *data, u8 command, + u8 val) +{ + int ret = 0; + mutex_lock(&data->config_lock); + ret = i2c_smbus_write_byte_data(data->client, command, val); + mutex_unlock(&data->config_lock); + if (ret < 0) + { + dev_err(&data->client->dev, "set command:0x%x value:0x%x error:%d\n", + command, val, ret); + } + return ret; +} + +static s32 mpq8785_update_byte(struct MPQ8785_DRIVER_DATA *data, u8 command, + u8 mask, u8 val) +{ + u8 old_value = 0; + u8 new_value = 0; + if (0 != (~mask & val)) + { + dev_err(&data->client->dev, "command:0x%x,input:0x%x outrange mask:0x%x\n", + command, val, mask); + return -EINVAL; + } + old_value = mpq8785_read_byte(data, command); + new_value = ~mask & old_value; + new_value = new_value | val; + return mpq8785_write_byte(data, command, new_value); +} + +static u16 mpq8785_read_word(struct MPQ8785_DRIVER_DATA *data, u8 command) +{ + int ret = 0; + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_word_data(data->client, command); + mutex_unlock(&data->config_lock); + if (ret < 0) + { + dev_err(&data->client->dev, "get command:0x%x value error:%d\n", command, + ret); + return 0xffff; + } + return (u16)ret; +} + +static u16 mpq8785_read_mask_word(struct MPQ8785_DRIVER_DATA *data, u8 command, + u16 mask) +{ + u16 ret = mpq8785_read_word(data, command); + return (ret & mask); +} + +static s32 mpq8785_write_word(struct MPQ8785_DRIVER_DATA *data, u8 command, + u16 val) +{ + int ret = 0; + mutex_lock(&data->config_lock); + ret = i2c_smbus_write_word_data(data->client, command, val); + mutex_unlock(&data->config_lock); + if (ret < 0) + { + dev_err(&data->client->dev, "set command:0x%x value:0x%x error:%d\n", + command, val, ret); + } + return ret; +} + +static s32 mpq8785_update_word(struct MPQ8785_DRIVER_DATA *data, u8 command, + u16 mask, u16 val) +{ + u16 old_value = 0; + u16 new_value = 0; + if (0 != (~mask & val)) + { + dev_err(&data->client->dev, "command:0x%x,input:0x%x outrange mask:0x%x\n", + command, val, mask); + return -EINVAL; + } + old_value = mpq8785_read_word(data, command); + new_value = ~mask & old_value; + new_value = new_value | val; + return mpq8785_write_word(data, command, new_value); +} + +static int mpq8785_read_block(struct MPQ8785_DRIVER_DATA *data, u8 command, + u8 *buf, u8 len, bool extra_len) +{ + int ret = 0; + int num = 0; + + if (extra_len) + { + len++; + } + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_i2c_block_data(data->client, command, len, buf); + mutex_unlock(&data->config_lock); + if (ret < 0) + { + dev_err(&data->client->dev, "get command:0x%x value error:%d\n", command, + ret); + return -EIO; + } + if (extra_len) + { + for (num = 0; num < (len - 1); num++) + { + buf[num] = buf[num + 1]; + } + buf[num] = 0; + } + return 0; +} + +static int mpq8785_get_enable(struct MPQ8785_DRIVER_DATA *data) +{ + u8 cache = 0; + + cache = mpq8785_read_byte(data, MPQ8785_CMD_OPERATION); + + return ((cache >> 7) & 0x1); +} + +static const struct hwmon_channel_info *mpq8785_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MAX_ALARM | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_MAX | HWMON_I_MAX_ALARM | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL), + NULL}; + +static umode_t mpq8785_is_visible(const void *_data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_input: + case hwmon_in_label: + case hwmon_in_max_alarm: + return 0444; + case hwmon_in_enable: + case hwmon_in_min: + case hwmon_in_max: + return 0644; + } + + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_input: + case hwmon_curr_crit_alarm: + case hwmon_curr_label: + return 0444; + case hwmon_curr_crit: + case hwmon_curr_max: + return 0644; + } + break; + case hwmon_temp: + switch (attr) + { + case hwmon_temp_input: + case hwmon_temp_label: + case hwmon_temp_crit_alarm: + return 0444; + case hwmon_temp_max: + case hwmon_temp_crit: + return 0644; + } + break; + + default: + break; + } + return 0; +} + +static int mpq8785_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 get_value = 0; + + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_input: + if (channel == 0) + { + get_value = mpq8785_read_word(data, MPQ8785_CMD_READ_VIN); + *val = get_value * MPQ8785_VOLTE_IN_SENSE_LSB; + } + else if (channel == 1) + { + get_value = mpq8785_read_word(data, MPQ8785_CMD_READ_VOUT); + *val = mpq8785_reg2volt(get_value, data->volt_numerator); + } + else + { + dev_err(dev, "not support channel%d\n", channel); + } + break; + case hwmon_in_max_alarm: + get_value = mpq8785_read_word(data, MPQ8785_CMD_STATUS_WORD); + if (channel == 0) + { + *val = ((get_value >> 13) & 0x1); + } + else if (channel == 1) + { + *val = ((get_value >> 15) & 0x1); + } + else + { + dev_err(dev, "not support channel%d\n", channel); + } + break; + case hwmon_in_enable: + *val = mpq8785_get_enable(data); + break; + case hwmon_in_min: + get_value = mpq8785_read_mask_word(data, MPQ8785_CMD_VOUT_MIN, + MPQ8785_MASK_VOUT_LIMIT); + *val = mpq8785_reg2volt(get_value, data->volt_numerator); + break; + case hwmon_in_max: + if (channel == 0) + { + get_value = + mpq8785_read_mask_word(data, MPQ8785_CMD_VIN_OV_FAULT_LIMIT, + MPQ8785_MASK_VIN_OV_FAULT); + *val = get_value * MPQ8785_VOLT_IN_LSB; + } + else if (channel == 1) + { + get_value = mpq8785_read_mask_word(data, MPQ8785_CMD_VOUT_MAX, + MPQ8785_MASK_VOUT_LIMIT); + *val = mpq8785_reg2volt(get_value, data->volt_numerator); + } + else + { + dev_err(dev, "not support channel%d\n", channel); + } + break; + } + + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_input: + get_value = mpq8785_read_word(data, MPQ8785_CMD_READ_IOUT); + *val = (get_value * MPQ8785_CURRENT_LSB_DENOMINATOR) >> + MPQ8785_CURRENT_LSB_NUMERATOR_BIT; + break; + case hwmon_curr_crit_alarm: + get_value = mpq8785_read_word(data, MPQ8785_CMD_STATUS_WORD); + if (channel == 0) + { + *val = ((get_value >> 4) & 0x1); + } + break; + case hwmon_curr_crit: + *val = mpq8785_read_mask_word(data, MPQ8785_CMD_IOUT_OC_FAULT_LIMIT, + MPQ8785_MASK_IOUT_LIMIT); + + break; + case hwmon_curr_max: + *val = mpq8785_read_mask_word(data, MPQ8785_CMD_IOUT_OC_WARN_LIMIT, + MPQ8785_MASK_IOUT_LIMIT); + + break; + } + break; + case hwmon_temp: + switch (attr) + { + case hwmon_temp_input: + *val = mpq8785_read_byte(data, MPQ8785_CMD_READ_TEMPERATURE); + break; + case hwmon_temp_crit_alarm: + get_value = mpq8785_read_word(data, MPQ8785_CMD_STATUS_WORD); + if (channel == 0) + { + *val = ((get_value >> 2) & 0x1); + } + break; + case hwmon_temp_max: + *val = mpq8785_read_mask_word(data, MPQ8785_CMD_OT_WARN_LIMIT, + MPQ8785_MASK_TOUT_LIMIT); + + break; + case hwmon_temp_crit: + *val = mpq8785_read_mask_word(data, MPQ8785_CMD_OT_FAULT_LIMIT, + MPQ8785_MASK_TOUT_LIMIT); + + break; + } + break; + + default: + break; + } + return 0; +} + +static int mpq8785_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct i2c_client *client = to_i2c_client(dev); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_label: + if (channel == 0) + { + *str = data->mpq8785_label[0]; + } + else if (channel == 1) + { + *str = data->mpq8785_label[1]; + } + else + { + dev_err(dev, "not support channel%d\n", channel); + } + break; + } + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_label: + *str = data->mpq8785_label[2]; + break; + } + break; + case hwmon_temp: + switch (attr) + { + + case hwmon_temp_label: + *str = data->mpq8785_label[3]; + break; + } + break; + + default: + break; + } + return 0; +} + +static int mpq8785_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u16 new_value = 0; + + int ret = 0; + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_enable: + mpq8785_update_byte(data, MPQ8785_CMD_OPERATION, + MPQ8785_MASK_OPERATION_ENABLE, (u8)(val << 7)); + break; + case hwmon_in_min: + new_value = mpq8785_volt2reg(val, data->volt_numerator); + ret = mpq8785_update_word(data, MPQ8785_CMD_VOUT_MIN, + MPQ8785_MASK_VOUT_LIMIT, new_value); + break; + case hwmon_in_max: + if (channel == 0) + { + new_value = (MPQ8785_VOLT_IN_LSB_HALF + val) / MPQ8785_VOLT_IN_LSB; + ret = mpq8785_update_word(data, MPQ8785_CMD_VIN_OV_FAULT_LIMIT, + MPQ8785_MASK_VIN_OV_FAULT, new_value); + } + else if (channel == 1) + { + new_value = mpq8785_volt2reg(val, data->volt_numerator); + ret = mpq8785_update_word(data, MPQ8785_CMD_VOUT_MAX, + MPQ8785_MASK_VOUT_LIMIT, new_value); + } + else + { + dev_err(dev, "not support channel%d\n", channel); + } + break; + } + + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_crit: + ret = mpq8785_update_word(data, MPQ8785_CMD_IOUT_OC_FAULT_LIMIT, + MPQ8785_MASK_IOUT_LIMIT, (u16)val); + break; + case hwmon_curr_max: + ret = mpq8785_update_word(data, MPQ8785_CMD_IOUT_OC_WARN_LIMIT, + MPQ8785_MASK_IOUT_LIMIT, (u16)val); + break; + } + break; + case hwmon_temp: + switch (attr) + { + case hwmon_temp_max: + ret = mpq8785_update_word(data, MPQ8785_CMD_OT_WARN_LIMIT, + MPQ8785_MASK_TOUT_LIMIT, (u16)val); + break; + case hwmon_temp_crit: + ret = mpq8785_update_word(data, MPQ8785_CMD_OT_FAULT_LIMIT, + MPQ8785_MASK_TOUT_LIMIT, (u16)val); + break; + } + break; + + default: + break; + } + return ret; +} +static const struct hwmon_ops pac193x_hwmon_ops = { + .is_visible = mpq8785_is_visible, + .read = mpq8785_read, + .write = mpq8785_write, + .read_string = mpq8785_read_string, +}; + +static struct hwmon_chip_info mpq8785_chip_info = { + .ops = &pac193x_hwmon_ops, + .info = mpq8785_info, + +}; + +static ssize_t mpq8785_status_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(d); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 value = mpq8785_read_word(data, MPQ8785_CMD_STATUS_WORD); + + return sysfs_emit( + buf, + "An output voltage fault or warning has occurred:%s\n" + "An output current or output power fault or warning has occurred:%s\n" + "An input voltage, input current, or input power fault or warning has " + "occurred:%s\n" + "Power Good:%s\n" + "Watch Dog overflow fault:%s\n" + "PMBus BUSY:%s\n" + "power OFF:%s\n" + "An output over-voltage fault has occurred:%s\n" + "An output over-current fault has occurred:%s\n" + "An input under-voltage fault has occurred:%s\n" + "A temperature fault or warning has occurred:%s\n" + "A communications, memory or logic fault has occurred:%s\n" + "DrMOS fault:%s\n", + garr_bool_string[(value >> 15) & 0x1], + garr_bool_string[(value >> 14) & 0x1], + garr_bool_string[(value >> 13) & 0x1], + garr_bool_string[(value >> 11) & 0x1], + garr_bool_string[(value >> 8) & 0x1], + garr_bool_string[(value >> 7) & 0x1], + garr_bool_string[(value >> 6) & 0x1], + garr_bool_string[(value >> 5) & 0x1], + garr_bool_string[(value >> 4) & 0x1], + garr_bool_string[(value >> 3) & 0x1], + garr_bool_string[(value >> 2) & 0x1], + garr_bool_string[(value >> 1) & 0x1], + garr_bool_string[value & 0x1]); +} +DEVICE_ATTR(mpq8785_status, S_IRUGO, mpq8785_status_show, NULL); + +static ssize_t mpq8785_cap_version_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(d); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u8 cap_value = mpq8785_read_byte(data, MPQ8785_CMD_CAPABILITY); + u8 rev_id = mpq8785_read_byte(data, MPQ8785_CMD_REV_ID); + u8 pmbus_rev = mpq8785_read_byte(data, MPQ8785_CMD_PMBUS_REV_CONST); + u32 mfr_id = 0; + u8 mfr_rev = mpq8785_read_byte(data, MPQ8785_CMD_MFR_REVISION); + mpq8785_read_block(data, MPQ8785_CMD_MFR_ID, (u8 *)&mfr_id, 3, true); + return sysfs_emit(buf, + "PEC_SUPPORT:%s\n" + "MAX_BUS_SPEED:%s\n" + "SMBALERT_SUPPORT:%s\n" + "AVSBUS_SUPPORT:%s\n" + "Silicon revision number:%x\n" + "pmbus_revision:1.%c\n" + "mfr_id:%c%c%c\n" + "mfr_revision:%x\n", + garr_bool_string[(cap_value >> 7) & 0x1], + garr_speed_string[(cap_value >> 5) & 0x3], + garr_bool_string[(cap_value >> 4) & 0x1], + garr_bool_string[(cap_value >> 2) & 0x1], rev_id, + pmbus_rev & 0xff, (mfr_id >> 16) & 0xff, + (mfr_id >> 8) & 0xff, mfr_id & 0xff, mfr_rev & 0xff); +} +DEVICE_ATTR(mpq8785_cap_verison, S_IRUGO, mpq8785_cap_version_show, NULL); + +static u32 mpq8785_get_vout(struct MPQ8785_DRIVER_DATA *data) +{ + u32 get_value = mpq8785_read_mask_word(data, MPQ8785_CMD_VOUT_COMMAND, + MPQ8785_MASK_VOUT_VALUE); + + return mpq8785_reg2volt(get_value, data->volt_numerator); +} + +static s32 mpq8785_set_vout(struct MPQ8785_DRIVER_DATA *data, u32 volt_mv) +{ + u16 new_value = mpq8785_volt2reg(volt_mv, data->volt_numerator); + return mpq8785_update_word(data, MPQ8785_CMD_VOUT_COMMAND, + MPQ8785_MASK_VOUT_VALUE, (new_value)); +} + +static ssize_t mpq8785_vout_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(d); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%u", mpq8785_get_vout(data)); +} +static ssize_t mpq8785_vout_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(d); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 volt_value = 0; + int ret = 0; + ret = mpq8785_str2ul(buf, &volt_value); + + if (ret) + { + return ret; + } + ret = mpq8785_set_vout(data, volt_value); + if (0 != ret) + { + return ret; + } + return count; +} +DEVICE_ATTR(mpq8785_vout, 0600, mpq8785_vout_show, mpq8785_vout_store); + +static ssize_t mpq8785_sw_freq_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(d); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 get_value = mpq8785_read_mask_word(data, MPQ8785_CMD_MFR_FS_CFG, + MPQ8785_MASK_SW_FREQ_FREQ); + + return sysfs_emit( + buf, "%u", + get_value * MPQ8785_FREQUENCY_LSB + MPQ8785_FREQUENCY_BASE_MINI); +} +static ssize_t mpq8785_sw_freq_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(d); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 new_value = 0; + int ret = 0; + + ret = mpq8785_str2ul(buf, &new_value); + + if (ret) + { + return ret; + } + if ((new_value < MPQ8785_FREQUENCY_BASE_MINI) || + (new_value > MPQ8785_FREQUENCY_BASE_MAX)) + { + return -EINVAL; + } + ret = mpq8785_update_word( + data, MPQ8785_CMD_MFR_FS_CFG, MPQ8785_MASK_SW_FREQ_FREQ, + ((new_value - MPQ8785_FREQUENCY_BASE_MINI) / MPQ8785_FREQUENCY_LSB)); + return count; +} +DEVICE_ATTR(mpq8785_sw_freq, 0600, mpq8785_sw_freq_show, mpq8785_sw_freq_store); + +static struct attribute *mp8785_attrs[] = { + &dev_attr_mpq8785_status.attr, &dev_attr_mpq8785_cap_verison.attr, + &dev_attr_mpq8785_vout.attr, &dev_attr_mpq8785_sw_freq.attr, NULL}; + +ATTRIBUTE_GROUPS(mp8785); + +static struct linear_range mpq8785_ext_ranges[] = { + /* REGULATOR_LINEAR_RANGE(700000, 0, 100, 15625), //=1.953125mV*1000*8 */ + REGULATOR_LINEAR_RANGE(600000, 0, 320, 3125), /* =1.5625mV*1000*2 */ +}; + +/** + * mpq8785_set_voltage_sel - set_voltage_sel for users + * + * @rdev: regulator to operate on + * @sel: Selector to set + */ +static s32 mpq8785_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct device *dev = &rdev->dev; + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 new_value = 0; + + if (selector > mpq8785_ext_ranges->max_sel) + { + dev_err(dev, "selector:%u out of rang 0~%u\n", selector, + mpq8785_ext_ranges->max_sel); + return -EINVAL; + } + + new_value = mpq8785_ext_ranges->min + mpq8785_ext_ranges->step * selector; + + dev_dbg(dev, "%s_volt:%duV,selector:%u,step:%u,min:%u\n", __FUNCTION__, + new_value, selector, mpq8785_ext_ranges->step, + mpq8785_ext_ranges->min); + + mpq8785_set_vout(data, new_value / 1000); + + return 0; +} + +/** + * mpq8785_get_voltage_sel - get_voltage_sel for users + * + * @rdev: regulator to operate on + */ +static s32 mpq8785_get_voltage_sel(struct regulator_dev *rdev) +{ + s32 index = 0; + struct device *dev = &rdev->dev; + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 volt_value = 0; + u32 diff_volt = 0; + + volt_value = mpq8785_get_vout(data); + volt_value *= 1000; + + if (volt_value >= mpq8785_ext_ranges->min) + { + diff_volt = volt_value - mpq8785_ext_ranges->min; + } + else + { + diff_volt = 0; + } + dev_dbg(dev, "%s_diff_volt:%duV,volt:%u,min:%u\n", __FUNCTION__, diff_volt, + volt_value, mpq8785_ext_ranges->min); + index = DIV_ROUND_CLOSEST(diff_volt, mpq8785_ext_ranges->step); + if (index > mpq8785_ext_ranges->max_sel) + { + dev_err(dev, "volt:%duV out legal range\n", volt_value); + } + + dev_dbg(dev, "%s_diff_volt:%duV,step:%d,index:%d\n", __FUNCTION__, diff_volt, + mpq8785_ext_ranges->step, index); + return index; +} +/** + * mpq8785_set_current_limit- set_current_limit for users + * @rdev: regulator to operate on + * @min_uA: Lower bound for current limit + * @max_uA: Upper bound for current limit + */ +static s32 mpq8785_set_current_limit(struct regulator_dev *rdev, s32 min_uA, + s32 max_uA) +{ + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u16 new_value = 0; + + new_value = max_uA / MPQ8785_REGULATOT_CURRENT_LSB; + mpq8785_update_word(data, MPQ8785_CMD_IOUT_OC_FAULT_LIMIT, + MPQ8785_MASK_IOUT_LIMIT, new_value); + dev_dbg(&rdev->dev, "mpq8785_set_current_limit,min_uA:%d,max_uA:%d,now_A:%d\n", min_uA, + max_uA, new_value); + return 0; +} + +static s32 mpq8785_get_current_limit(struct regulator_dev *rdev) +{ + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + u32 get_value = mpq8785_read_mask_word(data, MPQ8785_CMD_IOUT_OC_FAULT_LIMIT, + MPQ8785_MASK_IOUT_LIMIT); + get_value = get_value * MPQ8785_REGULATOT_CURRENT_LSB; + dev_dbg(&rdev->dev, "mpq8785_get_current_limit_%duA\n", get_value); + return get_value; +} + +static s32 mpq8785_get_error_flags(struct regulator_dev *rdev, u32 *flags) +{ + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + + *flags = mpq8785_read_word(data, MPQ8785_CMD_STATUS_WORD); + + dev_dbg(&rdev->dev, "mpq8785_get_error_flags_%u\n", *flags); + return 0; +} + +int mpq8785_regulator_enable(struct regulator_dev *rdev) +{ + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + dev_dbg(&rdev->dev, "%s.%d\n", __FUNCTION__, __LINE__); + return mpq8785_update_byte(data, MPQ8785_CMD_OPERATION, + MPQ8785_MASK_OPERATION_ENABLE, + MPQ8785_MASK_OPERATION_ENABLE); +} + +int mpq8785_regulator_disable(struct regulator_dev *rdev) +{ + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + dev_dbg(&rdev->dev, "%s.%d\n", __FUNCTION__, __LINE__); + return mpq8785_update_byte(data, MPQ8785_CMD_OPERATION, + MPQ8785_MASK_OPERATION_ENABLE, 0); +} +int mpq8785_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct i2c_client *client = to_i2c_client(rdev->dev.parent); + struct MPQ8785_DRIVER_DATA *data = i2c_get_clientdata(client); + dev_dbg(&rdev->dev, "%s.%d\n", __FUNCTION__, __LINE__); + return mpq8785_get_enable(data); +} + +static struct regulator_ops mpq8785_core_ops = { + + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + + /* get/set regulator voltage */ + /* Only one of each(set_voltage&&set_voltage_sel) should be implemented */ + /* .set_voltage = mpq8785_set_voltage, */ + .set_voltage_sel = mpq8785_set_voltage_sel, + + /* Only one of each(get_voltage&&get_voltage_sel) should be implemented */ + /* .get_voltage=mpq8785_get_voltage, */ + .get_voltage_sel = mpq8785_get_voltage_sel, + + /* get/set regulator current */ + .set_current_limit = mpq8785_set_current_limit, + .get_current_limit = mpq8785_get_current_limit, + + /* enable/disable regulator */ + .enable = mpq8785_regulator_enable, + .disable = mpq8785_regulator_disable, + .is_enabled = mpq8785_regulator_is_enabled, + + .get_error_flags = mpq8785_get_error_flags, + +}; +static struct regulator_desc mpq8785_regulator_desc = { + .name = "NPUVDD", + .type = REGULATOR_VOLTAGE, + .n_voltages = 321, + .ops = &mpq8785_core_ops, + .linear_ranges = mpq8785_ext_ranges, + .n_linear_ranges = ARRAY_SIZE(mpq8785_ext_ranges), + .owner = THIS_MODULE, +}; + +static s32 mpq8785_init_data(struct MPQ8785_DRIVER_DATA *data, + const struct regulation_constraints *constraints, u32 default_voltage) +{ + u8 value = 0; + s32 ret = 0; + u16 new_value = 0; + struct device *dev = &data->client->dev; + + /*set voltage format to VID to get better step for regulator*/ + mpq8785_write_byte(data, MPQ8785_CMD_VOUT_MODE, 0x20); + value = mpq8785_read_byte(data, MPQ8785_CMD_VOUT_MODE); /*format*/ + data->volt_format = (value >> 5) & 0x3; + data->volt_numerator = garr_volt_numerator[data->volt_format]; + + dev_info(dev, + "min_uV:%d,max_uV:%d,uV_offset:%d,min_uA:%d,max_uA:%d," + "over_voltage_limits:%d,%d,%d\n", + constraints->min_uV, constraints->max_uV, constraints->uV_offset, + constraints->min_uA, constraints->max_uA, + constraints->over_voltage_limits.err, + constraints->over_voltage_limits.prot, + constraints->over_voltage_limits.warn); + mpq8785_ext_ranges->min = constraints->min_uV; + mpq8785_ext_ranges->min_sel = 0; + mpq8785_ext_ranges->max_sel = (constraints->max_uV - constraints->min_uV) / mpq8785_ext_ranges->step + 1; + mpq8785_regulator_desc.n_voltages = mpq8785_ext_ranges->max_sel; + new_value = mpq8785_volt2reg(constraints->over_voltage_limits.prot / 1000, + data->volt_numerator); + ret = mpq8785_update_word(data, MPQ8785_CMD_VOUT_MAX, MPQ8785_MASK_VOUT_LIMIT, + new_value); + if (ret < 0) + { + dev_err(dev, "set vout limit error\n"); + return ret; + } + + mpq8785_set_vout(data, default_voltage / 1000); + return ret; +} + +static s32 mpq8785_probe(struct i2c_client *client) +{ + struct MPQ8785_DRIVER_DATA *data = NULL; + s32 ret = 0; + s32 regulator_cnt = 0; + u32 default_voltage = 0; + struct device *hwmon_dev; + struct regulator_config config = {}; + struct device *dev = &client->dev; + struct device_node *np, *parent; + const char *output_names[4]; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + { + dev_err(dev, "not support smbus\n"); + return -EIO; + } + data = devm_kzalloc(dev, sizeof(struct MPQ8785_DRIVER_DATA), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->config_lock); + data->client = client; + i2c_set_clientdata(client, data); + /* Get the device (PMIC) node */ + np = of_node_get(dev->of_node); + if (!np) + return -EINVAL; + + /* Get 'regulators' subnode */ + parent = of_get_child_by_name(np, "regulators"); + if (!parent) + { + dev_err(dev, "regulators node not found\n"); + return -EINVAL; + } + ret = of_property_read_u32(np, "eswin,regulator_default-microvolt", &default_voltage); + if (ret) + { + default_voltage = 900000; + } + of_property_read_string_array(np, "eswin,regulator_label", output_names, 4); + if (NULL != output_names[0]) + { + strcpy(data->mpq8785_label[0], output_names[0]); + } + if (NULL != output_names[1]) + { + strcpy(data->mpq8785_label[1], output_names[1]); + } + if (NULL != output_names[2]) + { + strcpy(data->mpq8785_label[2], output_names[2]); + } + if (NULL != output_names[3]) + { + strcpy(data->mpq8785_label[3], output_names[3]); + } + + dev_dbg(dev, "default_voltage:%u,%s,%s,%s,%s\n", default_voltage, data->mpq8785_label[0], + data->mpq8785_label[1], data->mpq8785_label[2], data->mpq8785_label[3]); + /* fill isl6271a_matches array */ + regulator_cnt = of_regulator_match(dev, parent, mpq8785_matches, ARRAY_SIZE(mpq8785_matches)); + of_node_put(parent); + if (regulator_cnt != 1) + { + dev_err(dev, "Error parsing regulator init data: %d\n", regulator_cnt); + return regulator_cnt; + } + + /* Fetched from device tree */ + config.init_data = mpq8785_matches[0].init_data; + config.dev = dev; + config.of_node = mpq8785_matches[0].of_node; + /* config.ena_gpio = -EINVAL; */ + ret = mpq8785_init_data(data, &config.init_data->constraints, default_voltage); + if (0 != ret) + { + dev_err(dev, "init mpq8785 error\n"); + return -EIO; + } + data->rdev = devm_regulator_register(dev, &mpq8785_regulator_desc, &config); + if (IS_ERR(data->rdev)) + { + dev_err(dev, "failed to register %s\n", mpq8785_regulator_desc.name); + } + hwmon_dev = devm_hwmon_device_register_with_info( + dev, client->name, data, &mpq8785_chip_info, mp8785_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_dbg(dev, "mpq8785_probe\n"); + + return 0; +} + +static void mpq8785_remove(struct i2c_client *client) +{ + dev_dbg(&client->dev, "mpq8785_remove\n"); +} + +static s32 mpq8785_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + dev_dbg(&client->dev, "mpq8785_detect\n"); + return 0; +} + +static const struct i2c_device_id mpq8785_id[] = {{"mpq8785", 0}, {}}; +MODULE_DEVICE_TABLE(i2c, mpq8785_id); + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, 0x60, + I2C_CLIENT_END}; + +static struct i2c_driver mpq8785_driver = { + .class = I2C_CLASS_HWMON, + .driver = + { + .name = "mpq8785", + }, + .probe = mpq8785_probe, + .remove = mpq8785_remove, + .id_table = mpq8785_id, + .detect = mpq8785_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mpq8785_driver); + +MODULE_AUTHOR("Yang Wei "); +MODULE_DESCRIPTION("mpq8785 driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file -- 2.47.0