18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IBM PowerNV platform sensors for temperature/fan/voltage/power 48c2ecf20Sopenharmony_ci * Copyright (C) 2014 IBM 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define DRVNAME "ibmpowernv" 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) DRVNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 148c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <asm/opal.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <asm/cputhreads.h> 228c2ecf20Sopenharmony_ci#include <asm/smp.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MAX_ATTR_LEN 32 258c2ecf20Sopenharmony_ci#define MAX_LABEL_LEN 64 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Sensor suffix name from DT */ 288c2ecf20Sopenharmony_ci#define DT_FAULT_ATTR_SUFFIX "faulted" 298c2ecf20Sopenharmony_ci#define DT_DATA_ATTR_SUFFIX "data" 308c2ecf20Sopenharmony_ci#define DT_THRESHOLD_ATTR_SUFFIX "thrs" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Enumerates all the types of sensors in the POWERNV platform and does index 348c2ecf20Sopenharmony_ci * into 'struct sensor_group' 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cienum sensors { 378c2ecf20Sopenharmony_ci FAN, 388c2ecf20Sopenharmony_ci TEMP, 398c2ecf20Sopenharmony_ci POWER_SUPPLY, 408c2ecf20Sopenharmony_ci POWER_INPUT, 418c2ecf20Sopenharmony_ci CURRENT, 428c2ecf20Sopenharmony_ci ENERGY, 438c2ecf20Sopenharmony_ci MAX_SENSOR_TYPE, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define INVALID_INDEX (-1U) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * 'compatible' string properties for sensor types as defined in old 508c2ecf20Sopenharmony_ci * PowerNV firmware (skiboot). These are ordered as 'enum sensors'. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic const char * const legacy_compatibles[] = { 538c2ecf20Sopenharmony_ci "ibm,opal-sensor-cooling-fan", 548c2ecf20Sopenharmony_ci "ibm,opal-sensor-amb-temp", 558c2ecf20Sopenharmony_ci "ibm,opal-sensor-power-supply", 568c2ecf20Sopenharmony_ci "ibm,opal-sensor-power" 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct sensor_group { 608c2ecf20Sopenharmony_ci const char *name; /* matches property 'sensor-type' */ 618c2ecf20Sopenharmony_ci struct attribute_group group; 628c2ecf20Sopenharmony_ci u32 attr_count; 638c2ecf20Sopenharmony_ci u32 hwmon_index; 648c2ecf20Sopenharmony_ci} sensor_groups[] = { 658c2ecf20Sopenharmony_ci { "fan" }, 668c2ecf20Sopenharmony_ci { "temp" }, 678c2ecf20Sopenharmony_ci { "in" }, 688c2ecf20Sopenharmony_ci { "power" }, 698c2ecf20Sopenharmony_ci { "curr" }, 708c2ecf20Sopenharmony_ci { "energy" }, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct sensor_data { 748c2ecf20Sopenharmony_ci u32 id; /* An opaque id of the firmware for each sensor */ 758c2ecf20Sopenharmony_ci u32 hwmon_index; 768c2ecf20Sopenharmony_ci u32 opal_index; 778c2ecf20Sopenharmony_ci enum sensors type; 788c2ecf20Sopenharmony_ci char label[MAX_LABEL_LEN]; 798c2ecf20Sopenharmony_ci char name[MAX_ATTR_LEN]; 808c2ecf20Sopenharmony_ci struct device_attribute dev_attr; 818c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct sensor_group_data { 858c2ecf20Sopenharmony_ci struct mutex mutex; 868c2ecf20Sopenharmony_ci u32 gid; 878c2ecf20Sopenharmony_ci bool enable; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistruct platform_data { 918c2ecf20Sopenharmony_ci const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1]; 928c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data; 938c2ecf20Sopenharmony_ci u32 sensors_count; /* Total count of sensors from each group */ 948c2ecf20Sopenharmony_ci u32 nr_sensor_groups; /* Total number of sensor groups */ 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, 988c2ecf20Sopenharmony_ci char *buf) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct sensor_data *sdata = container_of(devattr, struct sensor_data, 1018c2ecf20Sopenharmony_ci dev_attr); 1028c2ecf20Sopenharmony_ci ssize_t ret; 1038c2ecf20Sopenharmony_ci u64 x; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (sdata->sgrp_data && !sdata->sgrp_data->enable) 1068c2ecf20Sopenharmony_ci return -ENODATA; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = opal_get_sensor_data_u64(sdata->id, &x); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (ret) 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Convert temperature to milli-degrees */ 1148c2ecf20Sopenharmony_ci if (sdata->type == TEMP) 1158c2ecf20Sopenharmony_ci x *= 1000; 1168c2ecf20Sopenharmony_ci /* Convert power to micro-watts */ 1178c2ecf20Sopenharmony_ci else if (sdata->type == POWER_INPUT) 1188c2ecf20Sopenharmony_ci x *= 1000000; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", x); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic ssize_t show_enable(struct device *dev, 1248c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct sensor_data *sdata = container_of(devattr, struct sensor_data, 1278c2ecf20Sopenharmony_ci dev_attr); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", sdata->sgrp_data->enable); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic ssize_t store_enable(struct device *dev, 1338c2ecf20Sopenharmony_ci struct device_attribute *devattr, 1348c2ecf20Sopenharmony_ci const char *buf, size_t count) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct sensor_data *sdata = container_of(devattr, struct sensor_data, 1378c2ecf20Sopenharmony_ci dev_attr); 1388c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data = sdata->sgrp_data; 1398c2ecf20Sopenharmony_ci int ret; 1408c2ecf20Sopenharmony_ci bool data; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = kstrtobool(buf, &data); 1438c2ecf20Sopenharmony_ci if (ret) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&sgrp_data->mutex); 1478c2ecf20Sopenharmony_ci if (ret) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (data != sgrp_data->enable) { 1518c2ecf20Sopenharmony_ci ret = sensor_group_enable(sgrp_data->gid, data); 1528c2ecf20Sopenharmony_ci if (!ret) 1538c2ecf20Sopenharmony_ci sgrp_data->enable = data; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!ret) 1578c2ecf20Sopenharmony_ci ret = count; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci mutex_unlock(&sgrp_data->mutex); 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic ssize_t show_label(struct device *dev, struct device_attribute *devattr, 1648c2ecf20Sopenharmony_ci char *buf) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct sensor_data *sdata = container_of(devattr, struct sensor_data, 1678c2ecf20Sopenharmony_ci dev_attr); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", sdata->label); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int get_logical_cpu(int hwcpu) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int cpu; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) 1778c2ecf20Sopenharmony_ci if (get_hard_smp_processor_id(cpu) == hwcpu) 1788c2ecf20Sopenharmony_ci return cpu; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return -ENOENT; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void make_sensor_label(struct device_node *np, 1848c2ecf20Sopenharmony_ci struct sensor_data *sdata, const char *label) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci u32 id; 1878c2ecf20Sopenharmony_ci size_t n; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * Core temp pretty print 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "ibm,pir", &id)) { 1958c2ecf20Sopenharmony_ci int cpuid = get_logical_cpu(id); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (cpuid >= 0) 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * The digital thermal sensors are associated 2008c2ecf20Sopenharmony_ci * with a core. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci n += scnprintf(sdata->label + n, 2038c2ecf20Sopenharmony_ci sizeof(sdata->label) - n, " %d", 2048c2ecf20Sopenharmony_ci cpuid); 2058c2ecf20Sopenharmony_ci else 2068c2ecf20Sopenharmony_ci n += scnprintf(sdata->label + n, 2078c2ecf20Sopenharmony_ci sizeof(sdata->label) - n, " phy%d", id); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Membuffer pretty print 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "ibm,chip-id", &id)) 2148c2ecf20Sopenharmony_ci n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, 2158c2ecf20Sopenharmony_ci " %d", id & 0xffff); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int get_sensor_index_attr(const char *name, u32 *index, char *attr) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci char *hash_pos = strchr(name, '#'); 2218c2ecf20Sopenharmony_ci char buf[8] = { 0 }; 2228c2ecf20Sopenharmony_ci char *dash_pos; 2238c2ecf20Sopenharmony_ci u32 copy_len; 2248c2ecf20Sopenharmony_ci int err; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!hash_pos) 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dash_pos = strchr(hash_pos, '-'); 2308c2ecf20Sopenharmony_ci if (!dash_pos) 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci copy_len = dash_pos - hash_pos - 1; 2348c2ecf20Sopenharmony_ci if (copy_len >= sizeof(buf)) 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci strncpy(buf, hash_pos + 1, copy_len); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci err = kstrtou32(buf, 10, index); 2408c2ecf20Sopenharmony_ci if (err) 2418c2ecf20Sopenharmony_ci return err; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci strncpy(attr, dash_pos + 1, MAX_ATTR_LEN); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const char *convert_opal_attr_name(enum sensors type, 2498c2ecf20Sopenharmony_ci const char *opal_attr) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci const char *attr_name = NULL; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) { 2548c2ecf20Sopenharmony_ci attr_name = "fault"; 2558c2ecf20Sopenharmony_ci } else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) { 2568c2ecf20Sopenharmony_ci attr_name = "input"; 2578c2ecf20Sopenharmony_ci } else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) { 2588c2ecf20Sopenharmony_ci if (type == TEMP) 2598c2ecf20Sopenharmony_ci attr_name = "max"; 2608c2ecf20Sopenharmony_ci else if (type == FAN) 2618c2ecf20Sopenharmony_ci attr_name = "min"; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return attr_name; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * This function translates the DT node name into the 'hwmon' attribute name. 2698c2ecf20Sopenharmony_ci * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc. 2708c2ecf20Sopenharmony_ci * which need to be mapped as fan2_input, temp1_max respectively before 2718c2ecf20Sopenharmony_ci * populating them inside hwmon device class. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic const char *parse_opal_node_name(const char *node_name, 2748c2ecf20Sopenharmony_ci enum sensors type, u32 *index) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci char attr_suffix[MAX_ATTR_LEN]; 2778c2ecf20Sopenharmony_ci const char *attr_name; 2788c2ecf20Sopenharmony_ci int err; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci err = get_sensor_index_attr(node_name, index, attr_suffix); 2818c2ecf20Sopenharmony_ci if (err) 2828c2ecf20Sopenharmony_ci return ERR_PTR(err); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci attr_name = convert_opal_attr_name(type, attr_suffix); 2858c2ecf20Sopenharmony_ci if (!attr_name) 2868c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return attr_name; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int get_sensor_type(struct device_node *np) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci enum sensors type; 2948c2ecf20Sopenharmony_ci const char *str; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) { 2978c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, legacy_compatibles[type])) 2988c2ecf20Sopenharmony_ci return type; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * Let's check if we have a newer device tree 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci if (!of_device_is_compatible(np, "ibm,opal-sensor")) 3058c2ecf20Sopenharmony_ci return MAX_SENSOR_TYPE; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (of_property_read_string(np, "sensor-type", &str)) 3088c2ecf20Sopenharmony_ci return MAX_SENSOR_TYPE; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci for (type = 0; type < MAX_SENSOR_TYPE; type++) 3118c2ecf20Sopenharmony_ci if (!strcmp(str, sensor_groups[type].name)) 3128c2ecf20Sopenharmony_ci return type; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return MAX_SENSOR_TYPE; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic u32 get_sensor_hwmon_index(struct sensor_data *sdata, 3188c2ecf20Sopenharmony_ci struct sensor_data *sdata_table, int count) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * We don't use the OPAL index on newer device trees 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci if (sdata->opal_index != INVALID_INDEX) { 3268c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 3278c2ecf20Sopenharmony_ci if (sdata_table[i].opal_index == sdata->opal_index && 3288c2ecf20Sopenharmony_ci sdata_table[i].type == sdata->type) 3298c2ecf20Sopenharmony_ci return sdata_table[i].hwmon_index; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci return ++sensor_groups[sdata->type].hwmon_index; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int init_sensor_group_data(struct platform_device *pdev, 3358c2ecf20Sopenharmony_ci struct platform_data *pdata) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data; 3388c2ecf20Sopenharmony_ci struct device_node *groups, *sgrp; 3398c2ecf20Sopenharmony_ci int count = 0, ret = 0; 3408c2ecf20Sopenharmony_ci enum sensors type; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); 3438c2ecf20Sopenharmony_ci if (!groups) 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci for_each_child_of_node(groups, sgrp) { 3478c2ecf20Sopenharmony_ci type = get_sensor_type(sgrp); 3488c2ecf20Sopenharmony_ci if (type != MAX_SENSOR_TYPE) 3498c2ecf20Sopenharmony_ci pdata->nr_sensor_groups++; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!pdata->nr_sensor_groups) 3538c2ecf20Sopenharmony_ci goto out; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci sgrp_data = devm_kcalloc(&pdev->dev, pdata->nr_sensor_groups, 3568c2ecf20Sopenharmony_ci sizeof(*sgrp_data), GFP_KERNEL); 3578c2ecf20Sopenharmony_ci if (!sgrp_data) { 3588c2ecf20Sopenharmony_ci ret = -ENOMEM; 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci for_each_child_of_node(groups, sgrp) { 3638c2ecf20Sopenharmony_ci u32 gid; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci type = get_sensor_type(sgrp); 3668c2ecf20Sopenharmony_ci if (type == MAX_SENSOR_TYPE) 3678c2ecf20Sopenharmony_ci continue; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (of_property_read_u32(sgrp, "sensor-group-id", &gid)) 3708c2ecf20Sopenharmony_ci continue; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (of_count_phandle_with_args(sgrp, "sensors", NULL) <= 0) 3738c2ecf20Sopenharmony_ci continue; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci sensor_groups[type].attr_count++; 3768c2ecf20Sopenharmony_ci sgrp_data[count].gid = gid; 3778c2ecf20Sopenharmony_ci mutex_init(&sgrp_data[count].mutex); 3788c2ecf20Sopenharmony_ci sgrp_data[count++].enable = false; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci pdata->sgrp_data = sgrp_data; 3828c2ecf20Sopenharmony_ciout: 3838c2ecf20Sopenharmony_ci of_node_put(groups); 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic struct sensor_group_data *get_sensor_group(struct platform_data *pdata, 3888c2ecf20Sopenharmony_ci struct device_node *node, 3898c2ecf20Sopenharmony_ci enum sensors gtype) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data = pdata->sgrp_data; 3928c2ecf20Sopenharmony_ci struct device_node *groups, *sgrp; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); 3958c2ecf20Sopenharmony_ci if (!groups) 3968c2ecf20Sopenharmony_ci return NULL; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci for_each_child_of_node(groups, sgrp) { 3998c2ecf20Sopenharmony_ci struct of_phandle_iterator it; 4008c2ecf20Sopenharmony_ci u32 gid; 4018c2ecf20Sopenharmony_ci int rc, i; 4028c2ecf20Sopenharmony_ci enum sensors type; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci type = get_sensor_type(sgrp); 4058c2ecf20Sopenharmony_ci if (type != gtype) 4068c2ecf20Sopenharmony_ci continue; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (of_property_read_u32(sgrp, "sensor-group-id", &gid)) 4098c2ecf20Sopenharmony_ci continue; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci of_for_each_phandle(&it, rc, sgrp, "sensors", NULL, 0) 4128c2ecf20Sopenharmony_ci if (it.phandle == node->phandle) { 4138c2ecf20Sopenharmony_ci of_node_put(it.node); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (rc) 4188c2ecf20Sopenharmony_ci continue; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci for (i = 0; i < pdata->nr_sensor_groups; i++) 4218c2ecf20Sopenharmony_ci if (gid == sgrp_data[i].gid) { 4228c2ecf20Sopenharmony_ci of_node_put(sgrp); 4238c2ecf20Sopenharmony_ci of_node_put(groups); 4248c2ecf20Sopenharmony_ci return &sgrp_data[i]; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci of_node_put(groups); 4298c2ecf20Sopenharmony_ci return NULL; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int populate_attr_groups(struct platform_device *pdev) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct platform_data *pdata = platform_get_drvdata(pdev); 4358c2ecf20Sopenharmony_ci const struct attribute_group **pgroups = pdata->attr_groups; 4368c2ecf20Sopenharmony_ci struct device_node *opal, *np; 4378c2ecf20Sopenharmony_ci enum sensors type; 4388c2ecf20Sopenharmony_ci int ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = init_sensor_group_data(pdev, pdata); 4418c2ecf20Sopenharmony_ci if (ret) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci opal = of_find_node_by_path("/ibm,opal/sensors"); 4458c2ecf20Sopenharmony_ci for_each_child_of_node(opal, np) { 4468c2ecf20Sopenharmony_ci const char *label; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci type = get_sensor_type(np); 4498c2ecf20Sopenharmony_ci if (type == MAX_SENSOR_TYPE) 4508c2ecf20Sopenharmony_ci continue; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci sensor_groups[type].attr_count++; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * add attributes for labels, min and max 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci if (!of_property_read_string(np, "label", &label)) 4588c2ecf20Sopenharmony_ci sensor_groups[type].attr_count++; 4598c2ecf20Sopenharmony_ci if (of_find_property(np, "sensor-data-min", NULL)) 4608c2ecf20Sopenharmony_ci sensor_groups[type].attr_count++; 4618c2ecf20Sopenharmony_ci if (of_find_property(np, "sensor-data-max", NULL)) 4628c2ecf20Sopenharmony_ci sensor_groups[type].attr_count++; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci of_node_put(opal); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci for (type = 0; type < MAX_SENSOR_TYPE; type++) { 4688c2ecf20Sopenharmony_ci sensor_groups[type].group.attrs = devm_kcalloc(&pdev->dev, 4698c2ecf20Sopenharmony_ci sensor_groups[type].attr_count + 1, 4708c2ecf20Sopenharmony_ci sizeof(struct attribute *), 4718c2ecf20Sopenharmony_ci GFP_KERNEL); 4728c2ecf20Sopenharmony_ci if (!sensor_groups[type].group.attrs) 4738c2ecf20Sopenharmony_ci return -ENOMEM; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci pgroups[type] = &sensor_groups[type].group; 4768c2ecf20Sopenharmony_ci pdata->sensors_count += sensor_groups[type].attr_count; 4778c2ecf20Sopenharmony_ci sensor_groups[type].attr_count = 0; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, 4848c2ecf20Sopenharmony_ci ssize_t (*show)(struct device *dev, 4858c2ecf20Sopenharmony_ci struct device_attribute *attr, 4868c2ecf20Sopenharmony_ci char *buf), 4878c2ecf20Sopenharmony_ci ssize_t (*store)(struct device *dev, 4888c2ecf20Sopenharmony_ci struct device_attribute *attr, 4898c2ecf20Sopenharmony_ci const char *buf, size_t count)) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s", 4928c2ecf20Sopenharmony_ci sensor_groups[sdata->type].name, sdata->hwmon_index, 4938c2ecf20Sopenharmony_ci attr_name); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci sysfs_attr_init(&sdata->dev_attr.attr); 4968c2ecf20Sopenharmony_ci sdata->dev_attr.attr.name = sdata->name; 4978c2ecf20Sopenharmony_ci sdata->dev_attr.show = show; 4988c2ecf20Sopenharmony_ci if (store) { 4998c2ecf20Sopenharmony_ci sdata->dev_attr.store = store; 5008c2ecf20Sopenharmony_ci sdata->dev_attr.attr.mode = 0664; 5018c2ecf20Sopenharmony_ci } else { 5028c2ecf20Sopenharmony_ci sdata->dev_attr.attr.mode = 0444; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid, 5078c2ecf20Sopenharmony_ci const char *attr_name, enum sensors type, 5088c2ecf20Sopenharmony_ci const struct attribute_group *pgroup, 5098c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data, 5108c2ecf20Sopenharmony_ci ssize_t (*show)(struct device *dev, 5118c2ecf20Sopenharmony_ci struct device_attribute *attr, 5128c2ecf20Sopenharmony_ci char *buf), 5138c2ecf20Sopenharmony_ci ssize_t (*store)(struct device *dev, 5148c2ecf20Sopenharmony_ci struct device_attribute *attr, 5158c2ecf20Sopenharmony_ci const char *buf, size_t count)) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci sdata->id = sid; 5188c2ecf20Sopenharmony_ci sdata->type = type; 5198c2ecf20Sopenharmony_ci sdata->opal_index = od; 5208c2ecf20Sopenharmony_ci sdata->hwmon_index = hd; 5218c2ecf20Sopenharmony_ci create_hwmon_attr(sdata, attr_name, show, store); 5228c2ecf20Sopenharmony_ci pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr; 5238c2ecf20Sopenharmony_ci sdata->sgrp_data = sgrp_data; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic char *get_max_attr(enum sensors type) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci switch (type) { 5298c2ecf20Sopenharmony_ci case POWER_INPUT: 5308c2ecf20Sopenharmony_ci return "input_highest"; 5318c2ecf20Sopenharmony_ci default: 5328c2ecf20Sopenharmony_ci return "highest"; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic char *get_min_attr(enum sensors type) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci switch (type) { 5398c2ecf20Sopenharmony_ci case POWER_INPUT: 5408c2ecf20Sopenharmony_ci return "input_lowest"; 5418c2ecf20Sopenharmony_ci default: 5428c2ecf20Sopenharmony_ci return "lowest"; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* 5478c2ecf20Sopenharmony_ci * Iterate through the device tree for each child of 'sensors' node, create 5488c2ecf20Sopenharmony_ci * a sysfs attribute file, the file is named by translating the DT node name 5498c2ecf20Sopenharmony_ci * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max 5508c2ecf20Sopenharmony_ci * etc.. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic int create_device_attrs(struct platform_device *pdev) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct platform_data *pdata = platform_get_drvdata(pdev); 5558c2ecf20Sopenharmony_ci const struct attribute_group **pgroups = pdata->attr_groups; 5568c2ecf20Sopenharmony_ci struct device_node *opal, *np; 5578c2ecf20Sopenharmony_ci struct sensor_data *sdata; 5588c2ecf20Sopenharmony_ci u32 count = 0; 5598c2ecf20Sopenharmony_ci u32 group_attr_id[MAX_SENSOR_TYPE] = {0}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci sdata = devm_kcalloc(&pdev->dev, 5628c2ecf20Sopenharmony_ci pdata->sensors_count, sizeof(*sdata), 5638c2ecf20Sopenharmony_ci GFP_KERNEL); 5648c2ecf20Sopenharmony_ci if (!sdata) 5658c2ecf20Sopenharmony_ci return -ENOMEM; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci opal = of_find_node_by_path("/ibm,opal/sensors"); 5688c2ecf20Sopenharmony_ci for_each_child_of_node(opal, np) { 5698c2ecf20Sopenharmony_ci struct sensor_group_data *sgrp_data; 5708c2ecf20Sopenharmony_ci const char *attr_name; 5718c2ecf20Sopenharmony_ci u32 opal_index, hw_id; 5728c2ecf20Sopenharmony_ci u32 sensor_id; 5738c2ecf20Sopenharmony_ci const char *label; 5748c2ecf20Sopenharmony_ci enum sensors type; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci type = get_sensor_type(np); 5778c2ecf20Sopenharmony_ci if (type == MAX_SENSOR_TYPE) 5788c2ecf20Sopenharmony_ci continue; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* 5818c2ecf20Sopenharmony_ci * Newer device trees use a "sensor-data" property 5828c2ecf20Sopenharmony_ci * name for input. 5838c2ecf20Sopenharmony_ci */ 5848c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "sensor-id", &sensor_id) && 5858c2ecf20Sopenharmony_ci of_property_read_u32(np, "sensor-data", &sensor_id)) { 5868c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 5878c2ecf20Sopenharmony_ci "'sensor-id' missing in the node '%pOFn'\n", 5888c2ecf20Sopenharmony_ci np); 5898c2ecf20Sopenharmony_ci continue; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci sdata[count].id = sensor_id; 5938c2ecf20Sopenharmony_ci sdata[count].type = type; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* 5968c2ecf20Sopenharmony_ci * If we can not parse the node name, it means we are 5978c2ecf20Sopenharmony_ci * running on a newer device tree. We can just forget 5988c2ecf20Sopenharmony_ci * about the OPAL index and use a defaut value for the 5998c2ecf20Sopenharmony_ci * hwmon attribute name 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_ci attr_name = parse_opal_node_name(np->name, type, &opal_index); 6028c2ecf20Sopenharmony_ci if (IS_ERR(attr_name)) { 6038c2ecf20Sopenharmony_ci attr_name = "input"; 6048c2ecf20Sopenharmony_ci opal_index = INVALID_INDEX; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci hw_id = get_sensor_hwmon_index(&sdata[count], sdata, count); 6088c2ecf20Sopenharmony_ci sgrp_data = get_sensor_group(pdata, np, type); 6098c2ecf20Sopenharmony_ci populate_sensor(&sdata[count], opal_index, hw_id, sensor_id, 6108c2ecf20Sopenharmony_ci attr_name, type, pgroups[type], sgrp_data, 6118c2ecf20Sopenharmony_ci show_sensor, NULL); 6128c2ecf20Sopenharmony_ci count++; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (!of_property_read_string(np, "label", &label)) { 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * For the label attribute, we can reuse the 6178c2ecf20Sopenharmony_ci * "properties" of the previous "input" 6188c2ecf20Sopenharmony_ci * attribute. They are related to the same 6198c2ecf20Sopenharmony_ci * sensor. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci make_sensor_label(np, &sdata[count], label); 6238c2ecf20Sopenharmony_ci populate_sensor(&sdata[count], opal_index, hw_id, 6248c2ecf20Sopenharmony_ci sensor_id, "label", type, pgroups[type], 6258c2ecf20Sopenharmony_ci NULL, show_label, NULL); 6268c2ecf20Sopenharmony_ci count++; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) { 6308c2ecf20Sopenharmony_ci attr_name = get_max_attr(type); 6318c2ecf20Sopenharmony_ci populate_sensor(&sdata[count], opal_index, hw_id, 6328c2ecf20Sopenharmony_ci sensor_id, attr_name, type, 6338c2ecf20Sopenharmony_ci pgroups[type], sgrp_data, show_sensor, 6348c2ecf20Sopenharmony_ci NULL); 6358c2ecf20Sopenharmony_ci count++; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) { 6398c2ecf20Sopenharmony_ci attr_name = get_min_attr(type); 6408c2ecf20Sopenharmony_ci populate_sensor(&sdata[count], opal_index, hw_id, 6418c2ecf20Sopenharmony_ci sensor_id, attr_name, type, 6428c2ecf20Sopenharmony_ci pgroups[type], sgrp_data, show_sensor, 6438c2ecf20Sopenharmony_ci NULL); 6448c2ecf20Sopenharmony_ci count++; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (sgrp_data && !sgrp_data->enable) { 6488c2ecf20Sopenharmony_ci sgrp_data->enable = true; 6498c2ecf20Sopenharmony_ci hw_id = ++group_attr_id[type]; 6508c2ecf20Sopenharmony_ci populate_sensor(&sdata[count], opal_index, hw_id, 6518c2ecf20Sopenharmony_ci sgrp_data->gid, "enable", type, 6528c2ecf20Sopenharmony_ci pgroups[type], sgrp_data, show_enable, 6538c2ecf20Sopenharmony_ci store_enable); 6548c2ecf20Sopenharmony_ci count++; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci of_node_put(opal); 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int ibmpowernv_probe(struct platform_device *pdev) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct platform_data *pdata; 6658c2ecf20Sopenharmony_ci struct device *hwmon_dev; 6668c2ecf20Sopenharmony_ci int err; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 6698c2ecf20Sopenharmony_ci if (!pdata) 6708c2ecf20Sopenharmony_ci return -ENOMEM; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pdata); 6738c2ecf20Sopenharmony_ci pdata->sensors_count = 0; 6748c2ecf20Sopenharmony_ci pdata->nr_sensor_groups = 0; 6758c2ecf20Sopenharmony_ci err = populate_attr_groups(pdev); 6768c2ecf20Sopenharmony_ci if (err) 6778c2ecf20Sopenharmony_ci return err; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Create sysfs attribute data for each sensor found in the DT */ 6808c2ecf20Sopenharmony_ci err = create_device_attrs(pdev); 6818c2ecf20Sopenharmony_ci if (err) 6828c2ecf20Sopenharmony_ci return err; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Finally, register with hwmon */ 6858c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME, 6868c2ecf20Sopenharmony_ci pdata, 6878c2ecf20Sopenharmony_ci pdata->attr_groups); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic const struct platform_device_id opal_sensor_driver_ids[] = { 6938c2ecf20Sopenharmony_ci { 6948c2ecf20Sopenharmony_ci .name = "opal-sensor", 6958c2ecf20Sopenharmony_ci }, 6968c2ecf20Sopenharmony_ci { } 6978c2ecf20Sopenharmony_ci}; 6988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic const struct of_device_id opal_sensor_match[] = { 7018c2ecf20Sopenharmony_ci { .compatible = "ibm,opal-sensor" }, 7028c2ecf20Sopenharmony_ci { }, 7038c2ecf20Sopenharmony_ci}; 7048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, opal_sensor_match); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic struct platform_driver ibmpowernv_driver = { 7078c2ecf20Sopenharmony_ci .probe = ibmpowernv_probe, 7088c2ecf20Sopenharmony_ci .id_table = opal_sensor_driver_ids, 7098c2ecf20Sopenharmony_ci .driver = { 7108c2ecf20Sopenharmony_ci .name = DRVNAME, 7118c2ecf20Sopenharmony_ci .of_match_table = opal_sensor_match, 7128c2ecf20Sopenharmony_ci }, 7138c2ecf20Sopenharmony_ci}; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cimodule_platform_driver(ibmpowernv_driver); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); 7188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM POWERNV platform sensors"); 7198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 720