cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

scpi-hwmon.c (8206B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * System Control and Power Interface(SCPI) based hwmon sensor driver
      4 *
      5 * Copyright (C) 2015 ARM Ltd.
      6 * Punit Agrawal <punit.agrawal@arm.com>
      7 */
      8
      9#include <linux/hwmon.h>
     10#include <linux/module.h>
     11#include <linux/of_device.h>
     12#include <linux/platform_device.h>
     13#include <linux/scpi_protocol.h>
     14#include <linux/slab.h>
     15#include <linux/sysfs.h>
     16#include <linux/thermal.h>
     17
     18struct sensor_data {
     19	unsigned int scale;
     20	struct scpi_sensor_info info;
     21	struct device_attribute dev_attr_input;
     22	struct device_attribute dev_attr_label;
     23	char input[20];
     24	char label[20];
     25};
     26
     27struct scpi_thermal_zone {
     28	int sensor_id;
     29	struct scpi_sensors *scpi_sensors;
     30};
     31
     32struct scpi_sensors {
     33	struct scpi_ops *scpi_ops;
     34	struct sensor_data *data;
     35	struct list_head thermal_zones;
     36	struct attribute **attrs;
     37	struct attribute_group group;
     38	const struct attribute_group *groups[2];
     39};
     40
     41static const u32 gxbb_scpi_scale[] = {
     42	[TEMPERATURE]	= 1,		/* (celsius)		*/
     43	[VOLTAGE]	= 1000,		/* (millivolts)		*/
     44	[CURRENT]	= 1000,		/* (milliamperes)	*/
     45	[POWER]		= 1000000,	/* (microwatts)		*/
     46	[ENERGY]	= 1000000,	/* (microjoules)	*/
     47};
     48
     49static const u32 scpi_scale[] = {
     50	[TEMPERATURE]	= 1000,		/* (millicelsius)	*/
     51	[VOLTAGE]	= 1000,		/* (millivolts)		*/
     52	[CURRENT]	= 1000,		/* (milliamperes)	*/
     53	[POWER]		= 1000000,	/* (microwatts)		*/
     54	[ENERGY]	= 1000000,	/* (microjoules)	*/
     55};
     56
     57static void scpi_scale_reading(u64 *value, struct sensor_data *sensor)
     58{
     59	if (scpi_scale[sensor->info.class] != sensor->scale) {
     60		*value *= scpi_scale[sensor->info.class];
     61		do_div(*value, sensor->scale);
     62	}
     63}
     64
     65static int scpi_read_temp(void *dev, int *temp)
     66{
     67	struct scpi_thermal_zone *zone = dev;
     68	struct scpi_sensors *scpi_sensors = zone->scpi_sensors;
     69	struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
     70	struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id];
     71	u64 value;
     72	int ret;
     73
     74	ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
     75	if (ret)
     76		return ret;
     77
     78	scpi_scale_reading(&value, sensor);
     79
     80	*temp = value;
     81	return 0;
     82}
     83
     84/* hwmon callback functions */
     85static ssize_t
     86scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
     87{
     88	struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev);
     89	struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
     90	struct sensor_data *sensor;
     91	u64 value;
     92	int ret;
     93
     94	sensor = container_of(attr, struct sensor_data, dev_attr_input);
     95
     96	ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
     97	if (ret)
     98		return ret;
     99
    100	scpi_scale_reading(&value, sensor);
    101
    102	/*
    103	 * Temperature sensor values are treated as signed values based on
    104	 * observation even though that is not explicitly specified, and
    105	 * because an unsigned u64 temperature does not really make practical
    106	 * sense especially when the temperature is below zero degrees Celsius.
    107	 */
    108	if (sensor->info.class == TEMPERATURE)
    109		return sprintf(buf, "%lld\n", (s64)value);
    110
    111	return sprintf(buf, "%llu\n", value);
    112}
    113
    114static ssize_t
    115scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
    116{
    117	struct sensor_data *sensor;
    118
    119	sensor = container_of(attr, struct sensor_data, dev_attr_label);
    120
    121	return sprintf(buf, "%s\n", sensor->info.name);
    122}
    123
    124static const struct thermal_zone_of_device_ops scpi_sensor_ops = {
    125	.get_temp = scpi_read_temp,
    126};
    127
    128static const struct of_device_id scpi_of_match[] = {
    129	{.compatible = "arm,scpi-sensors", .data = &scpi_scale},
    130	{.compatible = "amlogic,meson-gxbb-scpi-sensors", .data = &gxbb_scpi_scale},
    131	{},
    132};
    133MODULE_DEVICE_TABLE(of, scpi_of_match);
    134
    135static int scpi_hwmon_probe(struct platform_device *pdev)
    136{
    137	u16 nr_sensors, i;
    138	const u32 *scale;
    139	int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
    140	int num_energy = 0;
    141	struct scpi_ops *scpi_ops;
    142	struct device *hwdev, *dev = &pdev->dev;
    143	struct scpi_sensors *scpi_sensors;
    144	int idx, ret;
    145
    146	scpi_ops = get_scpi_ops();
    147	if (!scpi_ops)
    148		return -EPROBE_DEFER;
    149
    150	ret = scpi_ops->sensor_get_capability(&nr_sensors);
    151	if (ret)
    152		return ret;
    153
    154	if (!nr_sensors)
    155		return -ENODEV;
    156
    157	scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL);
    158	if (!scpi_sensors)
    159		return -ENOMEM;
    160
    161	scpi_sensors->data = devm_kcalloc(dev, nr_sensors,
    162				   sizeof(*scpi_sensors->data), GFP_KERNEL);
    163	if (!scpi_sensors->data)
    164		return -ENOMEM;
    165
    166	scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1,
    167				   sizeof(*scpi_sensors->attrs), GFP_KERNEL);
    168	if (!scpi_sensors->attrs)
    169		return -ENOMEM;
    170
    171	scpi_sensors->scpi_ops = scpi_ops;
    172
    173	scale = of_device_get_match_data(&pdev->dev);
    174	if (!scale) {
    175		dev_err(&pdev->dev, "Unable to initialize scpi-hwmon data\n");
    176		return -ENODEV;
    177	}
    178
    179	for (i = 0, idx = 0; i < nr_sensors; i++) {
    180		struct sensor_data *sensor = &scpi_sensors->data[idx];
    181
    182		ret = scpi_ops->sensor_get_info(i, &sensor->info);
    183		if (ret)
    184			return ret;
    185
    186		switch (sensor->info.class) {
    187		case TEMPERATURE:
    188			snprintf(sensor->input, sizeof(sensor->input),
    189				 "temp%d_input", num_temp + 1);
    190			snprintf(sensor->label, sizeof(sensor->input),
    191				 "temp%d_label", num_temp + 1);
    192			num_temp++;
    193			break;
    194		case VOLTAGE:
    195			snprintf(sensor->input, sizeof(sensor->input),
    196				 "in%d_input", num_volt);
    197			snprintf(sensor->label, sizeof(sensor->input),
    198				 "in%d_label", num_volt);
    199			num_volt++;
    200			break;
    201		case CURRENT:
    202			snprintf(sensor->input, sizeof(sensor->input),
    203				 "curr%d_input", num_current + 1);
    204			snprintf(sensor->label, sizeof(sensor->input),
    205				 "curr%d_label", num_current + 1);
    206			num_current++;
    207			break;
    208		case POWER:
    209			snprintf(sensor->input, sizeof(sensor->input),
    210				 "power%d_input", num_power + 1);
    211			snprintf(sensor->label, sizeof(sensor->input),
    212				 "power%d_label", num_power + 1);
    213			num_power++;
    214			break;
    215		case ENERGY:
    216			snprintf(sensor->input, sizeof(sensor->input),
    217				 "energy%d_input", num_energy + 1);
    218			snprintf(sensor->label, sizeof(sensor->input),
    219				 "energy%d_label", num_energy + 1);
    220			num_energy++;
    221			break;
    222		default:
    223			continue;
    224		}
    225
    226		sensor->scale = scale[sensor->info.class];
    227
    228		sensor->dev_attr_input.attr.mode = 0444;
    229		sensor->dev_attr_input.show = scpi_show_sensor;
    230		sensor->dev_attr_input.attr.name = sensor->input;
    231
    232		sensor->dev_attr_label.attr.mode = 0444;
    233		sensor->dev_attr_label.show = scpi_show_label;
    234		sensor->dev_attr_label.attr.name = sensor->label;
    235
    236		scpi_sensors->attrs[idx << 1] = &sensor->dev_attr_input.attr;
    237		scpi_sensors->attrs[(idx << 1) + 1] = &sensor->dev_attr_label.attr;
    238
    239		sysfs_attr_init(scpi_sensors->attrs[idx << 1]);
    240		sysfs_attr_init(scpi_sensors->attrs[(idx << 1) + 1]);
    241		idx++;
    242	}
    243
    244	scpi_sensors->group.attrs = scpi_sensors->attrs;
    245	scpi_sensors->groups[0] = &scpi_sensors->group;
    246
    247	platform_set_drvdata(pdev, scpi_sensors);
    248
    249	hwdev = devm_hwmon_device_register_with_groups(dev,
    250			"scpi_sensors", scpi_sensors, scpi_sensors->groups);
    251
    252	if (IS_ERR(hwdev))
    253		return PTR_ERR(hwdev);
    254
    255	/*
    256	 * Register the temperature sensors with the thermal framework
    257	 * to allow their usage in setting up the thermal zones from
    258	 * device tree.
    259	 *
    260	 * NOTE: Not all temperature sensors maybe used for thermal
    261	 * control
    262	 */
    263	INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
    264	for (i = 0; i < nr_sensors; i++) {
    265		struct sensor_data *sensor = &scpi_sensors->data[i];
    266		struct thermal_zone_device *z;
    267		struct scpi_thermal_zone *zone;
    268
    269		if (sensor->info.class != TEMPERATURE)
    270			continue;
    271
    272		zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
    273		if (!zone)
    274			return -ENOMEM;
    275
    276		zone->sensor_id = i;
    277		zone->scpi_sensors = scpi_sensors;
    278		z = devm_thermal_zone_of_sensor_register(dev,
    279							 sensor->info.sensor_id,
    280							 zone,
    281							 &scpi_sensor_ops);
    282		/*
    283		 * The call to thermal_zone_of_sensor_register returns
    284		 * an error for sensors that are not associated with
    285		 * any thermal zones or if the thermal subsystem is
    286		 * not configured.
    287		 */
    288		if (IS_ERR(z))
    289			devm_kfree(dev, zone);
    290	}
    291
    292	return 0;
    293}
    294
    295static struct platform_driver scpi_hwmon_platdrv = {
    296	.driver = {
    297		.name	= "scpi-hwmon",
    298		.of_match_table = scpi_of_match,
    299	},
    300	.probe		= scpi_hwmon_probe,
    301};
    302module_platform_driver(scpi_hwmon_platdrv);
    303
    304MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>");
    305MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
    306MODULE_LICENSE("GPL v2");