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

scmi-hwmon.c (6297B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * System Control and Management Interface(SCMI) based hwmon sensor driver
      4 *
      5 * Copyright (C) 2018-2021 ARM Ltd.
      6 * Sudeep Holla <sudeep.holla@arm.com>
      7 */
      8
      9#include <linux/hwmon.h>
     10#include <linux/module.h>
     11#include <linux/scmi_protocol.h>
     12#include <linux/slab.h>
     13#include <linux/sysfs.h>
     14#include <linux/thermal.h>
     15
     16static const struct scmi_sensor_proto_ops *sensor_ops;
     17
     18struct scmi_sensors {
     19	const struct scmi_protocol_handle *ph;
     20	const struct scmi_sensor_info **info[hwmon_max];
     21};
     22
     23static inline u64 __pow10(u8 x)
     24{
     25	u64 r = 1;
     26
     27	while (x--)
     28		r *= 10;
     29
     30	return r;
     31}
     32
     33static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value)
     34{
     35	int scale = sensor->scale;
     36	u64 f;
     37
     38	switch (sensor->type) {
     39	case TEMPERATURE_C:
     40	case VOLTAGE:
     41	case CURRENT:
     42		scale += 3;
     43		break;
     44	case POWER:
     45	case ENERGY:
     46		scale += 6;
     47		break;
     48	default:
     49		break;
     50	}
     51
     52	if (scale == 0)
     53		return 0;
     54
     55	if (abs(scale) > 19)
     56		return -E2BIG;
     57
     58	f = __pow10(abs(scale));
     59	if (scale > 0)
     60		*value *= f;
     61	else
     62		*value = div64_u64(*value, f);
     63
     64	return 0;
     65}
     66
     67static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
     68			   u32 attr, int channel, long *val)
     69{
     70	int ret;
     71	u64 value;
     72	const struct scmi_sensor_info *sensor;
     73	struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
     74
     75	sensor = *(scmi_sensors->info[type] + channel);
     76	ret = sensor_ops->reading_get(scmi_sensors->ph, sensor->id, &value);
     77	if (ret)
     78		return ret;
     79
     80	ret = scmi_hwmon_scale(sensor, &value);
     81	if (!ret)
     82		*val = value;
     83
     84	return ret;
     85}
     86
     87static int
     88scmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
     89		       u32 attr, int channel, const char **str)
     90{
     91	const struct scmi_sensor_info *sensor;
     92	struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
     93
     94	sensor = *(scmi_sensors->info[type] + channel);
     95	*str = sensor->name;
     96
     97	return 0;
     98}
     99
    100static umode_t
    101scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
    102		      u32 attr, int channel)
    103{
    104	const struct scmi_sensor_info *sensor;
    105	const struct scmi_sensors *scmi_sensors = drvdata;
    106
    107	sensor = *(scmi_sensors->info[type] + channel);
    108	if (sensor)
    109		return 0444;
    110
    111	return 0;
    112}
    113
    114static const struct hwmon_ops scmi_hwmon_ops = {
    115	.is_visible = scmi_hwmon_is_visible,
    116	.read = scmi_hwmon_read,
    117	.read_string = scmi_hwmon_read_string,
    118};
    119
    120static struct hwmon_chip_info scmi_chip_info = {
    121	.ops = &scmi_hwmon_ops,
    122	.info = NULL,
    123};
    124
    125static int scmi_hwmon_add_chan_info(struct hwmon_channel_info *scmi_hwmon_chan,
    126				    struct device *dev, int num,
    127				    enum hwmon_sensor_types type, u32 config)
    128{
    129	int i;
    130	u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
    131
    132	if (!cfg)
    133		return -ENOMEM;
    134
    135	scmi_hwmon_chan->type = type;
    136	scmi_hwmon_chan->config = cfg;
    137	for (i = 0; i < num; i++, cfg++)
    138		*cfg = config;
    139
    140	return 0;
    141}
    142
    143static enum hwmon_sensor_types scmi_types[] = {
    144	[TEMPERATURE_C] = hwmon_temp,
    145	[VOLTAGE] = hwmon_in,
    146	[CURRENT] = hwmon_curr,
    147	[POWER] = hwmon_power,
    148	[ENERGY] = hwmon_energy,
    149};
    150
    151static u32 hwmon_attributes[hwmon_max] = {
    152	[hwmon_chip] = HWMON_C_REGISTER_TZ,
    153	[hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
    154	[hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
    155	[hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
    156	[hwmon_power] = HWMON_P_INPUT | HWMON_P_LABEL,
    157	[hwmon_energy] = HWMON_E_INPUT | HWMON_E_LABEL,
    158};
    159
    160static int scmi_hwmon_probe(struct scmi_device *sdev)
    161{
    162	int i, idx;
    163	u16 nr_sensors;
    164	enum hwmon_sensor_types type;
    165	struct scmi_sensors *scmi_sensors;
    166	const struct scmi_sensor_info *sensor;
    167	int nr_count[hwmon_max] = {0}, nr_types = 0;
    168	const struct hwmon_chip_info *chip_info;
    169	struct device *hwdev, *dev = &sdev->dev;
    170	struct hwmon_channel_info *scmi_hwmon_chan;
    171	const struct hwmon_channel_info **ptr_scmi_ci;
    172	const struct scmi_handle *handle = sdev->handle;
    173	struct scmi_protocol_handle *ph;
    174
    175	if (!handle)
    176		return -ENODEV;
    177
    178	sensor_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_SENSOR, &ph);
    179	if (IS_ERR(sensor_ops))
    180		return PTR_ERR(sensor_ops);
    181
    182	nr_sensors = sensor_ops->count_get(ph);
    183	if (!nr_sensors)
    184		return -EIO;
    185
    186	scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL);
    187	if (!scmi_sensors)
    188		return -ENOMEM;
    189
    190	scmi_sensors->ph = ph;
    191
    192	for (i = 0; i < nr_sensors; i++) {
    193		sensor = sensor_ops->info_get(ph, i);
    194		if (!sensor)
    195			return -EINVAL;
    196
    197		switch (sensor->type) {
    198		case TEMPERATURE_C:
    199		case VOLTAGE:
    200		case CURRENT:
    201		case POWER:
    202		case ENERGY:
    203			type = scmi_types[sensor->type];
    204			if (!nr_count[type])
    205				nr_types++;
    206			nr_count[type]++;
    207			break;
    208		}
    209	}
    210
    211	if (nr_count[hwmon_temp]) {
    212		nr_count[hwmon_chip]++;
    213		nr_types++;
    214	}
    215
    216	scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan),
    217				       GFP_KERNEL);
    218	if (!scmi_hwmon_chan)
    219		return -ENOMEM;
    220
    221	ptr_scmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_scmi_ci),
    222				   GFP_KERNEL);
    223	if (!ptr_scmi_ci)
    224		return -ENOMEM;
    225
    226	scmi_chip_info.info = ptr_scmi_ci;
    227	chip_info = &scmi_chip_info;
    228
    229	for (type = 0; type < hwmon_max; type++) {
    230		if (!nr_count[type])
    231			continue;
    232
    233		scmi_hwmon_add_chan_info(scmi_hwmon_chan, dev, nr_count[type],
    234					 type, hwmon_attributes[type]);
    235		*ptr_scmi_ci++ = scmi_hwmon_chan++;
    236
    237		scmi_sensors->info[type] =
    238			devm_kcalloc(dev, nr_count[type],
    239				     sizeof(*scmi_sensors->info), GFP_KERNEL);
    240		if (!scmi_sensors->info[type])
    241			return -ENOMEM;
    242	}
    243
    244	for (i = nr_sensors - 1; i >= 0 ; i--) {
    245		sensor = sensor_ops->info_get(ph, i);
    246		if (!sensor)
    247			continue;
    248
    249		switch (sensor->type) {
    250		case TEMPERATURE_C:
    251		case VOLTAGE:
    252		case CURRENT:
    253		case POWER:
    254		case ENERGY:
    255			type = scmi_types[sensor->type];
    256			idx = --nr_count[type];
    257			*(scmi_sensors->info[type] + idx) = sensor;
    258			break;
    259		}
    260	}
    261
    262	hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors",
    263						     scmi_sensors, chip_info,
    264						     NULL);
    265
    266	return PTR_ERR_OR_ZERO(hwdev);
    267}
    268
    269static const struct scmi_device_id scmi_id_table[] = {
    270	{ SCMI_PROTOCOL_SENSOR, "hwmon" },
    271	{ },
    272};
    273MODULE_DEVICE_TABLE(scmi, scmi_id_table);
    274
    275static struct scmi_driver scmi_hwmon_drv = {
    276	.name		= "scmi-hwmon",
    277	.probe		= scmi_hwmon_probe,
    278	.id_table	= scmi_id_table,
    279};
    280module_scmi_driver(scmi_hwmon_drv);
    281
    282MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
    283MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
    284MODULE_LICENSE("GPL v2");