1 // SPDX-License-Identifier: GPL-2.0
3 * System Control and Management Interface(SCMI) based hwmon sensor driver
5 * Copyright (C) 2018 ARM Ltd.
6 * Sudeep Holla <sudeep.holla@arm.com>
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>
17 const struct scmi_handle
*handle
;
18 const struct scmi_sensor_info
**info
[hwmon_max
];
21 static inline u64
__pow10(u8 x
)
31 static int scmi_hwmon_scale(const struct scmi_sensor_info
*sensor
, u64
*value
)
33 int scale
= sensor
->scale
;
36 switch (sensor
->type
) {
56 f
= __pow10(abs(scale
));
60 *value
= div64_u64(*value
, f
);
65 static int scmi_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
66 u32 attr
, int channel
, long *val
)
70 const struct scmi_sensor_info
*sensor
;
71 struct scmi_sensors
*scmi_sensors
= dev_get_drvdata(dev
);
72 const struct scmi_handle
*h
= scmi_sensors
->handle
;
74 sensor
= *(scmi_sensors
->info
[type
] + channel
);
75 ret
= h
->sensor_ops
->reading_get(h
, sensor
->id
, &value
);
79 ret
= scmi_hwmon_scale(sensor
, &value
);
87 scmi_hwmon_read_string(struct device
*dev
, enum hwmon_sensor_types type
,
88 u32 attr
, int channel
, const char **str
)
90 const struct scmi_sensor_info
*sensor
;
91 struct scmi_sensors
*scmi_sensors
= dev_get_drvdata(dev
);
93 sensor
= *(scmi_sensors
->info
[type
] + channel
);
100 scmi_hwmon_is_visible(const void *drvdata
, enum hwmon_sensor_types type
,
101 u32 attr
, int channel
)
103 const struct scmi_sensor_info
*sensor
;
104 const struct scmi_sensors
*scmi_sensors
= drvdata
;
106 sensor
= *(scmi_sensors
->info
[type
] + channel
);
113 static const struct hwmon_ops scmi_hwmon_ops
= {
114 .is_visible
= scmi_hwmon_is_visible
,
115 .read
= scmi_hwmon_read
,
116 .read_string
= scmi_hwmon_read_string
,
119 static struct hwmon_chip_info scmi_chip_info
= {
120 .ops
= &scmi_hwmon_ops
,
124 static int scmi_hwmon_add_chan_info(struct hwmon_channel_info
*scmi_hwmon_chan
,
125 struct device
*dev
, int num
,
126 enum hwmon_sensor_types type
, u32 config
)
129 u32
*cfg
= devm_kcalloc(dev
, num
+ 1, sizeof(*cfg
), GFP_KERNEL
);
134 scmi_hwmon_chan
->type
= type
;
135 scmi_hwmon_chan
->config
= cfg
;
136 for (i
= 0; i
< num
; i
++, cfg
++)
142 static enum hwmon_sensor_types scmi_types
[] = {
143 [TEMPERATURE_C
] = hwmon_temp
,
144 [VOLTAGE
] = hwmon_in
,
145 [CURRENT
] = hwmon_curr
,
146 [POWER
] = hwmon_power
,
147 [ENERGY
] = hwmon_energy
,
150 static u32 hwmon_attributes
[hwmon_max
] = {
151 [hwmon_chip
] = HWMON_C_REGISTER_TZ
,
152 [hwmon_temp
] = HWMON_T_INPUT
| HWMON_T_LABEL
,
153 [hwmon_in
] = HWMON_I_INPUT
| HWMON_I_LABEL
,
154 [hwmon_curr
] = HWMON_C_INPUT
| HWMON_C_LABEL
,
155 [hwmon_power
] = HWMON_P_INPUT
| HWMON_P_LABEL
,
156 [hwmon_energy
] = HWMON_E_INPUT
| HWMON_E_LABEL
,
159 static int scmi_hwmon_probe(struct scmi_device
*sdev
)
163 enum hwmon_sensor_types type
;
164 struct scmi_sensors
*scmi_sensors
;
165 const struct scmi_sensor_info
*sensor
;
166 int nr_count
[hwmon_max
] = {0}, nr_types
= 0;
167 const struct hwmon_chip_info
*chip_info
;
168 struct device
*hwdev
, *dev
= &sdev
->dev
;
169 struct hwmon_channel_info
*scmi_hwmon_chan
;
170 const struct hwmon_channel_info
**ptr_scmi_ci
;
171 const struct scmi_handle
*handle
= sdev
->handle
;
173 if (!handle
|| !handle
->sensor_ops
)
176 nr_sensors
= handle
->sensor_ops
->count_get(handle
);
180 scmi_sensors
= devm_kzalloc(dev
, sizeof(*scmi_sensors
), GFP_KERNEL
);
184 scmi_sensors
->handle
= handle
;
186 for (i
= 0; i
< nr_sensors
; i
++) {
187 sensor
= handle
->sensor_ops
->info_get(handle
, i
);
191 switch (sensor
->type
) {
197 type
= scmi_types
[sensor
->type
];
205 if (nr_count
[hwmon_temp
]) {
206 nr_count
[hwmon_chip
]++;
210 scmi_hwmon_chan
= devm_kcalloc(dev
, nr_types
, sizeof(*scmi_hwmon_chan
),
212 if (!scmi_hwmon_chan
)
215 ptr_scmi_ci
= devm_kcalloc(dev
, nr_types
+ 1, sizeof(*ptr_scmi_ci
),
220 scmi_chip_info
.info
= ptr_scmi_ci
;
221 chip_info
= &scmi_chip_info
;
223 for (type
= 0; type
< hwmon_max
; type
++) {
227 scmi_hwmon_add_chan_info(scmi_hwmon_chan
, dev
, nr_count
[type
],
228 type
, hwmon_attributes
[type
]);
229 *ptr_scmi_ci
++ = scmi_hwmon_chan
++;
231 scmi_sensors
->info
[type
] =
232 devm_kcalloc(dev
, nr_count
[type
],
233 sizeof(*scmi_sensors
->info
), GFP_KERNEL
);
234 if (!scmi_sensors
->info
[type
])
238 for (i
= nr_sensors
- 1; i
>= 0 ; i
--) {
239 sensor
= handle
->sensor_ops
->info_get(handle
, i
);
243 switch (sensor
->type
) {
249 type
= scmi_types
[sensor
->type
];
250 idx
= --nr_count
[type
];
251 *(scmi_sensors
->info
[type
] + idx
) = sensor
;
256 hwdev
= devm_hwmon_device_register_with_info(dev
, "scmi_sensors",
257 scmi_sensors
, chip_info
,
260 return PTR_ERR_OR_ZERO(hwdev
);
263 static const struct scmi_device_id scmi_id_table
[] = {
264 { SCMI_PROTOCOL_SENSOR
, "hwmon" },
267 MODULE_DEVICE_TABLE(scmi
, scmi_id_table
);
269 static struct scmi_driver scmi_hwmon_drv
= {
270 .name
= "scmi-hwmon",
271 .probe
= scmi_hwmon_probe
,
272 .id_table
= scmi_id_table
,
274 module_scmi_driver(scmi_hwmon_drv
);
276 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
277 MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
278 MODULE_LICENSE("GPL v2");