kernel/0019-feat-support-mpq8785-to-set-npu-voltage.patch

1339 lines
39 KiB
Diff
Raw Normal View History

2024-12-15 18:29:23 +00:00
From eb8ab7c349632add37514b09e4761bdce379cead Mon Sep 17 00:00:00 2001
From: yangwei1 <yangwei1@eswincomputing.com>
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 <https://www.gnu.org/licenses/>.
+ *
+ * Authors: Yang Wei <yangwei1@eswincomputing.com>
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#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 <yangwei1@eswincomputing.com>");
+MODULE_DESCRIPTION("mpq8785 driver");
+MODULE_LICENSE("GPL");
\ No newline at end of file
--
2.47.0