From: Guenter Roeck <linux@roeck-us.net>
To: Matt Turner <mattst88@gmail.com>,
Richard Henderson <richard.henderson@linaro.org>,
Magnus Lindholm <linmag7@gmail.com>
Cc: linux-alpha@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org
Subject: Re: [PATCH 5/7] hwmon: Add driver for Marvel AlphaServer environmental sensors
Date: Sat, 14 Nov 2026 15:00:05 -0800 [thread overview]
Message-ID: <e8ffe364-5c98-4987-b6bd-c263122aeae8@roeck-us.net> (raw)
In-Reply-To: <20261114-marvel-env-v1-5-890328e4b7e5@gmail.com>
On 11/14/26 09:39, Matt Turner wrote:
> The management firmware of the Marvel (EV7) AlphaServers, the GS1280,
> ES80 and ES47, monitors temperatures, fans, voltages and power supplies.
> The SRM console describes each sensor as a packet in a power and
> environment node of its config tree (GCT), and fills in the readings
> when asked to refresh the node. The packet layout was taken from Tru64
> UNIX.
>
> Export the sensors through hwmon. Temperatures come with their warning
> and fault thresholds and fans with their warning threshold. Voltages
> export a reading and an alarm, power supplies only an alarm.
>
> A refresh keeps the boot CPU in the console with interrupts disabled for
> up to 300 ms per node, so refresh a node at most once per
> update_interval.
>
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
> Documentation/hwmon/index.rst | 1 +
> Documentation/hwmon/marvel-env.rst | 60 ++++
> MAINTAINERS | 8 +
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/marvel-env.c | 552 +++++++++++++++++++++++++++++++++++++
> 6 files changed, 633 insertions(+)
>
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index 199ca79621e9..b062ad1ff53f 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -157,6 +157,7 @@ Hardware Monitoring Kernel Drivers
> ltc4286
> lx1308
> macsmc-hwmon
> + marvel-env
> max127
> max15301
> max16064
> diff --git a/Documentation/hwmon/marvel-env.rst b/Documentation/hwmon/marvel-env.rst
> new file mode 100644
> index 000000000000..db36127c3805
> --- /dev/null
> +++ b/Documentation/hwmon/marvel-env.rst
> @@ -0,0 +1,60 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Kernel driver marvel-env
> +========================
> +
> +Supported systems:
> +
> + * Marvel (EV7) AlphaServers: the GS1280, ES80 and ES47 families,
> + booted from the SRM console
> +
> +Author: Matt Turner <mattst88@gmail.com>
> +
> +Description
> +-----------
> +
> +The management firmware of these machines monitors temperatures, fans,
> +voltages and power supplies, and the SRM console describes each sensor in
> +its configuration tree (GCT). This driver asks the console to refresh the
> +readings and exports them.
> +
> +The console has no sensor names, so the labels are built from the reporting
> +unit (such as ``CMM0`` or ``MBM/PBM``) and the sensor's position in that
> +unit. On systems with more than one drawer the label starts with the
> +cabinet and drawer numbers.
> +
> +A refresh keeps the boot CPU in the console with interrupts disabled for up
> +to 300 ms per unit, so a unit is refreshed at most once every
> +``update_interval`` milliseconds (10000 by default).
> +
> +Sysfs entries
> +-------------
> +
> +=========================== =================================================
> +``update_interval`` Minimum time between refreshes of a unit, in ms
> +
> +``temp[1-*]_input`` Temperature
> +``temp[1-*]_max`` Warning threshold
> +``temp[1-*]_crit`` Fault threshold
> +``temp[1-*]_max_alarm`` Sensor is in the warning or fault state
"fault": Sensor is broken.
"max_alarm": Temperature is too high.
That is distincly different. A faulty sensor should not be reported
as "temperature too high".
> +``temp[1-*]_crit_alarm`` Sensor is in the fault state
"fault" and "critical" have a different meaning. If this is a
fault state (sensor is broken), it should be reported as such.
> +``temp[1-*]_label`` Sensor label
> +
> +``fan[1-*]_input`` Fan speed
> +``fan[1-*]_min`` Warning threshold
> +``fan[1-*]_min_alarm`` Fan is in the warning or fault state
This is wrong since there is also a separate fault attribute,
and "fault" means defective, not "speed too low".
> +``fan[1-*]_fault`` Fan is in the fault state
> +``fan[1-*]_label`` Fan label
> +
> +``in[0-*]_input`` Voltage
> +``in[0-*]_alarm`` Sensor is in the warning or fault state
Which one is it ? Is it distinct ? A "fault" means
that the hardware is broken/defective. An alarm means that
the voltage is too high or too low. A faulty channel
should not be reported as alarm but as fault.
> +``in[0-*]_label`` Sensor label
> +
> +``power[1-*]_alarm`` Power supply is in the warning or fault state
> +``power[1-*]_label`` Power supply label
"power" is supposed to report the system power, not
a "power supply warning or fault state". If there is
nothing else, this should be identical to the matching
inX_alarm and/or inX_fault attribute.
Thanks,
Guenter
> +=========================== =================================================
> +
> +Sensors that the console reports as unknown or not present when the driver
> +loads are not exported. If a sensor becomes unknown later, reading its input
> +attribute fails with ``-ENODATA``, as does reading the alarm of a voltage
> +sensor or power supply.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 93abbb315065..09dee8fab9ec 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -16100,6 +16100,14 @@ L: linux-mips@vger.kernel.org
> S: Maintained
> F: arch/mips/boot/dts/img/pistachio*
>
> +MARVEL ALPHASERVER ENVIRONMENTAL SENSOR DRIVER
> +M: Matt Turner <mattst88@gmail.com>
> +L: linux-hwmon@vger.kernel.org
> +L: linux-alpha@vger.kernel.org
> +S: Maintained
> +F: Documentation/hwmon/marvel-env.rst
> +F: drivers/hwmon/marvel-env.c
> +
> MARVELL 88E6XXX ETHERNET SWITCH FABRIC DRIVER
> M: Andrew Lunn <andrew@lunn.ch>
> L: netdev@vger.kernel.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 18c682e1a475..50a1bf966f30 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1255,6 +1255,17 @@ config SENSORS_MACSMC_HWMON
> This driver can also be built as a module. If so, the module will
> be called macsmc-hwmon.
>
> +config SENSORS_MARVEL_ENV
> + tristate "Marvel (EV7) AlphaServer environmental sensors"
> + depends on ALPHA_MARVEL || ALPHA_GENERIC
> + help
> + If you say yes here you get support for the temperature, fan,
> + voltage and power supply sensors of Marvel AlphaServers (the
> + GS1280, ES80 and ES47), as described by the SRM console.
> +
> + This driver can also be built as a module. If so, the module
> + will be called marvel-env.
> +
> config SENSORS_MAX1111
> tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
> depends on SPI_MASTER
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index d4c345ed7261..2b1b1850d641 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -157,6 +157,7 @@ obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o
> obj-$(CONFIG_SENSORS_LTC4283) += ltc4283.o
> obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
> obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o
> +obj-$(CONFIG_SENSORS_MARVEL_ENV) += marvel-env.o
> obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
> obj-$(CONFIG_SENSORS_MAX127) += max127.o
> obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
> diff --git a/drivers/hwmon/marvel-env.c b/drivers/hwmon/marvel-env.c
> new file mode 100644
> index 000000000000..cdc7e059e17b
> --- /dev/null
> +++ b/drivers/hwmon/marvel-env.c
> @@ -0,0 +1,552 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Environmental sensors of Marvel (EV7) AlphaServers: the GS1280, ES80 and
> + * ES47 families.
> + *
> + * The SRM console describes every sensor in the config tree (GCT). Each power
> + * and environment (P&E) node may carry a list of environmental packets:
> + * temperature and voltage sensors, fans and power supplies. The console fills
> + * in the readings when asked to refresh the node. The packet layout was
> + * taken from Tru64 UNIX.
> + */
> +
> +#include <linux/hwmon.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#include <asm/gct.h>
> +#include <asm/marvel_env.h>
> +
> +#define DRVNAME "marvel_env"
> +
> +#define GCT_TYPE_PE 0x13
> +#define GCT_FLAGS_REQUIRED BIT(9)
> +#define GCT_FLAGS_EXCLUDED (BIT(2) | BIT(7) | BIT(12) | BIT(14))
> +
> +/* Packet types. */
> +#define ENV_PKT_POWER_SUPPLY 0x08
> +#define ENV_PKT_FAN 0x09
> +#define ENV_PKT_SENSOR 0x2a
> +
> +/*
> + * Sensor packet: kind, status, reading, high warning, high fault.
> + * Temperatures are in tenths of a degree Celsius, voltages in millivolts.
> + * For voltages the two thresholds look like the nominal voltage and an upper
> + * limit, so they are not exported.
> + */
> +#define SENSOR_KIND 0x28
> +#define SENSOR_STATUS 0x2c
> +#define SENSOR_VALUE 0x30
> +#define SENSOR_WARN 0x34
> +#define SENSOR_FAULT 0x38
> +#define SENSOR_SIZE 0x3c
> +
> +#define SENSOR_KIND_THERMAL 2
> +#define SENSOR_KIND_VOLT_FIRST 3
> +#define SENSOR_KIND_VOLT_LAST 5
> +
> +/* Fan packet: status, rpm, low warning, low fault. */
> +#define FAN_STATUS 0x24
> +#define FAN_VALUE 0x28
> +#define FAN_WARN 0x2c
> +#define FAN_SIZE 0x34
> +
> +/* Power supply packet: status. */
> +#define PS_STATUS 0x24
> +#define PS_SIZE 0x28
> +
> +/* Status values, in the low byte of the status word. */
> +#define ENV_OK 0
> +#define ENV_WARNING 1
> +#define ENV_FAULT 2
> +#define ENV_UNKNOWN 0xfe
> +#define ENV_NOT_PRESENT 0xff
> +
> +/* Console status of a refresh that Tru64 also treats as success. */
> +#define CFGTREE_STATUS_OK2 (-0x4a)
> +
> +struct marvel_env_node {
> + gct6_node *node;
> + unsigned long offset; /* from the start of the GCT */
> + unsigned long last_refresh;
> + bool valid;
> + unsigned int cabinet, drawer, pe;
> +};
> +
> +struct marvel_env_chan {
> + struct marvel_env_node *node;
> + unsigned int pkt; /* packet offset in the node */
> + char label[32];
> +};
> +
> +struct marvel_env_data {
> + struct device *dev;
> + struct mutex lock; /* serializes refreshes and reads */
> + unsigned int update_interval; /* ms */
> + struct marvel_env_node *nodes;
> + unsigned int nr_nodes;
> + struct marvel_env_chan *chans[hwmon_max];
> + unsigned int nr_chans[hwmon_max];
> +};
> +
> +static const char * const pe_names[] = {
> + [0x09] = "MBM",
> + [0x0a] = "MBM/PBM",
> + [0x0c] = "PS",
> + [0x0d] = "FAN",
> + [0x0f] = "CMM0", [0x10] = "CMM1", [0x11] = "CMM2", [0x12] = "CMM3",
> + [0x14] = "VRM0", [0x15] = "VRM1", [0x16] = "VRM2", [0x17] = "VRM3",
> + [0x19] = "PBM",
> + [0x1a] = "XSHELF",
> + [0x1b] = "PCIPS",
> + [0x1c] = "PCIFANS",
> +};
> +
> +static inline u32 env_rd(struct marvel_env_node *n, unsigned int off)
> +{
> + return *(u32 *)((char *)n->node + off);
> +}
> +
> +static inline u8 env_status(struct marvel_env_node *n, unsigned int off)
> +{
> + return env_rd(n, off) & 0xff;
> +}
> +
> +static bool env_status_valid(u8 status)
> +{
> + return status != ENV_UNKNOWN && status != ENV_NOT_PRESENT;
> +}
> +
> +static bool env_status_alarm(u8 status)
> +{
> + return status == ENV_WARNING || status == ENV_FAULT;
> +}
> +
> +static unsigned int env_status_off(enum hwmon_sensor_types type)
> +{
> + switch (type) {
> + case hwmon_fan:
> + return FAN_STATUS;
> + case hwmon_power:
> + return PS_STATUS;
> + default:
> + return SENSOR_STATUS;
> + }
> +}
> +
> +static inline gct6_node *gct_node(gct6_handle h)
> +{
> + return GCT_NODE_PTR(h);
> +}
> +
> +/* Cabinet and drawer come from the P&E node's parent. */
> +static void marvel_env_keys(struct marvel_env_node *n)
> +{
> + gct6_node *parent = n->node->parent ? gct_node(n->node->parent) : NULL;
> +
> + n->pe = n->node->id & 0xff;
> + if (!parent)
> + return;
> + if (parent->type == GCT_TYPE_PE && parent->subtype == 0x3d)
> + n->cabinet = (parent->id >> 48) & 0xff;
> + else
> + n->cabinet = (parent->id >> 48) & 0x3f;
> + n->drawer = (parent->id >> 32) & 0xff;
> +}
> +
> +static int marvel_env_refresh(struct marvel_env_data *data,
> + struct marvel_env_node *n)
> +{
> + long status;
> +
> + if (n->valid && time_before(jiffies, n->last_refresh +
> + msecs_to_jiffies(data->update_interval)))
> + return 0;
> +
> + status = marvel_cfgtree_partial_update(n->offset);
> + if (status != 1 && status != CFGTREE_STATUS_OK2) {
> + dev_warn_ratelimited(data->dev,
> + "refresh of GCT node %#lx failed: %ld\n",
> + n->offset, status);
> + return -EIO;
> + }
> + n->last_refresh = jiffies;
> + n->valid = true;
> + return 0;
> +}
> +
> +/* The hwmon type of a packet, or hwmon_max to skip it. */
> +static enum hwmon_sensor_types marvel_env_pkt_type(struct marvel_env_node *n,
> + unsigned int pkt,
> + unsigned int type)
> +{
> + unsigned int len = env_rd(n, pkt) & 0xffff;
> + u32 kind;
> +
> + switch (type) {
> + case ENV_PKT_SENSOR:
> + if (len < SENSOR_SIZE)
> + break;
> + kind = env_rd(n, pkt + SENSOR_KIND);
> + if (kind == SENSOR_KIND_THERMAL)
> + return hwmon_temp;
> + if (kind >= SENSOR_KIND_VOLT_FIRST && kind <= SENSOR_KIND_VOLT_LAST)
> + return hwmon_in;
> + break;
> + case ENV_PKT_FAN:
> + if (len >= FAN_SIZE)
> + return hwmon_fan;
> + break;
> + case ENV_PKT_POWER_SUPPLY:
> + if (len >= PS_SIZE)
> + return hwmon_power;
> + break;
> + }
> + return hwmon_max;
> +}
> +
> +static void marvel_env_add_chan(struct marvel_env_data *data,
> + struct marvel_env_node *n, unsigned int pkt,
> + enum hwmon_sensor_types type,
> + unsigned int index)
> +{
> + static const char * const what[hwmon_max] = {
> + [hwmon_temp] = "Temp", [hwmon_fan] = "Fan",
> + [hwmon_in] = "Volt", [hwmon_power] = "PS",
> + };
> + struct marvel_env_chan *c;
> + const char *pe = NULL;
> + size_t len = 0;
> +
> + c = &data->chans[type][data->nr_chans[type]++];
> + c->node = n;
> + c->pkt = pkt;
> +
> + if (n->cabinet || n->drawer)
> + len = scnprintf(c->label, sizeof(c->label), "Cab%u Dra%u ",
> + n->cabinet, n->drawer);
> +
> + if (n->pe < ARRAY_SIZE(pe_names))
> + pe = pe_names[n->pe];
> + if (pe && !strncasecmp(pe, what[type], strlen(what[type])))
> + scnprintf(c->label + len, sizeof(c->label) - len, "%s %u",
> + pe, index);
> + else if (pe)
> + scnprintf(c->label + len, sizeof(c->label) - len, "%s %s %u",
> + pe, what[type], index);
> + else
> + scnprintf(c->label + len, sizeof(c->label) - len, "PE%u %s %u",
> + n->pe, what[type], index);
> +}
> +
> +/*
> + * Walk a P&E node's packets, counting them, or adding them when @add. The
> + * header at hd_extension holds the count at +0x10 and the offset of the
> + * first packet at +0x14. As Tru64 does, visit count + 1 packets and number
> + * sensors from their position minus one.
> + */
> +static void marvel_env_walk(struct marvel_env_data *data,
> + struct marvel_env_node *n, bool add)
> +{
> + unsigned int size = n->node->size;
> + unsigned int hdr = n->node->hd_extension;
> + unsigned int count, pkt, i;
> +
> + if (!hdr || hdr + 0x18 > size)
> + return;
> + count = env_rd(n, hdr + 0x10);
> + pkt = env_rd(n, hdr + 0x14);
> +
> + for (i = 0; i <= count; i++) {
> + enum hwmon_sensor_types type;
> + unsigned int len, pkt_type;
> +
> + if (pkt < 8 || pkt + 8 > size)
> + break;
> + len = env_rd(n, pkt) & 0xffff;
> + pkt_type = env_rd(n, pkt + 4) & 0xffff;
> + if (len < 8 || pkt + len > size)
> + break;
> +
> + type = marvel_env_pkt_type(n, pkt, pkt_type);
> + if (type != hwmon_max && add)
> + marvel_env_add_chan(data, n, pkt, type,
> + pkt_type == ENV_PKT_SENSOR && i ?
> + i - 1 : i);
> + else if (type != hwmon_max)
> + data->nr_chans[type]++;
> + pkt += len;
> + }
> +}
> +
> +/* Collect the P&E nodes with an environmental packet list. */
> +static void marvel_env_find_nodes(struct marvel_env_data *data,
> + gct6_node *node, bool record)
> +{
> + for (; node; node = node->next ? gct_node(node->next) : NULL) {
> + if (node->magic != GCT_NODE_MAGIC)
> + return;
> + if (node->type == GCT_TYPE_PE && node->hd_extension &&
> + (node->flags & GCT_FLAGS_REQUIRED) &&
> + !(node->flags & GCT_FLAGS_EXCLUDED)) {
> + if (record) {
> + struct marvel_env_node *n =
> + &data->nodes[data->nr_nodes];
> +
> + n->node = node;
> + n->offset = (char *)node - (char *)gct_node(0);
> + marvel_env_keys(n);
> + }
> + data->nr_nodes++;
> + }
> + if (node->child)
> + marvel_env_find_nodes(data, gct_node(node->child),
> + record);
> + }
> +}
> +
> +static umode_t marvel_env_is_visible(const void *drvdata,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + const struct marvel_env_data *data = drvdata;
> + const struct marvel_env_chan *c;
> +
> + if (type == hwmon_chip)
> + return 0644;
> +
> + /* Hide sensors that are absent when the driver loads. */
> + c = &data->chans[type][channel];
> + if (!env_status_valid(env_status(c->node, c->pkt + env_status_off(type))))
> + return 0;
> + return 0444;
> +}
> +
> +static int marvel_env_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + struct marvel_env_data *data = dev_get_drvdata(dev);
> + struct marvel_env_chan *c;
> + struct marvel_env_node *n;
> + unsigned int pkt;
> + u8 status;
> + int ret;
> +
> + if (type == hwmon_chip) {
> + *val = data->update_interval;
> + return 0;
> + }
> +
> + c = &data->chans[type][channel];
> + n = c->node;
> + pkt = c->pkt;
> +
> + mutex_lock(&data->lock);
> + ret = marvel_env_refresh(data, n);
> + if (ret)
> + goto out;
> +
> + switch (type) {
> + case hwmon_temp:
> + status = env_status(n, pkt + SENSOR_STATUS);
> + switch (attr) {
> + case hwmon_temp_input:
> + if (!env_status_valid(status))
> + ret = -ENODATA;
> + else
> + *val = (s32)env_rd(n, pkt + SENSOR_VALUE) * 100L;
> + break;
> + case hwmon_temp_max:
> + *val = (s32)env_rd(n, pkt + SENSOR_WARN) * 100L;
> + break;
> + case hwmon_temp_crit:
> + *val = (s32)env_rd(n, pkt + SENSOR_FAULT) * 100L;
> + break;
> + case hwmon_temp_max_alarm:
> + *val = env_status_alarm(status);
> + break;
> + case hwmon_temp_crit_alarm:
> + *val = status == ENV_FAULT;
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> + break;
> + case hwmon_fan:
> + status = env_status(n, pkt + FAN_STATUS);
> + switch (attr) {
> + case hwmon_fan_input:
> + if (!env_status_valid(status))
> + ret = -ENODATA;
> + else
> + *val = env_rd(n, pkt + FAN_VALUE);
> + break;
> + case hwmon_fan_min:
> + *val = env_rd(n, pkt + FAN_WARN);
> + break;
> + case hwmon_fan_min_alarm:
> + *val = env_status_alarm(status);
> + break;
> + case hwmon_fan_fault:
> + *val = status == ENV_FAULT;
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> + break;
> + case hwmon_in:
> + case hwmon_power:
> + status = env_status(n, pkt + env_status_off(type));
> + if (!env_status_valid(status))
> + ret = -ENODATA;
> + else if (type == hwmon_in && attr == hwmon_in_input)
> + *val = (s32)env_rd(n, pkt + SENSOR_VALUE);
> + else
> + *val = env_status_alarm(status);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +out:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +static int marvel_env_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + struct marvel_env_data *data = dev_get_drvdata(dev);
> +
> + if (type != hwmon_chip || attr != hwmon_chip_update_interval)
> + return -EOPNOTSUPP;
> +
> + mutex_lock(&data->lock);
> + data->update_interval = clamp_val(val, 0, 3600 * MSEC_PER_SEC);
> + mutex_unlock(&data->lock);
> + return 0;
> +}
> +
> +static int marvel_env_read_string(struct device *dev,
> + enum hwmon_sensor_types type, u32 attr,
> + int channel, const char **str)
> +{
> + struct marvel_env_data *data = dev_get_drvdata(dev);
> +
> + *str = data->chans[type][channel].label;
> + return 0;
> +}
> +
> +static const struct hwmon_ops marvel_env_ops = {
> + .is_visible = marvel_env_is_visible,
> + .read = marvel_env_read,
> + .write = marvel_env_write,
> + .read_string = marvel_env_read_string,
> +};
> +
> +static const struct {
> + enum hwmon_sensor_types type;
> + u32 config;
> +} marvel_env_hwmon[] = {
> + { hwmon_temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL },
> + { hwmon_fan, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MIN_ALARM |
> + HWMON_F_FAULT | HWMON_F_LABEL },
> + { hwmon_in, HWMON_I_INPUT | HWMON_I_ALARM | HWMON_I_LABEL },
> + { hwmon_power, HWMON_P_ALARM | HWMON_P_LABEL },
> +};
> +
> +static const struct hwmon_channel_info * const marvel_env_chip_info =
> + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL);
> +
> +static int marvel_env_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + const struct hwmon_channel_info **info;
> + struct marvel_env_data *data;
> + struct hwmon_chip_info *chip;
> + struct device *hwmon;
> + unsigned int i, k, nr_info = 0;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> + data->dev = dev;
> + data->update_interval = 10 * MSEC_PER_SEC;
> + mutex_init(&data->lock);
> +
> + marvel_env_find_nodes(data, gct_node(0), false);
> + if (!data->nr_nodes)
> + return -ENODEV;
> + data->nodes = devm_kcalloc(dev, data->nr_nodes, sizeof(*data->nodes),
> + GFP_KERNEL);
> + if (!data->nodes)
> + return -ENOMEM;
> + data->nr_nodes = 0;
> + marvel_env_find_nodes(data, gct_node(0), true);
> +
> + for (i = 0; i < data->nr_nodes; i++) {
> + marvel_env_refresh(data, &data->nodes[i]);
> + marvel_env_walk(data, &data->nodes[i], false);
> + }
> +
> + info = devm_kcalloc(dev, ARRAY_SIZE(marvel_env_hwmon) + 2,
> + sizeof(*info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> + info[nr_info++] = marvel_env_chip_info;
> +
> + for (k = 0; k < ARRAY_SIZE(marvel_env_hwmon); k++) {
> + enum hwmon_sensor_types type = marvel_env_hwmon[k].type;
> + struct hwmon_channel_info *ci;
> + u32 *config;
> +
> + if (!data->nr_chans[type])
> + continue;
> + data->chans[type] = devm_kcalloc(dev, data->nr_chans[type],
> + sizeof(*data->chans[type]),
> + GFP_KERNEL);
> + ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
> + config = devm_kcalloc(dev, data->nr_chans[type] + 1,
> + sizeof(*config), GFP_KERNEL);
> + if (!data->chans[type] || !ci || !config)
> + return -ENOMEM;
> + for (i = 0; i < data->nr_chans[type]; i++)
> + config[i] = marvel_env_hwmon[k].config;
> + ci->type = type;
> + ci->config = config;
> + info[nr_info++] = ci;
> + data->nr_chans[type] = 0;
> + }
> + if (nr_info == 1)
> + return -ENODEV;
> +
> + for (i = 0; i < data->nr_nodes; i++)
> + marvel_env_walk(data, &data->nodes[i], true);
> +
> + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
> + chip->ops = &marvel_env_ops;
> + chip->info = info;
> +
> + hwmon = devm_hwmon_device_register_with_info(dev, DRVNAME, data, chip,
> + NULL);
> + return PTR_ERR_OR_ZERO(hwmon);
> +}
> +
> +static struct platform_driver marvel_env_driver = {
> + .driver = {
> + .name = DRVNAME,
> + },
> + .probe = marvel_env_probe,
> +};
> +module_platform_driver(marvel_env_driver);
> +
> +MODULE_ALIAS("platform:" DRVNAME);
> +MODULE_DESCRIPTION("Marvel (EV7) AlphaServer environmental sensors");
> +MODULE_LICENSE("GPL");
>
next prev parent reply other threads:[~2026-11-14 23:00 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 17:39 [PATCH 0/7] alpha: marvel: " Matt Turner
2026-11-14 17:39 ` [PATCH 1/7] alpha: marvel: report environmental events without VERBOSE_MCHECK Matt Turner
2026-11-14 23:01 ` Guenter Roeck
2026-11-14 17:39 ` [PATCH 2/7] alpha: marvel: fix the environmental subpacket layout Matt Turner
2026-11-14 17:39 ` [PATCH 3/7] alpha: marvel: refresh environmental readings through the console Matt Turner
2026-11-14 17:39 ` [PATCH 4/7] alpha: marvel: register a platform device for the environmental sensors Matt Turner
2026-11-14 17:39 ` [PATCH 5/7] hwmon: Add driver for Marvel AlphaServer " Matt Turner
2026-11-14 23:00 ` Guenter Roeck [this message]
2026-11-14 17:39 ` [PATCH 6/7] alpha: marvel: notify environmental events Matt Turner
2026-11-14 17:39 ` [PATCH 7/7] hwmon: (marvel-env) Notify userspace of " Matt Turner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e8ffe364-5c98-4987-b6bd-c263122aeae8@roeck-us.net \
--to=linux@roeck-us.net \
--cc=linmag7@gmail.com \
--cc=linux-alpha@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mattst88@gmail.com \
--cc=richard.henderson@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®