Merge tag 'scmi-updates-4.17' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into next/drivers

Pull "ARM SCMI support for v4.17" from Sudeep Holla:

ARM System Control and Management Interface(SCMI)[1] is more flexible and
easily extensible than any of the existing interfaces.

Few existing as well as future ARM platforms provide micro-controllers
to abstract various power and other system management tasks which have
similar interfaces, both in terms of the functions that are provided by
them, and in terms of how requests are communicated to them.

There are quite a few protocols like ARM SCPI, TI SCI, QCOM RPM, Nvidia Tegra
BPMP, and so on already. This specification is to standardize and avoid any
further fragmentation in the design of such interface by various vendors.

The current SCMI driver implementation is very basic and initial support.
It lacks support for notifications, asynchronous/delayed response, perf/power
statistics region and sensor register region.

Mailbox is the only form of transport supported currently in the driver.
SCMI supports interrupt based mailbox communication, where, on completion
of the processing of a message, the caller receives an interrupt as well as
polling for completion.

SCMI is designed to minimize the dependency on the mailbox/transport
hardware. So in terms of SCMI, each channel in the mailbox includes
memory area, doorbell and completion interrupt.

However the doorbell and completion interrupt is highly mailbox dependent
which was bit of controversial as part of SCMI/mailbox discussions.

Arnd and me discussed about the few aspects of SCMI and the mailbox framework:

1. Use of mailbox framework for doorbell type mailbox controller:
   - Such hardware may not require any data to be sent to signal the remote
     about the presence of a message. The channel will have in-built
     information on how to trigger the signal to the remote.
     There are few mailbox controller drivers which are purely doorbell based.
     e.g.QCOM IPC, STM, Tegra, ACPI PCC,..etc

2. Supporting other mailbox controller:
   - SCMI just needs a mechanism to signal the remote firmware. Such
     controller may need fixed message to be sent to trigger a doorbell.
     In such case we may need to get that data from DT and pass the same
     to the controller. It's not covered in the current DT binding, but
     can be extended as optional property in future.

     However handling notifications may be interesting on such mailbox, but
     again there is no way to interpret what the data field(remote message)
     means, it could be a bit mask or a number or don't-care.

Arnd mentioned that he doesn't like the way the mailbox binding deals
with doorbell-type hardware, but we do have quite a few precedent drivers
already and changing the binding to add a data field would not make it any
better, but could cause other problems. So he is happy with the status quo
of SCMI implementation.

[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0056a/index.html

* tag 'scmi-updates-4.17' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  cpufreq: scmi: add support for fast frequency switching
  cpufreq: add support for CPU DVFS based on SCMI message protocol
  hwmon: add support for sensors exported via ARM SCMI
  hwmon: (core) Add hwmon_max to hwmon_sensor_types enumeration
  clk: add support for clocks provided by SCMI
  firmware: arm_scmi: add device power domain support using genpd
  firmware: arm_scmi: add per-protocol channels support using idr objects
  firmware: arm_scmi: refactor in preparation to support per-protocol channels
  firmware: arm_scmi: add option for polling based performance domain operations
  firmware: arm_scmi: add support for polling based SCMI transfers
  firmware: arm_scmi: probe and initialise all the supported protocols
  firmware: arm_scmi: add initial support for sensor protocol
  firmware: arm_scmi: add initial support for power protocol
  firmware: arm_scmi: add initial support for clock protocol
  firmware: arm_scmi: add initial support for performance protocol
  firmware: arm_scmi: add scmi protocol bus to enumerate protocol devices
  firmware: arm_scmi: add common infrastructure and support for base protocol
  firmware: arm_scmi: add basic driver infrastructure for SCMI
  dt-bindings: arm: add support for ARM System Control and Management Interface(SCMI) protocol
  dt-bindings: mailbox: add support for mailbox client shared memory
This commit is contained in:
Arnd Bergmann
2018-03-07 16:45:07 +01:00
26 changed files with 4172 additions and 5 deletions

View File

@@ -317,6 +317,18 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
config SENSORS_ARM_SCMI
tristate "ARM SCMI Sensors"
depends on ARM_SCMI_PROTOCOL
depends on THERMAL || !THERMAL_OF
help
This driver provides support for temperature, voltage, current
and power sensors available on SCMI based platforms. The actual
number and type of sensors exported depend on the platform.
This driver can also be built as a module. If so, the module
will be called scmi-hwmon.
config SENSORS_ARM_SCPI
tristate "ARM SCPI Sensors"
depends on ARM_SCPI_PROTOCOL

View File

@@ -46,6 +46,7 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o

225
drivers/hwmon/scmi-hwmon.c Normal file
View File

@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface(SCMI) based hwmon sensor driver
*
* Copyright (C) 2018 ARM Ltd.
* Sudeep Holla <sudeep.holla@arm.com>
*/
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
struct scmi_sensors {
const struct scmi_handle *handle;
const struct scmi_sensor_info **info[hwmon_max];
};
static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
int ret;
u64 value;
const struct scmi_sensor_info *sensor;
struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
const struct scmi_handle *h = scmi_sensors->handle;
sensor = *(scmi_sensors->info[type] + channel);
ret = h->sensor_ops->reading_get(h, sensor->id, false, &value);
if (!ret)
*val = value;
return ret;
}
static int
scmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
const struct scmi_sensor_info *sensor;
struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
sensor = *(scmi_sensors->info[type] + channel);
*str = sensor->name;
return 0;
}
static umode_t
scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct scmi_sensor_info *sensor;
const struct scmi_sensors *scmi_sensors = drvdata;
sensor = *(scmi_sensors->info[type] + channel);
if (sensor && sensor->name)
return S_IRUGO;
return 0;
}
static const struct hwmon_ops scmi_hwmon_ops = {
.is_visible = scmi_hwmon_is_visible,
.read = scmi_hwmon_read,
.read_string = scmi_hwmon_read_string,
};
static struct hwmon_chip_info scmi_chip_info = {
.ops = &scmi_hwmon_ops,
.info = NULL,
};
static int scmi_hwmon_add_chan_info(struct hwmon_channel_info *scmi_hwmon_chan,
struct device *dev, int num,
enum hwmon_sensor_types type, u32 config)
{
int i;
u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
scmi_hwmon_chan->type = type;
scmi_hwmon_chan->config = cfg;
for (i = 0; i < num; i++, cfg++)
*cfg = config;
return 0;
}
static enum hwmon_sensor_types scmi_types[] = {
[TEMPERATURE_C] = hwmon_temp,
[VOLTAGE] = hwmon_in,
[CURRENT] = hwmon_curr,
[POWER] = hwmon_power,
[ENERGY] = hwmon_energy,
};
static u32 hwmon_attributes[] = {
[hwmon_chip] = HWMON_C_REGISTER_TZ,
[hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
[hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
[hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
[hwmon_power] = HWMON_P_INPUT | HWMON_P_LABEL,
[hwmon_energy] = HWMON_E_INPUT | HWMON_E_LABEL,
};
static int scmi_hwmon_probe(struct scmi_device *sdev)
{
int i, idx;
u16 nr_sensors;
enum hwmon_sensor_types type;
struct scmi_sensors *scmi_sensors;
const struct scmi_sensor_info *sensor;
int nr_count[hwmon_max] = {0}, nr_types = 0;
const struct hwmon_chip_info *chip_info;
struct device *hwdev, *dev = &sdev->dev;
struct hwmon_channel_info *scmi_hwmon_chan;
const struct hwmon_channel_info **ptr_scmi_ci;
const struct scmi_handle *handle = sdev->handle;
if (!handle || !handle->sensor_ops)
return -ENODEV;
nr_sensors = handle->sensor_ops->count_get(handle);
if (!nr_sensors)
return -EIO;
scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL);
if (!scmi_sensors)
return -ENOMEM;
scmi_sensors->handle = handle;
for (i = 0; i < nr_sensors; i++) {
sensor = handle->sensor_ops->info_get(handle, i);
if (!sensor)
return PTR_ERR(sensor);
switch (sensor->type) {
case TEMPERATURE_C:
case VOLTAGE:
case CURRENT:
case POWER:
case ENERGY:
type = scmi_types[sensor->type];
if (!nr_count[type])
nr_types++;
nr_count[type]++;
break;
}
}
if (nr_count[hwmon_temp])
nr_count[hwmon_chip]++, nr_types++;
scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan),
GFP_KERNEL);
if (!scmi_hwmon_chan)
return -ENOMEM;
ptr_scmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_scmi_ci),
GFP_KERNEL);
if (!ptr_scmi_ci)
return -ENOMEM;
scmi_chip_info.info = ptr_scmi_ci;
chip_info = &scmi_chip_info;
for (type = 0; type < hwmon_max && nr_count[type]; type++) {
scmi_hwmon_add_chan_info(scmi_hwmon_chan, dev, nr_count[type],
type, hwmon_attributes[type]);
*ptr_scmi_ci++ = scmi_hwmon_chan++;
scmi_sensors->info[type] =
devm_kcalloc(dev, nr_count[type],
sizeof(*scmi_sensors->info), GFP_KERNEL);
if (!scmi_sensors->info[type])
return -ENOMEM;
}
for (i = nr_sensors - 1; i >= 0 ; i--) {
sensor = handle->sensor_ops->info_get(handle, i);
if (!sensor)
continue;
switch (sensor->type) {
case TEMPERATURE_C:
case VOLTAGE:
case CURRENT:
case POWER:
case ENERGY:
type = scmi_types[sensor->type];
idx = --nr_count[type];
*(scmi_sensors->info[type] + idx) = sensor;
break;
}
}
hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors",
scmi_sensors, chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwdev);
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_SENSOR },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_hwmon_drv = {
.name = "scmi-hwmon",
.probe = scmi_hwmon_probe,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_hwmon_drv);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
MODULE_LICENSE("GPL v2");