18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 ARM Limited 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define DRVNAME "vexpress-hwmon" 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) DRVNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/vexpress.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct vexpress_hwmon_data { 218c2ecf20Sopenharmony_ci struct device *hwmon_dev; 228c2ecf20Sopenharmony_ci struct regmap *reg; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic ssize_t vexpress_hwmon_label_show(struct device *dev, 268c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buffer) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci const char *label = of_get_property(dev->of_node, "label", NULL); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci return snprintf(buffer, PAGE_SIZE, "%s\n", label); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic ssize_t vexpress_hwmon_u32_show(struct device *dev, 348c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buffer) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct vexpress_hwmon_data *data = dev_get_drvdata(dev); 378c2ecf20Sopenharmony_ci int err; 388c2ecf20Sopenharmony_ci u32 value; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci err = regmap_read(data->reg, 0, &value); 418c2ecf20Sopenharmony_ci if (err) 428c2ecf20Sopenharmony_ci return err; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return snprintf(buffer, PAGE_SIZE, "%u\n", value / 458c2ecf20Sopenharmony_ci to_sensor_dev_attr(dev_attr)->index); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic ssize_t vexpress_hwmon_u64_show(struct device *dev, 498c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buffer) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct vexpress_hwmon_data *data = dev_get_drvdata(dev); 528c2ecf20Sopenharmony_ci int err; 538c2ecf20Sopenharmony_ci u32 value_hi, value_lo; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci err = regmap_read(data->reg, 0, &value_lo); 568c2ecf20Sopenharmony_ci if (err) 578c2ecf20Sopenharmony_ci return err; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci err = regmap_read(data->reg, 1, &value_hi); 608c2ecf20Sopenharmony_ci if (err) 618c2ecf20Sopenharmony_ci return err; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return snprintf(buffer, PAGE_SIZE, "%llu\n", 648c2ecf20Sopenharmony_ci div_u64(((u64)value_hi << 32) | value_lo, 658c2ecf20Sopenharmony_ci to_sensor_dev_attr(dev_attr)->index)); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, 698c2ecf20Sopenharmony_ci struct attribute *attr, int index) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 728c2ecf20Sopenharmony_ci struct device_attribute *dev_attr = container_of(attr, 738c2ecf20Sopenharmony_ci struct device_attribute, attr); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (dev_attr->show == vexpress_hwmon_label_show && 768c2ecf20Sopenharmony_ci !of_get_property(dev->of_node, "label", NULL)) 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return attr->mode; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct vexpress_hwmon_type { 838c2ecf20Sopenharmony_ci const char *name; 848c2ecf20Sopenharmony_ci const struct attribute_group **attr_groups; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#if !defined(CONFIG_REGULATOR_VEXPRESS) 888c2ecf20Sopenharmony_cistatic DEVICE_ATTR(in1_label, 0444, vexpress_hwmon_label_show, NULL); 898c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, vexpress_hwmon_u32, 1000); 908c2ecf20Sopenharmony_cistatic struct attribute *vexpress_hwmon_attrs_volt[] = { 918c2ecf20Sopenharmony_ci &dev_attr_in1_label.attr, 928c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 938c2ecf20Sopenharmony_ci NULL 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_cistatic struct attribute_group vexpress_hwmon_group_volt = { 968c2ecf20Sopenharmony_ci .is_visible = vexpress_hwmon_attr_is_visible, 978c2ecf20Sopenharmony_ci .attrs = vexpress_hwmon_attrs_volt, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_cistatic struct vexpress_hwmon_type vexpress_hwmon_volt = { 1008c2ecf20Sopenharmony_ci .name = "vexpress_volt", 1018c2ecf20Sopenharmony_ci .attr_groups = (const struct attribute_group *[]) { 1028c2ecf20Sopenharmony_ci &vexpress_hwmon_group_volt, 1038c2ecf20Sopenharmony_ci NULL, 1048c2ecf20Sopenharmony_ci }, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(curr1_label, 0444, vexpress_hwmon_label_show, NULL); 1098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_input, vexpress_hwmon_u32, 1000); 1108c2ecf20Sopenharmony_cistatic struct attribute *vexpress_hwmon_attrs_amp[] = { 1118c2ecf20Sopenharmony_ci &dev_attr_curr1_label.attr, 1128c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_input.dev_attr.attr, 1138c2ecf20Sopenharmony_ci NULL 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_cistatic struct attribute_group vexpress_hwmon_group_amp = { 1168c2ecf20Sopenharmony_ci .is_visible = vexpress_hwmon_attr_is_visible, 1178c2ecf20Sopenharmony_ci .attrs = vexpress_hwmon_attrs_amp, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_cistatic struct vexpress_hwmon_type vexpress_hwmon_amp = { 1208c2ecf20Sopenharmony_ci .name = "vexpress_amp", 1218c2ecf20Sopenharmony_ci .attr_groups = (const struct attribute_group *[]) { 1228c2ecf20Sopenharmony_ci &vexpress_hwmon_group_amp, 1238c2ecf20Sopenharmony_ci NULL 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic DEVICE_ATTR(temp1_label, 0444, vexpress_hwmon_label_show, NULL); 1288c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, vexpress_hwmon_u32, 1000); 1298c2ecf20Sopenharmony_cistatic struct attribute *vexpress_hwmon_attrs_temp[] = { 1308c2ecf20Sopenharmony_ci &dev_attr_temp1_label.attr, 1318c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 1328c2ecf20Sopenharmony_ci NULL 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_cistatic struct attribute_group vexpress_hwmon_group_temp = { 1358c2ecf20Sopenharmony_ci .is_visible = vexpress_hwmon_attr_is_visible, 1368c2ecf20Sopenharmony_ci .attrs = vexpress_hwmon_attrs_temp, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_cistatic struct vexpress_hwmon_type vexpress_hwmon_temp = { 1398c2ecf20Sopenharmony_ci .name = "vexpress_temp", 1408c2ecf20Sopenharmony_ci .attr_groups = (const struct attribute_group *[]) { 1418c2ecf20Sopenharmony_ci &vexpress_hwmon_group_temp, 1428c2ecf20Sopenharmony_ci NULL 1438c2ecf20Sopenharmony_ci }, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power1_label, 0444, vexpress_hwmon_label_show, NULL); 1478c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input, vexpress_hwmon_u32, 1); 1488c2ecf20Sopenharmony_cistatic struct attribute *vexpress_hwmon_attrs_power[] = { 1498c2ecf20Sopenharmony_ci &dev_attr_power1_label.attr, 1508c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_input.dev_attr.attr, 1518c2ecf20Sopenharmony_ci NULL 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_cistatic struct attribute_group vexpress_hwmon_group_power = { 1548c2ecf20Sopenharmony_ci .is_visible = vexpress_hwmon_attr_is_visible, 1558c2ecf20Sopenharmony_ci .attrs = vexpress_hwmon_attrs_power, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_cistatic struct vexpress_hwmon_type vexpress_hwmon_power = { 1588c2ecf20Sopenharmony_ci .name = "vexpress_power", 1598c2ecf20Sopenharmony_ci .attr_groups = (const struct attribute_group *[]) { 1608c2ecf20Sopenharmony_ci &vexpress_hwmon_group_power, 1618c2ecf20Sopenharmony_ci NULL 1628c2ecf20Sopenharmony_ci }, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic DEVICE_ATTR(energy1_label, 0444, vexpress_hwmon_label_show, NULL); 1668c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(energy1_input, vexpress_hwmon_u64, 1); 1678c2ecf20Sopenharmony_cistatic struct attribute *vexpress_hwmon_attrs_energy[] = { 1688c2ecf20Sopenharmony_ci &dev_attr_energy1_label.attr, 1698c2ecf20Sopenharmony_ci &sensor_dev_attr_energy1_input.dev_attr.attr, 1708c2ecf20Sopenharmony_ci NULL 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_cistatic struct attribute_group vexpress_hwmon_group_energy = { 1738c2ecf20Sopenharmony_ci .is_visible = vexpress_hwmon_attr_is_visible, 1748c2ecf20Sopenharmony_ci .attrs = vexpress_hwmon_attrs_energy, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_cistatic struct vexpress_hwmon_type vexpress_hwmon_energy = { 1778c2ecf20Sopenharmony_ci .name = "vexpress_energy", 1788c2ecf20Sopenharmony_ci .attr_groups = (const struct attribute_group *[]) { 1798c2ecf20Sopenharmony_ci &vexpress_hwmon_group_energy, 1808c2ecf20Sopenharmony_ci NULL 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct of_device_id vexpress_hwmon_of_match[] = { 1858c2ecf20Sopenharmony_ci#if !defined(CONFIG_REGULATOR_VEXPRESS) 1868c2ecf20Sopenharmony_ci { 1878c2ecf20Sopenharmony_ci .compatible = "arm,vexpress-volt", 1888c2ecf20Sopenharmony_ci .data = &vexpress_hwmon_volt, 1898c2ecf20Sopenharmony_ci }, 1908c2ecf20Sopenharmony_ci#endif 1918c2ecf20Sopenharmony_ci { 1928c2ecf20Sopenharmony_ci .compatible = "arm,vexpress-amp", 1938c2ecf20Sopenharmony_ci .data = &vexpress_hwmon_amp, 1948c2ecf20Sopenharmony_ci }, { 1958c2ecf20Sopenharmony_ci .compatible = "arm,vexpress-temp", 1968c2ecf20Sopenharmony_ci .data = &vexpress_hwmon_temp, 1978c2ecf20Sopenharmony_ci }, { 1988c2ecf20Sopenharmony_ci .compatible = "arm,vexpress-power", 1998c2ecf20Sopenharmony_ci .data = &vexpress_hwmon_power, 2008c2ecf20Sopenharmony_ci }, { 2018c2ecf20Sopenharmony_ci .compatible = "arm,vexpress-energy", 2028c2ecf20Sopenharmony_ci .data = &vexpress_hwmon_energy, 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci {} 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int vexpress_hwmon_probe(struct platform_device *pdev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci const struct of_device_id *match; 2118c2ecf20Sopenharmony_ci struct vexpress_hwmon_data *data; 2128c2ecf20Sopenharmony_ci const struct vexpress_hwmon_type *type; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 2158c2ecf20Sopenharmony_ci if (!data) 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); 2208c2ecf20Sopenharmony_ci if (!match) 2218c2ecf20Sopenharmony_ci return -ENODEV; 2228c2ecf20Sopenharmony_ci type = match->data; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci data->reg = devm_regmap_init_vexpress_config(&pdev->dev); 2258c2ecf20Sopenharmony_ci if (IS_ERR(data->reg)) 2268c2ecf20Sopenharmony_ci return PTR_ERR(data->reg); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci data->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, 2298c2ecf20Sopenharmony_ci type->name, data, type->attr_groups); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(data->hwmon_dev); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic struct platform_driver vexpress_hwmon_driver = { 2358c2ecf20Sopenharmony_ci .probe = vexpress_hwmon_probe, 2368c2ecf20Sopenharmony_ci .driver = { 2378c2ecf20Sopenharmony_ci .name = DRVNAME, 2388c2ecf20Sopenharmony_ci .of_match_table = vexpress_hwmon_of_match, 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cimodule_platform_driver(vexpress_hwmon_driver); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); 2458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Versatile Express hwmon sensors driver"); 2468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2478c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:vexpress-hwmon"); 248