18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MEN 14F021P00 Board Management Controller (BMC) hwmon driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This is the core hwmon driver of the MEN 14F021P00 BMC. 68c2ecf20Sopenharmony_ci * The BMC monitors the board voltages which can be access with this 78c2ecf20Sopenharmony_ci * driver through sysfs. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 168c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 178c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define DRV_NAME "menf21bmc_hwmon" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define BMC_VOLT_COUNT 5 258c2ecf20Sopenharmony_ci#define MENF21BMC_V33 0 268c2ecf20Sopenharmony_ci#define MENF21BMC_V5 1 278c2ecf20Sopenharmony_ci#define MENF21BMC_V12 2 288c2ecf20Sopenharmony_ci#define MENF21BMC_V5_SB 3 298c2ecf20Sopenharmony_ci#define MENF21BMC_VBAT 4 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define IDX_TO_VOLT_MIN_CMD(idx) (0x40 + idx) 328c2ecf20Sopenharmony_ci#define IDX_TO_VOLT_MAX_CMD(idx) (0x50 + idx) 338c2ecf20Sopenharmony_ci#define IDX_TO_VOLT_INP_CMD(idx) (0x60 + idx) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct menf21bmc_hwmon { 368c2ecf20Sopenharmony_ci bool valid; 378c2ecf20Sopenharmony_ci struct i2c_client *i2c_client; 388c2ecf20Sopenharmony_ci unsigned long last_update; 398c2ecf20Sopenharmony_ci int in_val[BMC_VOLT_COUNT]; 408c2ecf20Sopenharmony_ci int in_min[BMC_VOLT_COUNT]; 418c2ecf20Sopenharmony_ci int in_max[BMC_VOLT_COUNT]; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const char *const input_names[] = { 458c2ecf20Sopenharmony_ci [MENF21BMC_V33] = "MON_3_3V", 468c2ecf20Sopenharmony_ci [MENF21BMC_V5] = "MON_5V", 478c2ecf20Sopenharmony_ci [MENF21BMC_V12] = "MON_12V", 488c2ecf20Sopenharmony_ci [MENF21BMC_V5_SB] = "5V_STANDBY", 498c2ecf20Sopenharmony_ci [MENF21BMC_VBAT] = "VBAT" 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct menf21bmc_hwmon *menf21bmc_hwmon_update(struct device *dev) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int i; 558c2ecf20Sopenharmony_ci int val; 568c2ecf20Sopenharmony_ci struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev); 578c2ecf20Sopenharmony_ci struct menf21bmc_hwmon *data_ret = drv_data; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (time_after(jiffies, drv_data->last_update + HZ) 608c2ecf20Sopenharmony_ci || !drv_data->valid) { 618c2ecf20Sopenharmony_ci for (i = 0; i < BMC_VOLT_COUNT; i++) { 628c2ecf20Sopenharmony_ci val = i2c_smbus_read_word_data(drv_data->i2c_client, 638c2ecf20Sopenharmony_ci IDX_TO_VOLT_INP_CMD(i)); 648c2ecf20Sopenharmony_ci if (val < 0) { 658c2ecf20Sopenharmony_ci data_ret = ERR_PTR(val); 668c2ecf20Sopenharmony_ci goto abort; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci drv_data->in_val[i] = val; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci drv_data->last_update = jiffies; 718c2ecf20Sopenharmony_ci drv_data->valid = true; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ciabort: 748c2ecf20Sopenharmony_ci return data_ret; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int menf21bmc_hwmon_get_volt_limits(struct menf21bmc_hwmon *drv_data) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int i, val; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < BMC_VOLT_COUNT; i++) { 828c2ecf20Sopenharmony_ci val = i2c_smbus_read_word_data(drv_data->i2c_client, 838c2ecf20Sopenharmony_ci IDX_TO_VOLT_MIN_CMD(i)); 848c2ecf20Sopenharmony_ci if (val < 0) 858c2ecf20Sopenharmony_ci return val; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci drv_data->in_min[i] = val; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci val = i2c_smbus_read_word_data(drv_data->i2c_client, 908c2ecf20Sopenharmony_ci IDX_TO_VOLT_MAX_CMD(i)); 918c2ecf20Sopenharmony_ci if (val < 0) 928c2ecf20Sopenharmony_ci return val; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci drv_data->in_max[i] = val; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic ssize_t 1008c2ecf20Sopenharmony_cilabel_show(struct device *dev, struct device_attribute *devattr, char *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", input_names[attr->index]); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic ssize_t 1088c2ecf20Sopenharmony_ciin_show(struct device *dev, struct device_attribute *devattr, char *buf) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1118c2ecf20Sopenharmony_ci struct menf21bmc_hwmon *drv_data = menf21bmc_hwmon_update(dev); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (IS_ERR(drv_data)) 1148c2ecf20Sopenharmony_ci return PTR_ERR(drv_data); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", drv_data->in_val[attr->index]); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic ssize_t 1208c2ecf20Sopenharmony_cimin_show(struct device *dev, struct device_attribute *devattr, char *buf) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1238c2ecf20Sopenharmony_ci struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", drv_data->in_min[attr->index]); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic ssize_t 1298c2ecf20Sopenharmony_cimax_show(struct device *dev, struct device_attribute *devattr, char *buf) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1328c2ecf20Sopenharmony_ci struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", drv_data->in_max[attr->index]); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); 1388c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_min, min, 0); 1398c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_max, max, 0); 1408c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_label, label, 0); 1418c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); 1428c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_min, min, 1); 1438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_max, max, 1); 1448c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_label, label, 1); 1458c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); 1468c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_min, min, 2); 1478c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_max, max, 2); 1488c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_label, label, 2); 1498c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); 1508c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_min, min, 3); 1518c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_max, max, 3); 1528c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_label, label, 3); 1538c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); 1548c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_min, min, 4); 1558c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_max, max, 4); 1568c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_label, label, 4); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct attribute *menf21bmc_hwmon_attrs[] = { 1598c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 1608c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_min.dev_attr.attr, 1618c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_max.dev_attr.attr, 1628c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_label.dev_attr.attr, 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 1658c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 1668c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 1678c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_label.dev_attr.attr, 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 1708c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 1718c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 1728c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_label.dev_attr.attr, 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_input.dev_attr.attr, 1758c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_min.dev_attr.attr, 1768c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_max.dev_attr.attr, 1778c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_label.dev_attr.attr, 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_input.dev_attr.attr, 1808c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_min.dev_attr.attr, 1818c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_max.dev_attr.attr, 1828c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_label.dev_attr.attr, 1838c2ecf20Sopenharmony_ci NULL 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(menf21bmc_hwmon); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int menf21bmc_hwmon_probe(struct platform_device *pdev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci struct menf21bmc_hwmon *drv_data; 1928c2ecf20Sopenharmony_ci struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent); 1938c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci drv_data = devm_kzalloc(&pdev->dev, sizeof(struct menf21bmc_hwmon), 1968c2ecf20Sopenharmony_ci GFP_KERNEL); 1978c2ecf20Sopenharmony_ci if (!drv_data) 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci drv_data->i2c_client = i2c_client; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = menf21bmc_hwmon_get_volt_limits(drv_data); 2038c2ecf20Sopenharmony_ci if (ret) { 2048c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to read sensor limits"); 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, 2098c2ecf20Sopenharmony_ci "menf21bmc", drv_data, 2108c2ecf20Sopenharmony_ci menf21bmc_hwmon_groups); 2118c2ecf20Sopenharmony_ci if (IS_ERR(hwmon_dev)) 2128c2ecf20Sopenharmony_ci return PTR_ERR(hwmon_dev); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "MEN 14F021P00 BMC hwmon device enabled"); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct platform_driver menf21bmc_hwmon = { 2208c2ecf20Sopenharmony_ci .probe = menf21bmc_hwmon_probe, 2218c2ecf20Sopenharmony_ci .driver = { 2228c2ecf20Sopenharmony_ci .name = DRV_NAME, 2238c2ecf20Sopenharmony_ci }, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cimodule_platform_driver(menf21bmc_hwmon); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 2298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon"); 2308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2318c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:menf21bmc_hwmon"); 232