From 413071dc2f13f581eb844a0009d571dfa0bb2f02 Mon Sep 17 00:00:00 2001 From: Lars Randers Date: Thu, 20 Jun 2024 13:55:40 +0000 Subject: [PATCH] BeagleV-Fire: Add MPFS TVS auxiliary driver Signed-off-by: Lars Randers --- drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/tvs-mpfs.c | 371 +++++++++++++++++++++++++++++++++ drivers/mailbox/mailbox-mpfs.c | 62 ++++++ 4 files changed, 445 insertions(+) create mode 100644 drivers/hwmon/tvs-mpfs.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index a5143d01b..e3564df18 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2108,6 +2108,17 @@ config SENSORS_TMP513 This driver can also be built as a module. If so, the module will be called tmp513. +config SENSORS_TVS_MPFS + tristate "PolarFire SoC (MPFS) TVS" + depends on POLARFIRE_SOC_MAILBOX + help + This driver adds support for the PolarFire SoC (MPFS) Temperature Voltage Sensor. + + To compile this driver as a module, choose M here. the + module will be called tvs-mpfs. + + If unsure, say N. + config SENSORS_VEXPRESS tristate "Versatile Express" depends on VEXPRESS_CONFIG diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 11d076cad..1cebd0e73 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -203,6 +203,7 @@ obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TMP464) += tmp464.o obj-$(CONFIG_SENSORS_TMP513) += tmp513.o +obj-$(CONFIG_SENSORS_TVS_MPFS) += tvs-mpfs.o obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o diff --git a/drivers/hwmon/tvs-mpfs.c b/drivers/hwmon/tvs-mpfs.c new file mode 100644 index 000000000..087a8b33f --- /dev/null +++ b/drivers/hwmon/tvs-mpfs.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * + * Author: Lars Randers + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PFSOC_CONTROL_SCB_TVS_CONTROL 0x08 +#define PFSOC_CONTROL_SCB_TVS_OUTPUT0 0x24 +#define PFSOC_CONTROL_SCB_TVS_OUTPUT1 0x28 + +#define CTRL_POWEROFF BIT(5) +#define CTRL_ABORT BIT(4) +#define CTRL_TEMP BIT(3) +#define CTRL_2P5 BIT(2) +#define CTRL_1P8 BIT(1) +#define CTRL_1P05 BIT(0) + +#define OUTPUT0_U1P8_MASK 0x7FFF0000 +#define OUTPUT0_U1P8_OFF 16 +#define OUTPUT0_U1P0_MASK 0x00007FFF +#define OUTPUT0_U1P0_OFF 0 +#define OUTPUT1_TEMP_MASK 0xFFFF0000 +#define OUTPUT1_TEMP_OFF 16 +#define OUTPUT1_U2P5_MASK 0x00007FFF +#define OUTPUT1_U2P5_OFF 0 + +/* + * Poll intervals (in milliseconds) + */ +#define MPFS_TVS_MIN_POLL_INTERVAL 2000 + +// The following constant is 273.5 in (16.4) fixedpoint notation +#define MPFS_TVS_MIN_TEMP_IN_K 0x1112 + +typedef struct { + long min; + long actual; + long max; +} mpfs_tvs_sensor_t; + +typedef enum { + SN_V1P05 = 0, + SN_V1P8, + SN_V2P5, + SN_TEMP, + + SN_MAX +} mpfs_tvs_sn_t; + +static const char *mpfs_tvs_voltage_labels[] = { "U1P05", "U1P8", "U2P5" }; + +struct mpfs_tvs { + struct device *dev; + struct device *hwmon_dev; + struct task_struct *poll_task; + bool kthread_running; + long update_interval; /* in milli-seconds */ + mpfs_tvs_sensor_t sensors[SN_MAX]; + void __iomem *ctrl_base; +}; + +static int mpfs_tvs_update_sensors(struct mpfs_tvs *data) { + u32 temp; + u32 work; + + /* read measurement */ + temp = readl(data->ctrl_base + PFSOC_CONTROL_SCB_TVS_OUTPUT1); + work = temp; + + /* compute temperature */ + temp = (temp & OUTPUT1_TEMP_MASK) >> OUTPUT1_TEMP_OFF; + temp = clamp_val(temp, MPFS_TVS_MIN_TEMP_IN_K, INT_MAX); + temp = temp - MPFS_TVS_MIN_TEMP_IN_K; /* Kelvin to Celsius */ + temp = (temp * 1000) >> 4; + data->sensors[SN_TEMP].actual = temp; + data->sensors[SN_TEMP].max = + max(data->sensors[SN_TEMP].actual, data->sensors[SN_TEMP].max); + data->sensors[SN_TEMP].min = + min(data->sensors[SN_TEMP].min, data->sensors[SN_TEMP].actual); + + /* compute voltage */ + work &= OUTPUT1_U2P5_MASK; + work = (1 * work) >> 3; + data->sensors[SN_V2P5].actual = work; + data->sensors[SN_V2P5].max = + max(data->sensors[SN_V2P5].actual, data->sensors[SN_V2P5].max); + data->sensors[SN_V2P5].min = + min(data->sensors[SN_V2P5].min, data->sensors[SN_V2P5].actual); + + temp = readl(data->ctrl_base + PFSOC_CONTROL_SCB_TVS_OUTPUT0); + work = temp; + temp = (OUTPUT0_U1P8_MASK & temp) >> OUTPUT0_U1P8_OFF; + temp = (1 * temp) >> 3; + data->sensors[SN_V1P8].actual = temp; + data->sensors[SN_V1P8].max = + max(data->sensors[SN_V1P8].actual, data->sensors[SN_V1P8].max); + data->sensors[SN_V2P5].min = + min(data->sensors[SN_V1P8].min, data->sensors[SN_V1P8].actual); + + work &= OUTPUT0_U1P0_MASK; + work = (1 * work) >> 3; + data->sensors[SN_V1P05].actual = work; + data->sensors[SN_V1P05].max = + max(data->sensors[SN_V1P05].actual, data->sensors[SN_V1P05].max); + data->sensors[SN_V1P05].min = + min(data->sensors[SN_V1P05].min, data->sensors[SN_V1P05].actual); + + return 0; +} + + +static int mpfs_tvs_chip_read(struct mpfs_tvs *data, long *val) +{ + *val = data->update_interval; + return 0; +} + +static int mpfs_tvs_temp_read(struct mpfs_tvs *data, u32 attr, + int channel, long *val) +{ + switch(attr) { + case hwmon_temp_input: + *val = data->sensors[SN_TEMP].actual; + break; + + case hwmon_temp_max: + *val = data->sensors[SN_TEMP].max; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int mpfs_tvs_voltage_read(struct mpfs_tvs *data, u32 attr, + int channel, long *val) +{ + dev_dbg(data->dev, "read voltage chan %d\n", channel); + switch(attr) { + case hwmon_in_input: + *val = data->sensors[channel].actual; + break; + + case hwmon_in_max: + *val = data->sensors[channel].max; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static ssize_t mpfs_tvs_interval_write(struct mpfs_tvs *data, long val) +{ + data->update_interval = clamp_val(val, + MPFS_TVS_MIN_POLL_INTERVAL, INT_MAX); + return 0; +} + + +static umode_t mpfs_tvs_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if(type == hwmon_chip && attr == hwmon_chip_update_interval) + return 0644; + + if(type == hwmon_temp) { + switch(attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_label: + return 0444; + + default: + return 0; + } + } else if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + return 0444; + + default: + return 0; + } + } + return 0; +} + +static int mpfs_tvs_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct mpfs_tvs *data = dev_get_drvdata(dev); + + switch(type) { + case hwmon_temp: + return mpfs_tvs_temp_read(data, attr, channel, val); + case hwmon_in: + return mpfs_tvs_voltage_read(data, attr, channel, val); + case hwmon_chip: + return mpfs_tvs_chip_read(data, val); + + default: + return -EOPNOTSUPP; + } +} + +static int mpfs_tvs_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct mpfs_tvs *data = dev_get_drvdata(dev); + + switch(type) { + case hwmon_chip: + return mpfs_tvs_interval_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int mpfs_tvs_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, + const char **str) +{ + switch(type) { + case hwmon_temp: + *str = "CPU Temp"; + return 0; + case hwmon_in: + *str = mpfs_tvs_voltage_labels[channel]; + return 0; + default: + return -ENOTSUPP; + } +} + + +static const struct hwmon_ops mpfs_tvs_ops = { + .is_visible = mpfs_tvs_is_visible, + .read_string = mpfs_tvs_read_labels, + .read = mpfs_tvs_read, + .write = mpfs_tvs_write, +}; + +static const struct hwmon_channel_info *mpfs_tvs_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ|HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT|HWMON_T_MIN|HWMON_T_MAX|HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT|HWMON_I_LABEL, + HWMON_I_INPUT|HWMON_I_LABEL, + HWMON_I_INPUT|HWMON_I_LABEL), + NULL +}; + +static const struct hwmon_chip_info mpfs_tvs_chip_info = { + .ops = &mpfs_tvs_ops, + .info = mpfs_tvs_info, +}; + + +static int mpfs_tvs_poll_task(void *ptr) +{ + struct mpfs_tvs *data = ptr; + int ret = 0; + + data->kthread_running = true; + + set_freezable(); + + while(!kthread_should_stop()) { + schedule_timeout_interruptible(data->update_interval); + try_to_freeze(); + ret = mpfs_tvs_update_sensors(data); + if(ret) + break; + } + + data->kthread_running = false; + return ret; +} + +static int mpfs_tvs_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &auxdev->dev; + struct device *hwmon_dev; + struct mpfs_tvs *data; + struct task_struct *task; + int err; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if(!data) + return -ENOMEM; + + data->ctrl_base = (void __iomem *)auxdev->dev.platform_data; + + data->dev = dev; + data->kthread_running = false; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "mpfs_tvs", + data, + &mpfs_tvs_chip_info, + NULL); + + if(IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + dev_err(dev, "Class registration failed (%d)\n", err); + return err; + } + + /* enable HW sensor */ + writel(CTRL_1P05 | CTRL_1P8 | CTRL_2P5 | CTRL_TEMP, + data->ctrl_base + PFSOC_CONTROL_SCB_TVS_CONTROL); + + data->hwmon_dev = hwmon_dev; + data->sensors[SN_TEMP].max = 0; + data->sensors[SN_V1P05].min = + data->sensors[SN_V1P8].min = + data->sensors[SN_V2P5].min = 20000; + data->sensors[SN_V1P05].max = + data->sensors[SN_V1P8].max = + data->sensors[SN_V2P5].max = 0; + data->update_interval = MPFS_TVS_MIN_POLL_INTERVAL; + mpfs_tvs_update_sensors(data); + + task = kthread_run(mpfs_tvs_poll_task, data, "tvs-mpfs-kthread"); + if (IS_ERR(task)) { + err = PTR_ERR(task); + dev_err(dev, "Unable to run kthread err %d\n", err); + return err; + } + + data->poll_task = task; + + dev_info(dev, "Registered MPFS TVS auxiliary driver\n"); + return 0; +} + +static const struct auxiliary_device_id mpfs_tvs_ids[] = { + { + .name = "mailbox_mpfs.tvs-mpfs", + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids); + +static struct auxiliary_driver mpfs_tvs_driver = { + .probe = mpfs_tvs_probe, + .id_table = mpfs_tvs_ids, +}; +module_auxiliary_driver(mpfs_tvs_driver); + +MODULE_AUTHOR("Lars Randers "); +MODULE_DESCRIPTION("MPFS temperature voltage sensor driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index 162df4965..198e6b37d 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -8,6 +8,7 @@ * */ +#include #include #include #include @@ -220,6 +221,64 @@ static const struct mbox_chan_ops mpfs_mbox_ops = { .last_tx_done = mpfs_mbox_last_tx_done, }; +static void mpfs_mbox_auxdev_release(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + + kfree(auxdev); +} + +static struct auxiliary_device *mpfs_mbox_adev_alloc(struct device *parent_dev) +{ + struct auxiliary_device *auxdev; + int ret; + + auxdev = kzalloc(sizeof(*auxdev), GFP_KERNEL); + if(!auxdev) + return ERR_PTR(-ENOMEM); + + auxdev->name = "tvs-mpfs"; + auxdev->dev.parent = parent_dev; + auxdev->dev.release = mpfs_mbox_auxdev_release; + auxdev->id = 10u; + + ret = auxiliary_device_init(auxdev); + if(ret) { + kfree(auxdev); + return ERR_PTR(ret); + } + + return auxdev; +} + +static void mpfs_mbox_unregister_auxdev(void *dev) +{ + struct auxiliary_device *auxdev = dev; + + auxiliary_device_delete(auxdev); + auxiliary_device_uninit(auxdev); +} + +int mpfs_mbox_auxdev_register(struct device *parent_dev, void __iomem *base) +{ + struct auxiliary_device *auxdev; + int ret; + + auxdev = mpfs_mbox_adev_alloc(parent_dev); + if(IS_ERR(auxdev)) + return PTR_ERR(auxdev); + + auxdev->dev.platform_data = base; + + ret = auxiliary_device_add(auxdev); + if(ret) { + auxiliary_device_uninit(auxdev); + return ret; + } + + return devm_add_action_or_reset(parent_dev, mpfs_mbox_unregister_auxdev, auxdev); +} + static int mpfs_mbox_probe(struct platform_device *pdev) { struct mpfs_mbox *mbox; @@ -261,6 +320,9 @@ static int mpfs_mbox_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Registering MPFS mailbox controller failed\n"); return ret; } + + mpfs_mbox_auxdev_register(&pdev->dev, mbox->ctrl_base); + dev_info(&pdev->dev, "Registered MPFS mailbox controller driver\n"); return 0; -- 2.39.2