On 6/16/20 1:25 AM, Lars Povlsen wrote: > This patch adds a temperature sensor driver to the Sparx5 SoC. > > Signed-off-by: Lars Povlsen <lars.povlsen@xxxxxxxxxxxxx> > --- > drivers/hwmon/Kconfig | 10 +++ > drivers/hwmon/Makefile | 1 + > drivers/hwmon/sparx5-temp.c | 136 ++++++++++++++++++++++++++++++++++++ This will also require documentation in Documentation/hwmon/sparx5-temp.rst > 3 files changed, 147 insertions(+) > create mode 100644 drivers/hwmon/sparx5-temp.c > > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig > index 288ae9f63588c..7fb5e0c6c6306 100644 > --- a/drivers/hwmon/Kconfig > +++ b/drivers/hwmon/Kconfig > @@ -515,6 +515,16 @@ config SENSORS_I5K_AMB > This driver can also be built as a module. If so, the module > will be called i5k_amb. > > +config SENSORS_SPARX5 > + tristate "Sparx5 SoC temperature sensor" > + depends on ARCH_SPARX5 || COMPILE_TEST > + help > + If you say yes here you get support for temperature monitoring > + with the Microchip Sparx5 SoC. > + > + This driver can also be built as a module. If so, the module > + will be called sparx5-temp. > + > config SENSORS_F71805F > tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" > depends on !PPC > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile > index 3e32c21f5efe3..857293f650412 100644 > --- a/drivers/hwmon/Makefile > +++ b/drivers/hwmon/Makefile > @@ -167,6 +167,7 @@ obj-$(CONFIG_SENSORS_SMM665) += smm665.o > obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o > obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o > obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o > +obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o > obj-$(CONFIG_SENSORS_STTS751) += stts751.o > obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o > obj-$(CONFIG_SENSORS_TC74) += tc74.o > diff --git a/drivers/hwmon/sparx5-temp.c b/drivers/hwmon/sparx5-temp.c > new file mode 100644 > index 0000000000000..4ed8a2aec3ae9 > --- /dev/null > +++ b/drivers/hwmon/sparx5-temp.c > @@ -0,0 +1,136 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* Sparx5 SoC temperature sensor driver > + * > + * Copyright (C) 2020 Lars Povlsen <lars.povlsen@xxxxxxxxxxxxx> > + */ > + > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/hwmon.h> > +#include <linux/io.h> > +#include <linux/platform_device.h> > +#include <linux/mod_devicetable.h> > +#include <linux/bitfield.h> Alphabetic order, please. > + > +#define TEMP_CTRL 0 > +#define TEMP_CFG 4 > +#define TEMP_CFG_CYCLES GENMASK(24, 15) > +#define TEMP_CFG_ENA BIT(0) > +#define TEMP_STAT 8 > +#define TEMP_STAT_VALID BIT(12) > +#define TEMP_STAT_TEMP GENMASK(11, 0) > + > +struct s5_hwmon { > + void __iomem *base; > +}; > + > +static void s5_temp_enable(struct s5_hwmon *hwmon) > +{ > + u32 val = readl(hwmon->base + TEMP_CFG); > + u32 clk = 250; > + Unnecessary variable, and magic number. It would be better to use a define or at least explain what the number is for. Also, if this is associated with a system clock, would it make sense to use the clock subsystem API to get the rate ? > + val &= ~TEMP_CFG_CYCLES; > + val |= FIELD_PREP(TEMP_CFG_CYCLES, clk); > + val |= TEMP_CFG_ENA; > + > + writel(val, hwmon->base + TEMP_CFG); > +} > + > +static int s5_read(struct device *dev, enum hwmon_sensor_types type, > + u32 attr, int channel, long *temp) > +{ > + struct s5_hwmon *hwmon = dev_get_drvdata(dev); > + int rc = 0, value; > + u32 stat; > + > + switch (attr) { > + case hwmon_temp_input: > + stat = readl_relaxed(hwmon->base + TEMP_STAT); > + if (!(stat & TEMP_STAT_VALID)) > + return -EIO; > + value = stat & TEMP_STAT_TEMP; > + value = DIV_ROUND_CLOSEST(value * 3522, 4096) - 1094; A comment describing the calculation would be useful, not only to help the reader but also to help me verify if the calculation is correct (especially since datasheets don't seem to be public). > + value *= 100; > + *temp = value; > + break; > + default: > + rc = -EOPNOTSUPP; > + break; > + } > + > + return rc; > +} > + > +static umode_t s5_is_visible(const void *_data, enum hwmon_sensor_types type, > + u32 attr, int channel) > +{ > + if (type != hwmon_temp) > + return 0; > + > + switch (attr) { > + case hwmon_temp_input: > + return 0444; > + default: > + return 0; > + } > +} > + > +static const struct hwmon_channel_info *s5_info[] = { > + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), > + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), > + NULL > +}; > + > +static const struct hwmon_ops s5_hwmon_ops = { > + .is_visible = s5_is_visible, > + .read = s5_read, > +}; > + > +static const struct hwmon_chip_info s5_chip_info = { > + .ops = &s5_hwmon_ops, > + .info = s5_info, > +}; > + > +static int s5_temp_probe(struct platform_device *pdev) > +{ > + struct device *hwmon_dev; > + struct s5_hwmon *hwmon; > + > + hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); > + if (!hwmon) > + return -ENOMEM; > + > + hwmon->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(hwmon->base)) > + return PTR_ERR(hwmon->base); > + > + s5_temp_enable(hwmon); > + > + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, > + "s5_temp", > + hwmon, > + &s5_chip_info, > + NULL); > + > + return PTR_ERR_OR_ZERO(hwmon_dev); > +} > + > +const struct of_device_id s5_temp_match[] = { > + { .compatible = "microchip,sparx5-temp" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, s5_temp_match); > + > +static struct platform_driver s5_temp_driver = { > + .probe = s5_temp_probe, > + .driver = { > + .name = "sparx5-temp", > + .of_match_table = s5_temp_match, > + }, > +}; > + > +module_platform_driver(s5_temp_driver); > + > +MODULE_AUTHOR("Lars Povlsen <lars.povlsen@xxxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("Sparx5 SoC temperature sensor driver"); > +MODULE_LICENSE("GPL"); >