/* * rg_cpld.c - A driver for pmbus psu * * Copyright (c) 2019 * * 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; either version 2 of the License, or * (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #define MAGIC_PSU_RATE (0xA7) #define MAGIC_PSU_OUT_CURRENT (0x8C) #define MAGIC_PSU_OUT_VOLTAGE (0x8B) #define MAGIC_PSU_IN_VOLTAGE (0x88) #define MAGIC_PSU_IN_CURRENT (0x89) #define MAGIC_PSU_TEMP (0x8D) #define MAGIC_PSU_TYPE (0x25) #define MAGIC_PSU_SN (0x38) #define MAGIC_PSU_HW (0x35) #define PSU_SIZE (256) typedef enum { DBG_START, DBG_VERBOSE, DBG_KEY, DBG_WARN, DBG_ERROR, DBG_END, } dbg_level_t; static int debuglevel = 0; #define DBG_DEBUG(fmt, arg...) \ do { \ if (debuglevel > DBG_START && debuglevel < DBG_ERROR) { \ printk(KERN_INFO "[DEBUG]:<%s, %d>:" fmt, \ __FUNCTION__, __LINE__, ##arg); \ } else if (debuglevel >= DBG_ERROR) { \ printk(KERN_ERR "[DEBUG]:<%s, %d>:" fmt, __FUNCTION__, \ __LINE__, ##arg); \ } else { \ } \ } while (0) #define DBG_INFO(fmt, arg...) \ do { \ if (debuglevel > DBG_KEY) { \ printk(KERN_INFO "[INFO]:<%s, %d>:" fmt, __FUNCTION__, \ __LINE__, ##arg); \ } \ } while (0) #define DBG_ERROR(fmt, arg...) \ do { \ if (debuglevel > DBG_START) { \ printk(KERN_ERR "[ERROR]:<%s, %d>:" fmt, __FUNCTION__, \ __LINE__, ##arg); \ } \ } while (0) static const unsigned short rg_i2c_psu[] = { 0x50, 0x53, 0x58, 0x5b, I2C_CLIENT_END }; extern s32 platform_i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command); extern s32 platform_i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values); struct psu_data { struct i2c_client *client; struct device *hwmon_dev; struct mutex update_lock; char valid; /* !=0 if registers are valid */ unsigned long last_updated; /* In jiffies */ u8 data[PSU_SIZE]; /* Register value */ }; static ssize_t show_psu_sysfs_value(struct device *dev, struct device_attribute *da, char *buf); static ssize_t show_sysfs_15_value(struct device *dev, struct device_attribute *da, char *buf); static ssize_t show_psu_value(struct device *dev, struct device_attribute *da, char *buf); static SENSOR_DEVICE_ATTR(psu_rate, S_IRUGO, show_psu_sysfs_value, NULL, MAGIC_PSU_RATE); static SENSOR_DEVICE_ATTR(psu_out_current, S_IRUGO, show_psu_sysfs_value, NULL, MAGIC_PSU_OUT_CURRENT); static SENSOR_DEVICE_ATTR(psu_out_voltage, S_IRUGO, show_psu_sysfs_value, NULL, MAGIC_PSU_OUT_VOLTAGE); static SENSOR_DEVICE_ATTR(psu_in_voltage, S_IRUGO, show_psu_sysfs_value, NULL, MAGIC_PSU_IN_VOLTAGE); static SENSOR_DEVICE_ATTR(psu_in_current, S_IRUGO, show_psu_sysfs_value, NULL, MAGIC_PSU_IN_CURRENT); static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_psu_sysfs_value, NULL, MAGIC_PSU_TEMP); static SENSOR_DEVICE_ATTR(psu_type, S_IRUGO, show_sysfs_15_value, NULL, MAGIC_PSU_TYPE); static SENSOR_DEVICE_ATTR(psu_sn, S_IRUGO, show_sysfs_15_value, NULL, MAGIC_PSU_SN); static SENSOR_DEVICE_ATTR(psu_hw, S_IRUGO, show_psu_value, NULL, MAGIC_PSU_HW); static struct attribute *psu_pmbus_sysfs_attrs[] = { &sensor_dev_attr_psu_rate.dev_attr.attr, &sensor_dev_attr_psu_out_current.dev_attr.attr, &sensor_dev_attr_psu_out_voltage.dev_attr.attr, &sensor_dev_attr_psu_in_voltage.dev_attr.attr, &sensor_dev_attr_psu_in_current.dev_attr.attr, &sensor_dev_attr_psu_temp.dev_attr.attr, NULL }; static struct attribute *psu_fru_sysfs_attrs[] = { &sensor_dev_attr_psu_type.dev_attr.attr, &sensor_dev_attr_psu_sn.dev_attr.attr, &sensor_dev_attr_psu_hw.dev_attr.attr, NULL }; static const struct attribute_group psu_pmbus_sysfs_attrs_group = { .attrs = psu_pmbus_sysfs_attrs, }; static const struct attribute_group psu_fru_sysfs_attrs_group = { .attrs = psu_fru_sysfs_attrs, }; static ssize_t show_psu_value(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct psu_data *data = i2c_get_clientdata(client); int ret; char psu_buf[PSU_SIZE]; memset(psu_buf, 0, PSU_SIZE); mutex_lock(&data->update_lock); ret = platform_i2c_smbus_read_i2c_block_data(client, attr->index, 2, psu_buf); if (ret < 0) { DBG_ERROR("Failed to read psu\n"); } DBG_DEBUG("cpld reg pos:0x%x value:0x%02x\n", attr->index, data->data[0]); mutex_unlock(&data->update_lock); return snprintf(buf, 3, "%s\n", psu_buf); } static int linear_to_value(short reg, bool v_out) { short exponent; int mantissa; long val; if (v_out) { exponent = -9; mantissa = reg; } else { exponent = reg >> 11; mantissa = (((reg & 0x7ff) << 5)) >> 5; } val = mantissa; val = val * 1000L; if (exponent >= 0) { val <<= exponent; } else { val >>= -exponent; } return val; } static ssize_t show_psu_sysfs_value(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct psu_data *data = i2c_get_clientdata(client); int ret; u8 smbud_buf[PSU_SIZE]; uint16_t value; int result; ret = -1; memset(smbud_buf, 0, PSU_SIZE); mutex_lock(&data->update_lock); DBG_DEBUG("ret:%d", ret); ret = platform_i2c_smbus_read_i2c_block_data(client, attr->index, 2, smbud_buf); if (ret < 0) { DBG_ERROR("Failed to read psu \n"); } value = smbud_buf[1]; value = value << 8; value |= smbud_buf[0]; if (attr->index == 0x8b) { result = linear_to_value(value, true); } else { result = linear_to_value(value, false); } mutex_unlock(&data->update_lock); return snprintf(buf, PSU_SIZE, "%d\n", result); } static ssize_t show_sysfs_15_value(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct psu_data *data = i2c_get_clientdata(client); int ret; u8 smbud_buf[PSU_SIZE]; memset(smbud_buf, 0, PSU_SIZE); mutex_lock(&data->update_lock); ret = platform_i2c_smbus_read_i2c_block_data(client, attr->index, 15, smbud_buf); if (ret < 0) { DBG_ERROR("Failed to read psu\n"); } mutex_unlock(&data->update_lock); return snprintf(buf, PSU_SIZE, "%s\n", smbud_buf); } static ssize_t show_sysfs_13_value(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct psu_data *data = i2c_get_clientdata(client); int ret; u8 smbud_buf[PSU_SIZE]; memset(smbud_buf, 0, PSU_SIZE); mutex_lock(&data->update_lock); ret = platform_i2c_smbus_read_i2c_block_data(client, attr->index, 13, smbud_buf); if (ret < 0) { DBG_ERROR("Failed to read psu \n"); } mutex_unlock(&data->update_lock); return snprintf(buf, PSU_SIZE, "%s\n", smbud_buf); } static int psu_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; int conf; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; conf = platform_i2c_smbus_read_byte_data(new_client, 0); if (!conf) return -ENODEV; return 0; } static int psu_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct psu_data *data; int status; status = -1; data = devm_kzalloc(&client->dev, sizeof(struct psu_data), GFP_KERNEL); if (!data) return -ENOMEM; data->client = client; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); switch (client->addr) { case 0x50: case 0x53: status = sysfs_create_group(&client->dev.kobj, &psu_fru_sysfs_attrs_group); if (status != 0) { DBG_ERROR("%s %d sysfs_create_group err\n", __func__, __LINE__); } break; case 0x58: case 0x5b: status = sysfs_create_group(&client->dev.kobj, &psu_pmbus_sysfs_attrs_group); if (status != 0) { DBG_ERROR("%s %d sysfs_create_group err\n", __func__, __LINE__); break; } break; default: break; } return status; } static int psu_remove(struct i2c_client *client) { switch (client->addr) { case 0x50: case 0x53: sysfs_remove_group(&client->dev.kobj, &psu_fru_sysfs_attrs_group); break; case 0x58: case 0x5b: sysfs_remove_group(&client->dev.kobj, &psu_pmbus_sysfs_attrs_group); break; default: break; } return 0; } static const struct i2c_device_id psu_id[] = { { "rg_psu", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, psu_id); static struct i2c_driver rg_psu_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "rg_psu", }, .probe = psu_probe, .remove = psu_remove, .id_table = psu_id, .detect = psu_detect, .address_list = rg_i2c_psu, }; module_i2c_driver(rg_psu_driver); MODULE_AUTHOR("support "); MODULE_DESCRIPTION("ragile pmbus psu driver"); MODULE_LICENSE("GPL");