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