From c197503e13e1bc622aa0ccd8908ecfa9f3754253 Mon Sep 17 00:00:00 2001 From: yangwei1 Date: Thu, 23 May 2024 16:16:05 +0800 Subject: [PATCH 025/219] feat:add support pac1934 to get som voltage info Changelogs: --- .../boot/dts/eswin/hifive-premier-550.dts | 5 +- arch/riscv/configs/win2030_defconfig | 1 + drivers/hwmon/Kconfig | 6 + drivers/hwmon/Makefile | 1 + drivers/hwmon/pac193x.c | 938 ++++++++++++++++++ 5 files changed, 949 insertions(+), 2 deletions(-) create mode 100644 drivers/hwmon/pac193x.c diff --git a/arch/riscv/boot/dts/eswin/hifive-premier-550.dts b/arch/riscv/boot/dts/eswin/hifive-premier-550.dts index 550a8cc2fbaf..f0883f403393 100644 --- a/arch/riscv/boot/dts/eswin/hifive-premier-550.dts +++ b/arch/riscv/boot/dts/eswin/hifive-premier-550.dts @@ -663,13 +663,14 @@ &d0_aon_i2c1 { status = "okay"; eswin,syscfg = <&d0_sys_con 0x3C0 15>; iic_hold_time = <0x40>; - sic451@10 { + pac1934@10 { compatible = "microchip,pac1934"; /*update all register data*/ update_time_ms = <1000>; + eswin,chan_label = "som vdd", "soc vdd", "cpu vdd", "ddr lpvdd"; /*The update number of times the energy accumulates*/ energy_acc_count = <0>; - sense_resistances=<4 4 4 4>; + shunt_resistors=<4 4 4 4>; reg = <0x10>; }; sic451@11 { diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig index f57a7581943b..82c00984fe52 100644 --- a/arch/riscv/configs/win2030_defconfig +++ b/arch/riscv/configs/win2030_defconfig @@ -149,6 +149,7 @@ CONFIG_GPIOLIB=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_DWAPB=y CONFIG_SENSORS_INA2XX=y +CONFIG_SENSORS_PAC1934=y CONFIG_WATCHDOG=y CONFIG_DW_WATCHDOG=y CONFIG_REGULATOR=y diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index a4c361b6619c..14e8320b5c07 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2371,6 +2371,12 @@ config SENSORS_INTEL_M10_BMC_HWMON sensors monitor various telemetry data of different components on the card, e.g. board temperature, FPGA core temperature/voltage/current. +config SENSORS_PAC1934 + tristate "microchip PAC193X Hardware Monitoring" + depends on I2C + help + If you say yes here you get support for microchip PAC193X + Single/Multi-Channel DC Power/Energy Monitor with Accumulator. if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4ac9452b5430..6b5643ef316a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -220,6 +220,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o +obj-$(CONFIG_SENSORS_PAC1934) += pac193x.o obj-$(CONFIG_SENSORS_OCC) += occ/ obj-$(CONFIG_SENSORS_PECI) += peci/ diff --git a/drivers/hwmon/pac193x.c b/drivers/hwmon/pac193x.c new file mode 100644 index 000000000000..ebad3bde611e --- /dev/null +++ b/drivers/hwmon/pac193x.c @@ -0,0 +1,938 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for microchip pac1931,pac1932,pac1933,pac1934 power monitor chips + * + * 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 + +#define PAC193X_MAX_CHAN_CNT 4 + +#define PAC193X_REGISTERS 0x27 +#define PAC193X_VPOWERN_ACC_LEN 6 +#define PAC193X_VOLT_VALUE_LEN 2 +#define PAC193X_VPOWERN_VALUE_LEN 4 +#define PAC193X_ACC_COUNT_LEN 3 + +#define PAC193X_CMD_REFRESH 0x0 +#define PAC193X_CMD_CTRL 0x1 +#define PAC193X_CMD_ACC_COUNT 0x2 +#define PAC193X_CMD_VPOWER1_ACC 0x3 +#define PAC193X_CMD_VPOWER2_ACC 0x4 +#define PAC193X_CMD_VPOWER3_ACC 0x5 +#define PAC193X_CMD_VPOWER4_ACC 0x6 +#define PAC193X_CMD_VBUS1 0x7 +#define PAC193X_CMD_VBUS2 0x8 +#define PAC193X_CMD_VBUS3 0x9 +#define PAC193X_CMD_VBUS4 0xa +#define PAC193X_CMD_VSENSE1 0xb +#define PAC193X_CMD_VSENSE2 0xc +#define PAC193X_CMD_VSENSE3 0xd +#define PAC193X_CMD_VSENSE4 0xe +#define PAC193X_CMD_VBUS1_AVG 0xf +#define PAC193X_CMD_VBUS2_AVG 0x10 +#define PAC193X_CMD_VBUS3_AVG 0x11 +#define PAC193X_CMD_VBUS4_AVG 0x12 +#define PAC193X_CMD_VSENSE1_AVG 0x13 +#define PAC193X_CMD_VSENSE2_AVG 0x14 +#define PAC193X_CMD_VSENSE3_AVG 0x15 +#define PAC193X_CMD_VSENSE4_AVG 0x16 +#define PAC193X_CMD_VPOWER1 0x17 +#define PAC193X_CMD_VPOWER2 0x18 +#define PAC193X_CMD_VPOWER3 0x19 +#define PAC193X_CMD_VPOWER4 0x1a +#define PAC193X_CMD_CHANNEL_SMBUS 0x1c +#define PAC193X_CMD_NEG_PWR 0x1D +#define PAC193X_CMD_REFRESH_G 0x1E +#define PAC193X_CMD_REFRESH_V 0x1F +#define PAC193X_CMD_SLOW 0x1F +#define PAC193X_CMD_CTRL_ACT 0x21 +#define PAC193X_CMD_DIS_ACT 0x22 +#define PAC193X_CMD_NEG_PWR_ACT 0x23 +#define PAC193X_CMD_CTRL_LAT 0x24 +#define PAC193X_CMD_DIS_LAT 0x25 +#define PAC193X_CMD_NEG_PWR_LAT 0x26 +#define PAC193X_CMD_PID 0xFD +#define PAC193X_CMD_MID 0xFE +#define PAC193X_CMD_REVERSION_ID 0xFF + +#define PAC193X_COSTANT_PWR_M 3200000000ull /* 3.2V^2*1000mO*/ +#define PAC193X_COSTANT_CURRENT_M 100000 /* 100mv*1000mO*/ + +struct pac193x_data +{ + struct mutex config_lock; + struct i2c_client *client; + u32 update_time_ms; + u32 energy_acc_count; + struct workqueue_struct *update_workqueue; + struct delayed_work update_work; + u32 vbus_denominator[PAC193X_MAX_CHAN_CNT]; + u32 vsense_denominator[PAC193X_MAX_CHAN_CNT]; + u32 vpower_denominator[PAC193X_MAX_CHAN_CNT]; + u32 shunt_resistors[PAC193X_MAX_CHAN_CNT]; + char pac193x_label[PAC193X_MAX_CHAN_CNT][16]; + u32 sample_rate; +}; + +enum PAC193X_CHAN_INDEX +{ + pac1931_index = 0, + pac1932_index, + pac1933_index, + pac1934_index +}; + +static int pac193x_get_energy(struct device *dev, u8 commmd, u8 chan, long *val) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + int ret; + u64 cache = 0; + u8 *pcache = (u8 *)&cache; + u64 energy_value = 0; + + commmd = commmd + chan; + dev_dbg(dev, "%s.%d,chan:%d,commad:%d,LEN:%ld\n", __FUNCTION__, + __LINE__, chan, commmd, sizeof(cache)); + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_i2c_block_data(data->client, commmd, + PAC193X_VPOWERN_ACC_LEN, pcache); + mutex_unlock(&data->config_lock); + + energy_value = ((cache & 0xff) << 40) | (((cache >> 8) & 0xff) << 32) | + (((cache >> 16) & 0xff) << 24) | + (((cache >> 24) & 0xff) << 16) | + (((cache >> 32) & 0xff) << 8) | (((cache >> 40) & 0xff)); + energy_value = energy_value & 0xffffffffffffULL; + dev_dbg(dev, + "%s.%d,commd:0x%x,value:%lld,ret:%d,resistances:%u,denominator:%u,sample_rate:%u\n", + __FUNCTION__, __LINE__, commmd, energy_value, ret, + data->shunt_resistors[chan], data->vpower_denominator[chan], + data->sample_rate); + + /* energy=3200000*Vpower/(Rsense*denominator*sample_rate) */ + energy_value = ((energy_value / (u64)data->vpower_denominator[chan]) * + (PAC193X_COSTANT_PWR_M / + (data->shunt_resistors[chan] * data->sample_rate))); + + *val = energy_value; + + return 0; +} + +static u16 pac193x_get_word(struct pac193x_data *data, u8 commmd) +{ + int ret; + u16 cache = 0; + + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_word_data(data->client, commmd); + mutex_unlock(&data->config_lock); + cache = ((ret & 0xff) << 8) | ((ret >> 8) & 0xff); + dev_dbg(&data->client->dev, "%s.%d,commd:0x%x,value:0x%x,ret:0x%x\n", + __FUNCTION__, __LINE__, commmd, cache, ret); + + return cache; +} + +static int pac193x_get_volt(struct device *dev, u8 commmd, u8 chan, long *val) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + u32 cache = 0; + + commmd = commmd + chan; + dev_dbg(dev, "%s.%d,commd:0x%x,chan:%d,vbus_denominator:%d\n", + __FUNCTION__, __LINE__, commmd, chan, + data->vbus_denominator[chan]); + cache = pac193x_get_word(data, commmd); + /*to mV*/ + cache = cache * 1000 / data->vbus_denominator[chan]; + *val = cache; + return 0; +} + +static int pac193x_get_current(struct device *dev, u8 commmd, u8 chan, + long *val) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + u32 cache = 0; + + commmd = commmd + chan; + dev_dbg(dev, + "%s.%d,commd:0x%x,chan:%d,vbus_denominator:%d,resistances:%d\n", + __FUNCTION__, __LINE__, commmd, chan, + data->vsense_denominator[chan], data->shunt_resistors[chan]); + cache = pac193x_get_word(data, commmd); + /* I=Vsense*100/(Rsense*denominator) */ + cache = cache * PAC193X_COSTANT_CURRENT_M / + (data->vsense_denominator[chan] * + data->shunt_resistors[chan]); + *val = cache; + return 0; +} + +static int pac193x_get_power(struct device *dev, u8 commmd, u8 chan, long *val) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + int ret; + u32 cache = 0; + u8 *pcache = (u8 *)&cache; + u64 pwr_value = 0; + + commmd = commmd + chan; + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_i2c_block_data(data->client, commmd, + PAC193X_VPOWERN_VALUE_LEN, pcache); + mutex_unlock(&data->config_lock); + pwr_value = ((cache & 0xff) << 24) | (((cache >> 8) & 0xff) << 16) | + (((cache >> 16) & 0xff) << 8) | ((cache >> 24) & 0xff); + pwr_value = pwr_value >> 4; + dev_dbg(dev, + "%s.%d,commd:0x%x,chan:%d,value:0x%x,pwr_value:0x%llx,%llu,ret:%d,resistances:%u,denominator:%u\n", + __FUNCTION__, __LINE__, commmd, chan, cache, pwr_value, + pwr_value, ret, data->shunt_resistors[chan], + data->vpower_denominator[chan]); + /* pwr=3200000*Vpower/Rsense*denominator */ + pwr_value = pwr_value * PAC193X_COSTANT_PWR_M / + ((u64)data->shunt_resistors[chan] * + (u64)data->vpower_denominator[chan]); + *val = pwr_value; + return 0; +} + +static int pac193x_send_refresh_cmd(struct pac193x_data *data, u8 command) +{ + int ret = 0; + + mutex_lock(&data->config_lock); + ret = i2c_smbus_write_byte(data->client, command); + mutex_unlock(&data->config_lock); + dev_dbg(&data->client->dev, "%s.%d,commd:0x%x,ret:%d\n", __FUNCTION__, + __LINE__, command, ret); + return ret; +} + +static ssize_t pac193x_refresh_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(da); + struct pac193x_data *data = dev_get_drvdata(dev); + + pac193x_send_refresh_cmd(data, (u8)sensor_attr->index); + return count; +} + +static struct sensor_device_attribute pac1934_refreshs[] = { + SENSOR_ATTR_WO(refresh_clear_acc, pac193x_refresh, PAC193X_CMD_REFRESH), + SENSOR_ATTR_WO(refresh_all_193x, pac193x_refresh, + PAC193X_CMD_REFRESH_G), + SENSOR_ATTR_WO(refresh_updata_value, pac193x_refresh, + PAC193X_CMD_REFRESH_V), +}; + +static u8 pac193x_read_byte_data(struct pac193x_data *data, u8 command) +{ + int cache = 0; + int cnt = 0; + while (1) + { + mutex_lock(&data->config_lock); + cache = i2c_smbus_read_byte_data(data->client, command); + mutex_unlock(&data->config_lock); + if (0xff != cache) + { + break; + } + cnt++; + if (cnt > 100) + { + dev_err(&data->client->dev, + "get command:%d value error\n", command); + return 0xff; + } + } + return (u8)cache; +} + +static ssize_t pac193x_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + u8 set_val = 0, act_val = 0, lat_val = 0; + + set_val = pac193x_read_byte_data(data, PAC193X_CMD_CTRL); + act_val = pac193x_read_byte_data(data, PAC193X_CMD_CTRL_ACT); + lat_val = pac193x_read_byte_data(data, PAC193X_CMD_CTRL_LAT); + + return sprintf(buf, + "%16s:%6s,%6s,%6s\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n", + "", "set", "act", "latch", + "sample_rate", (set_val >> 6) & 0x3, (act_val >> 6) & 0x3, (lat_val >> 6) & 0x3, + "SLEEP", (set_val >> 5) & 0x1, (act_val >> 5) & 0x1, (lat_val >> 5) & 0x1, + "SING", (set_val >> 4) & 0x1, (act_val >> 4) & 0x1, (lat_val >> 4) & 0x1, + "ALERT_PIN", (set_val >> 3) & 0x1, (act_val >> 3) & 0x1, (lat_val >> 3) & 0x1, + "ALERT_CC", (set_val >> 2) & 0x1, (act_val >> 2) & 0x1, (lat_val >> 2) & 0x1, + "OVF ALERT", (set_val >> 1) & 0x1, (act_val >> 1) & 0x1, (lat_val >> 1) & 0x1, + "OVF", (set_val >> 0) & 0x1, (act_val >> 0) & 0x1, (lat_val >> 0) & 0x1); +} + +int pac193x_common_reg_set(struct device *dev, const char *buf, u8 commad) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + unsigned long val; + int status; + int ret = 0; + + status = kstrtoul(buf, 10, &val); + if (status < 0) + return status; + mutex_lock(&data->config_lock); + ret = i2c_smbus_write_byte_data(data->client, commad, (u8)val); + mutex_unlock(&data->config_lock); + return ret; +} + +static ssize_t pac193x_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return pac193x_common_reg_set(dev, buf, PAC193X_CMD_CTRL); +} + +DEVICE_ATTR(control, S_IWUSR | S_IRUGO, pac193x_ctrl_show, pac193x_ctrl_store); + +static ssize_t pac193x_acc_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + int ret; + u32 cache = 0; + u8 *pcache = (u8 *)&cache; + u32 acc_cnt = 0; + mutex_lock(&data->config_lock); + ret = i2c_smbus_read_i2c_block_data(data->client, PAC193X_CMD_ACC_COUNT, + PAC193X_ACC_COUNT_LEN, pcache); + mutex_unlock(&data->config_lock); + + acc_cnt = ((cache & 0xff) << 16) | (((cache >> 8) & 0xff) << 8) | + ((cache >> 16) & 0xff); + acc_cnt = acc_cnt & 0xffffff; + + ret = sysfs_emit(buf, "%u\n", acc_cnt); + return ret; +} + +DEVICE_ATTR(acc_count, 0400, pac193x_acc_count_show, NULL); + +static ssize_t pac193x_dis_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + u8 set_val = 0, act_val = 0, lat_val = 0; + + set_val = pac193x_read_byte_data(data, PAC193X_CMD_CHANNEL_SMBUS); + act_val = pac193x_read_byte_data(data, PAC193X_CMD_DIS_ACT); + lat_val = pac193x_read_byte_data(data, PAC193X_CMD_DIS_LAT); + + return sprintf(buf, + "%16s:%6s,%6s,%6s\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n", + "", "set", "act", "latch", + "CH1_OFF", (set_val >> 7) & 0x1, (act_val >> 7) & 0x1, (lat_val >> 7) & 0x1, + "CH2_OFF", (set_val >> 6) & 0x1, (act_val >> 6) & 0x1, (lat_val >> 6) & 0x1, + "CH3_OFF", (set_val >> 5) & 0x1, (act_val >> 5) & 0x1, (lat_val >> 5) & 0x1, + "CH4_OFF", (set_val >> 4) & 0x1, (act_val >> 4) & 0x1, (lat_val >> 4) & 0x1, + "TIMEOUT", (set_val >> 3) & 0x1, (act_val >> 3) & 0x1, (lat_val >> 3) & 0x1, + "BYTE COUNT", (set_val >> 2) & 0x1, (act_val >> 2) & 0x1, (lat_val >> 2) & 0x1, + "NO SKIP", (set_val >> 1) & 0x1, (act_val >> 1) & 0x1, (lat_val >> 1) & 0x1); +} + +static ssize_t pac193x_dis_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + return pac193x_common_reg_set(dev, buf, PAC193X_CMD_CHANNEL_SMBUS); +} + +DEVICE_ATTR(disable_chan_pmbus, S_IWUSR | S_IRUGO, pac193x_dis_show, + pac193x_dis_store); + +static ssize_t pac193x_neg_pwr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + u8 set_val = 0, act_val = 0, lat_val = 0; + + set_val = pac193x_read_byte_data(data, PAC193X_CMD_NEG_PWR); + act_val = pac193x_read_byte_data(data, PAC193X_CMD_NEG_PWR_ACT); + lat_val = pac193x_read_byte_data(data, PAC193X_CMD_NEG_PWR_LAT); + + return sprintf(buf, + "%16s:%6s,%6s,%6s\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n" + "%16s:%6d,%6d,%6d\n", + "", "set", "act", "latch", + "CH1_BIDI", (set_val >> 7) & 0x1, (act_val >> 7) & 0x1, (lat_val >> 7) & 0x1, + "CH2_BIDI", (set_val >> 6) & 0x1, (act_val >> 6) & 0x1, (lat_val >> 6) & 0x1, + "CH3_BIDI", (set_val >> 5) & 0x1, (act_val >> 5) & 0x1, (lat_val >> 5) & 0x1, + "CH4_BIDI", (set_val >> 4) & 0x1, (act_val >> 4) & 0x1, (lat_val >> 5) & 0x1, + "CH1_BIDV", (set_val >> 3) & 0x1, (act_val >> 3) & 0x1, (lat_val >> 3) & 0x1, + "CH2_BIDV", (set_val >> 2) & 0x1, (act_val >> 2) & 0x1, (lat_val >> 2) & 0x1, + "CH3_BIDV", (set_val >> 1) & 0x1, (act_val >> 1) & 0x1, (lat_val >> 1) & 0x1, + "CH4_BIDV", (set_val >> 0) & 0x1, (act_val >> 0) & 0x1, (lat_val >> 0) & 0x1); +} + +static ssize_t pac193x_neg_pwr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return pac193x_common_reg_set(dev, buf, PAC193X_CMD_NEG_PWR); +} + +DEVICE_ATTR(neg_pwr, S_IWUSR | S_IRUGO, pac193x_neg_pwr_show, + pac193x_neg_pwr_store); + +static ssize_t pac193x_slow_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + u8 set_val = 0; + + set_val = pac193x_read_byte_data(data, PAC193X_CMD_SLOW); + + return sprintf(buf, + "SLOW:%d,SLOW-LH:%d,SLOW-HL:%d, R_RISE:%d," + "R_V_RISE:%d,R_FALL:%d,R_V_FALL:%d,POR:%d\n", + (set_val >> 7) & 0x1, (set_val >> 6) & 0x1, + (set_val >> 5) & 0x1, (set_val >> 4) & 0x1, + (set_val >> 3) & 0x1, (set_val >> 2) & 0x1, + (set_val >> 1) & 0x1, (set_val >> 0) & 0x1); +} + +static ssize_t pac193x_slow_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return pac193x_common_reg_set(dev, buf, PAC193X_CMD_SLOW); +} + +DEVICE_ATTR(slow_ctrl, S_IWUSR | S_IRUGO, pac193x_slow_show, + pac193x_slow_store); + +static ssize_t pac193x_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + int ret; + u8 pid = 0, mid = 0, rid = 0; + + pid = pac193x_read_byte_data(data, PAC193X_CMD_PID); + mid = pac193x_read_byte_data(data, PAC193X_CMD_MID); + rid = pac193x_read_byte_data(data, PAC193X_CMD_REVERSION_ID); + + ret = sysfs_emit(buf, "PID:0x%x,MID:0x%x,RID:0x%x\n", pid, mid, rid); + return ret; +} +DEVICE_ATTR(pac193x_version, 0400, pac193x_version_show, NULL); + +static struct attribute *pac193x_attrs[] = { + &dev_attr_control.attr, + &dev_attr_acc_count.attr, + &dev_attr_neg_pwr.attr, + &dev_attr_disable_chan_pmbus.attr, + &dev_attr_slow_ctrl.attr, + &dev_attr_pac193x_version.attr, + &pac1934_refreshs[0].dev_attr.attr, + &pac1934_refreshs[1].dev_attr.attr, + &pac1934_refreshs[2].dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(pac193x); + +static const struct hwmon_channel_info *pac1931_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_AVERAGE), + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT), + HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT), + NULL}; +static const struct hwmon_channel_info *pac1932_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_AVERAGE, + HWMON_C_INPUT | HWMON_C_AVERAGE), + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT, HWMON_P_INPUT), + HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT, HWMON_E_INPUT), + NULL}; +static const struct hwmon_channel_info *pac1933_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_AVERAGE, + HWMON_C_INPUT | HWMON_C_AVERAGE, + HWMON_C_INPUT | HWMON_C_AVERAGE), + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT, HWMON_P_INPUT, HWMON_P_INPUT), + HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT, HWMON_E_INPUT, HWMON_E_INPUT), + NULL}; +static const struct hwmon_channel_info *pac1934_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_AVERAGE, + HWMON_I_INPUT | HWMON_I_AVERAGE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_AVERAGE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_AVERAGE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_AVERAGE | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL, + HWMON_P_INPUT | HWMON_P_LABEL, + HWMON_P_INPUT | HWMON_P_LABEL, + HWMON_P_INPUT | HWMON_P_LABEL), + HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, + HWMON_E_INPUT | HWMON_E_LABEL, + HWMON_E_INPUT | HWMON_E_LABEL, + HWMON_E_INPUT | HWMON_E_LABEL), + NULL}; + +static umode_t pac1934x_is_visible(const void *_data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_label: + if (channel == 0) + { + return 0; + } + else + { + return 0444; + } + case hwmon_in_input: + case hwmon_in_average: + if (channel == 0) + { + return 0; + } + else + { + return S_IRUGO; + } + } + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_label: + return 0444; + case hwmon_curr_input: + case hwmon_curr_average: + return S_IRUGO; + } + break; + case hwmon_power: + switch (attr) + { + case hwmon_power_label: + return 0444; + case hwmon_power_input: + return S_IRUGO; + } + break; + case hwmon_energy: + switch (attr) + { + case hwmon_energy_label: + return 0444; + case hwmon_energy_input: + return S_IRUGO; + } + break; + default: + break; + } + return 0; +} + +static int pac1934x_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct pac193x_data *data = dev_get_drvdata(dev); + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_label: + if (channel != 0) + { + *str = data->pac193x_label[channel - 1]; + } + break; + } + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_label: + *str = data->pac193x_label[channel]; + break; + } + break; + case hwmon_power: + switch (attr) + { + case hwmon_power_label: + *str = data->pac193x_label[channel]; + break; + } + break; + case hwmon_energy: + switch (attr) + { + case hwmon_energy_label: + *str = data->pac193x_label[channel]; + break; + } + break; + default: + break; + } + return 0; +} + +static int pac193x_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) + { + case hwmon_in: + switch (attr) + { + case hwmon_in_input: + return pac193x_get_volt(dev, PAC193X_CMD_VBUS1, channel - 1, + val); + case hwmon_in_average: + return pac193x_get_volt(dev, PAC193X_CMD_VBUS1_AVG, + channel - 1, val); + } + break; + case hwmon_curr: + switch (attr) + { + case hwmon_curr_input: + return pac193x_get_current(dev, PAC193X_CMD_VSENSE1, + channel, val); + case hwmon_curr_average: + return pac193x_get_current(dev, PAC193X_CMD_VSENSE1_AVG, + channel, val); + } + break; + case hwmon_power: + switch (attr) + { + case hwmon_power_input: + return pac193x_get_power(dev, PAC193X_CMD_VPOWER1, + channel, val); + } + break; + case hwmon_energy: + switch (attr) + { + case hwmon_energy_input: + return pac193x_get_energy(dev, PAC193X_CMD_VPOWER1_ACC, + channel, val); + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_ops pac193x_hwmon_ops = { + .is_visible = pac1934x_is_visible, + .read = pac193x_read, + .read_string = pac1934x_read_string, +}; + +static struct hwmon_chip_info pac193x_chip_info = { + .ops = &pac193x_hwmon_ops, + .info = NULL, +}; + +static void update_reg_data(struct work_struct *work) +{ + static u32 updata_cnt = 0; + struct pac193x_data *data; + int act_val = 0, ctrl_val = 0, slow_val = 0; + int num = 0; + bool is_neg = false; + + data = container_of(work, struct pac193x_data, update_work.work); + if ((data->energy_acc_count == 0) || (updata_cnt < data->energy_acc_count)) + { + pac193x_send_refresh_cmd(data, PAC193X_CMD_REFRESH_V); + updata_cnt++; + } + else + { + pac193x_send_refresh_cmd(data, PAC193X_CMD_REFRESH); + updata_cnt = 0; + } + + act_val = pac193x_read_byte_data(data, PAC193X_CMD_NEG_PWR_ACT); + + for (num = 0; num < PAC193X_MAX_CHAN_CNT; num++) + { + is_neg = false; + if (0x1 == ((act_val >> num) & 0x1)) + { + /* Vsource=32*Vbus/2^15 = Vbus/2^10=Vbus/1024 */ + data->vbus_denominator[3 - num] = 1024; + is_neg = true; + } + else + { + /* Vsource=32*Vbus/2^16 = Vbus/2^10=Vbus/1024 */ + data->vbus_denominator[3 - num] = 2048; + } + + if (0x1 == ((act_val >> (num + 4)) & 0x1)) + { + /* 2^15 */ + data->vsense_denominator[3 - num] = 32768; + is_neg = true; + } + else + { + /*2^16 */ + data->vsense_denominator[3 - num] = 65536; + } + if (true == is_neg) + { + /* 2^28 */ + data->vpower_denominator[3 - num] = 134217728; + } + else + { + /* 2^29 */ + data->vpower_denominator[3 - num] = 268435456; + } + } + + slow_val = pac193x_read_byte_data(data, PAC193X_CMD_SLOW); + ctrl_val = pac193x_read_byte_data(data, PAC193X_CMD_CTRL_ACT); + if ((0x1 == ((slow_val >> 7) & 0x1)) && + (0x0 == ((ctrl_val >> 3) & 0x1))) + { + data->sample_rate = 8; + } + else + { + switch ((ctrl_val >> 6) & 0x3) + { + case 0: + data->sample_rate = 1024; + break; + case 1: + data->sample_rate = 256; + break; + case 2: + data->sample_rate = 64; + break; + case 3: + data->sample_rate = 8; + break; + default: + break; + } + } + queue_delayed_work(data->update_workqueue, &data->update_work, + msecs_to_jiffies(data->update_time_ms)); +} + +static int pac193x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pac193x_data *data; + struct device *hwmon_dev; + int ret = 0; + int num = 0; + const char *output_names[4]; + struct device_node *np; + enum PAC193X_CHAN_INDEX chan_index = + (enum PAC193X_CHAN_INDEX)of_device_get_match_data(&client->dev); + + switch (chan_index) + { + case pac1931_index: + pac193x_chip_info.info = pac1931_info; + break; + case pac1932_index: + pac193x_chip_info.info = pac1932_info; + break; + case pac1933_index: + pac193x_chip_info.info = pac1933_info; + break; + case pac1934_index: + pac193x_chip_info.info = pac1934_info; + break; + default: + break; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + for (num = 0; num < PAC193X_MAX_CHAN_CNT; num++) + { + data->vbus_denominator[num] = 1; + data->vsense_denominator[num] = 1; + data->shunt_resistors[num] = 1; + } + + np = of_node_get(dev->of_node); + if (!np) + return -EINVAL; + ret = of_property_read_u32(np, "update_time_ms", + &data->update_time_ms); + if (0 != ret) + { + dev_err(dev, "can not get update_time_ms:%d\n", ret); + data->update_time_ms = 100; + } + + ret = of_property_read_u32(np, "energy_acc_count", + &data->energy_acc_count); + if (0 != ret) + { + dev_err(dev, "can not get energy_acc_count:%d\n", ret); + data->energy_acc_count = 0; + } + ret = of_property_read_u32_array(np, "shunt_resistors", + data->shunt_resistors, + PAC193X_MAX_CHAN_CNT); + if (0 != ret) + { + dev_err(dev, "can not get shunt_resistors:%d\n", ret); + } + + dev_info(dev, + "update_time:%d,energy_acc_count:%d,resistances:%d,%d,%d,%dmOhm\n", + data->update_time_ms, data->energy_acc_count, + data->shunt_resistors[0], data->shunt_resistors[1], + data->shunt_resistors[2], data->shunt_resistors[3]); + + np = of_node_get(np); + if (!np) + return -EINVAL; + of_property_read_string_array(np, "eswin,chan_label", output_names, 4); + + strcpy(data->pac193x_label[0], output_names[0]); + strcpy(data->pac193x_label[1], output_names[1]); + strcpy(data->pac193x_label[2], output_names[2]); + strcpy(data->pac193x_label[3], output_names[3]); + + mutex_init(&data->config_lock); + data->client = client; + mutex_lock(&data->config_lock); + i2c_smbus_write_byte_data(data->client, PAC193X_CMD_CTRL, 0x8); + mutex_unlock(&data->config_lock); + pac193x_send_refresh_cmd(data, PAC193X_CMD_REFRESH); + + hwmon_dev = devm_hwmon_device_register_with_info( + dev, client->name, data, &pac193x_chip_info, pac193x_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + data->update_workqueue = create_workqueue("update_workqueue"); + + INIT_DELAYED_WORK(&data->update_work, update_reg_data); + queue_delayed_work(data->update_workqueue, &data->update_work, + msecs_to_jiffies(0)); + + return 0; +} + +static const struct of_device_id __maybe_unused pac193x_of_match[] = { + {.compatible = "microchip,pac1931", .data = (void *)pac1931_index}, + {.compatible = "microchip,pac1932", .data = (void *)pac1932_index}, + {.compatible = "microchip,pac1933", .data = (void *)pac1933_index}, + {.compatible = "microchip,pac1934", .data = (void *)pac1934_index}, + {}, +}; +MODULE_DEVICE_TABLE(of, pac193x_of_match); + +static struct i2c_driver pac193x_driver = { + .driver = { + .name = "pac193x", + .of_match_table = of_match_ptr(pac193x_of_match), + }, + .probe = pac193x_probe, +}; + +module_i2c_driver(pac193x_driver); + +MODULE_AUTHOR("Yang Wei