hwmon: add SMARC-sAM67 support

Add a new driver for the Kontron SMARC-sAM67 board management
controller. It has two voltage sensors and one temperature sensor.

Signed-off-by: Michael Walle <mwalle@kernel.org>
Link: https://lore.kernel.org/r/20250912120745.2295115-7-mwalle@kernel.org
[groeck: Added sa67 to index.rst]
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Michael Walle
2025-09-12 14:07:44 +02:00
committed by Guenter Roeck
parent 0f6eae86e6
commit 443b39c82c
6 changed files with 215 additions and 0 deletions

View File

@@ -214,6 +214,7 @@ Hardware Monitoring Kernel Drivers
q54sj108a2
qnap-mcu-hwmon
raspberrypi-hwmon
sa67
sbrmi
sbtsi_temp
sch5627

View File

@@ -0,0 +1,41 @@
.. SPDX-License-Identifier: GPL-2.0-only
Kernel driver sa67mcu
=====================
Supported chips:
* Kontron sa67mcu
Prefix: 'sa67mcu'
Datasheet: not available
Authors: Michael Walle <mwalle@kernel.org>
Description
-----------
The sa67mcu is a board management controller which also exposes a hardware
monitoring controller.
The controller has two voltage and one temperature sensor. The values are
hold in two 8 bit registers to form one 16 bit value. Reading the lower byte
will also capture the high byte to make the access atomic. The unit of the
volatge sensors are 1mV and the unit of the temperature sensor is 0.1degC.
Sysfs entries
-------------
The following attributes are supported.
======================= ========================================================
in0_label "VDDIN"
in0_input Measured VDDIN voltage.
in1_label "VDD_RTC"
in1_input Measured VDD_RTC voltage.
temp1_input MCU temperature. Roughly the board temperature.
======================= ========================================================

View File

@@ -23231,6 +23231,7 @@ F: Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
F: Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml
F: Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml
F: drivers/gpio/gpio-sl28cpld.c
F: drivers/hwmon/sa67mcu-hwmon.c
F: drivers/hwmon/sl28cpld-hwmon.c
F: drivers/irqchip/irq-sl28cpld.c
F: drivers/pwm/pwm-sl28cpld.c

View File

@@ -1905,6 +1905,16 @@ config SENSORS_RASPBERRYPI_HWMON
This driver can also be built as a module. If so, the module
will be called raspberrypi-hwmon.
config SENSORS_SA67MCU
tristate "Kontron sa67mcu hardware monitoring driver"
depends on MFD_SL28CPLD || COMPILE_TEST
help
If you say yes here you get support for the voltage and temperature
monitor of the sa67 board management controller.
This driver can also be built as a module. If so, the module
will be called sa67mcu-hwmon.
config SENSORS_SL28CPLD
tristate "Kontron sl28cpld hardware monitoring driver"
depends on MFD_SL28CPLD || COMPILE_TEST

View File

@@ -197,6 +197,7 @@ obj-$(CONFIG_SENSORS_PT5161L) += pt5161l.o
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) += qnap-mcu-hwmon.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
obj-$(CONFIG_SENSORS_SA67MCU) += sa67mcu-hwmon.o
obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o
obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o
obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o

View File

@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* sl67mcu hardware monitoring driver
*
* Copyright 2025 Kontron Europe GmbH
*/
#include <linux/bitfield.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#define SA67MCU_VOLTAGE(n) (0x00 + ((n) * 2))
#define SA67MCU_TEMP(n) (0x04 + ((n) * 2))
struct sa67mcu_hwmon {
struct regmap *regmap;
u32 offset;
};
static int sa67mcu_hwmon_read(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, long *input)
{
struct sa67mcu_hwmon *hwmon = dev_get_drvdata(dev);
unsigned int offset;
u8 reg[2];
int ret;
switch (type) {
case hwmon_in:
switch (attr) {
case hwmon_in_input:
offset = hwmon->offset + SA67MCU_VOLTAGE(channel);
break;
default:
return -EOPNOTSUPP;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
offset = hwmon->offset + SA67MCU_TEMP(channel);
break;
default:
return -EOPNOTSUPP;
}
break;
default:
return -EOPNOTSUPP;
}
/* Reading the low byte will capture the value */
ret = regmap_bulk_read(hwmon->regmap, offset, reg, ARRAY_SIZE(reg));
if (ret)
return ret;
*input = reg[1] << 8 | reg[0];
/* Temperatures are s16 and in 0.1degC steps. */
if (type == hwmon_temp)
*input = sign_extend32(*input, 15) * 100;
return 0;
}
static const struct hwmon_channel_info * const sa67mcu_hwmon_info[] = {
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
static const char *const sa67mcu_hwmon_in_labels[] = {
"VDDIN",
"VDD_RTC",
};
static int sa67mcu_hwmon_read_string(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
switch (type) {
case hwmon_in:
switch (attr) {
case hwmon_in_label:
*str = sa67mcu_hwmon_in_labels[channel];
return 0;
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
}
static const struct hwmon_ops sa67mcu_hwmon_ops = {
.visible = 0444,
.read = sa67mcu_hwmon_read,
.read_string = sa67mcu_hwmon_read_string,
};
static const struct hwmon_chip_info sa67mcu_hwmon_chip_info = {
.ops = &sa67mcu_hwmon_ops,
.info = sa67mcu_hwmon_info,
};
static int sa67mcu_hwmon_probe(struct platform_device *pdev)
{
struct sa67mcu_hwmon *hwmon;
struct device *hwmon_dev;
int ret;
if (!pdev->dev.parent)
return -ENODEV;
hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!hwmon->regmap)
return -ENODEV;
ret = device_property_read_u32(&pdev->dev, "reg", &hwmon->offset);
if (ret)
return -EINVAL;
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"sa67mcu_hwmon", hwmon,
&sa67mcu_hwmon_chip_info,
NULL);
if (IS_ERR(hwmon_dev))
dev_err(&pdev->dev, "failed to register as hwmon device");
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct of_device_id sa67mcu_hwmon_of_match[] = {
{ .compatible = "kontron,sa67mcu-hwmon", },
{}
};
MODULE_DEVICE_TABLE(of, sa67mcu_hwmon_of_match);
static struct platform_driver sa67mcu_hwmon_driver = {
.probe = sa67mcu_hwmon_probe,
.driver = {
.name = "sa67mcu-hwmon",
.of_match_table = sa67mcu_hwmon_of_match,
},
};
module_platform_driver(sa67mcu_hwmon_driver);
MODULE_DESCRIPTION("sa67mcu Hardware Monitoring Driver");
MODULE_AUTHOR("Michael Walle <mwalle@kernel.org>");
MODULE_LICENSE("GPL");