* Re: [PATCH v5 08/11] leds: rgb: add support for Samsung S2M series PMIC RGB LED device
From: Lee Jones @ 2026-05-07 19:00 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
Jonathan Corbet, Shuah Khan, Nam Tran,
Łukasz Lebiedziński, linux-leds, devicetree,
linux-kernel, linux-pm, linux-samsung-soc, linux-rtc, linux-doc
In-Reply-To: <20260424-s2mu005-pmic-v5-8-fcbc9da5a004@disroot.org>
On Fri, 24 Apr 2026, Kaustabh Chakraborty wrote:
> Add support for the RGB LEDs found in certain Samsung S2M series PMICs.
> The device has three LED channels, controlled as a single device. These
> LEDs are typically used as status indicators in mobile phones.
>
> The driver includes initial support for the S2MU005 PMIC RGB LEDs.
>
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> ---
> drivers/leds/rgb/Kconfig | 11 +
> drivers/leds/rgb/Makefile | 1 +
> drivers/leds/rgb/leds-s2m-rgb.c | 446 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 458 insertions(+)
>
> diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig
> index 28ef4c487367c..30051342f4e4d 100644
> --- a/drivers/leds/rgb/Kconfig
> +++ b/drivers/leds/rgb/Kconfig
> @@ -75,6 +75,17 @@ config LEDS_QCOM_LPG
>
> If compiled as a module, the module will be named leds-qcom-lpg.
>
> +config LEDS_S2M_RGB
> + tristate "Samsung S2M series PMICs RGB LED support"
> + depends on LEDS_CLASS
> + depends on MFD_SEC_CORE
> + select REGMAP_IRQ
> + help
> + This option enables support for the S2MU005 RGB LEDs. These
> + devices have three LED channels, with 8-bit brightness control
> + for each channel. It's usually found in mobile phones as
"The S2MU005 is ..."
> + status indicators.
> +
> config LEDS_MT6370_RGB
> tristate "LED Support for MediaTek MT6370 PMIC"
> depends on MFD_MT6370
> diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile
> index be45991f63f50..98050e1aa4255 100644
> --- a/drivers/leds/rgb/Makefile
> +++ b/drivers/leds/rgb/Makefile
> @@ -6,4 +6,5 @@ obj-$(CONFIG_LEDS_LP5812) += leds-lp5812.o
> obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o
> obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
> obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
> +obj-$(CONFIG_LEDS_S2M_RGB) += leds-s2m-rgb.o
> obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o
> diff --git a/drivers/leds/rgb/leds-s2m-rgb.c b/drivers/leds/rgb/leds-s2m-rgb.c
> new file mode 100644
> index 0000000000000..51d12f2ef762a
> --- /dev/null
> +++ b/drivers/leds/rgb/leds-s2m-rgb.c
> @@ -0,0 +1,446 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RGB LED Driver for Samsung S2M series PMICs.
> + *
> + * Copyright (c) 2015 Samsung Electronics Co., Ltd
> + * Copyright (c) 2026 Kaustabh Chakraborty <kauschluss@disroot.org>
> + */
> +
> +#include <linux/container_of.h>
> +#include <linux/led-class-multicolor.h>
> +#include <linux/mfd/samsung/core.h>
> +#include <linux/mfd/samsung/s2mu005.h>
> +#include <linux/minmax.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +struct s2m_rgb {
> + struct device *dev;
> + struct regmap *regmap;
> + struct led_classdev_mc mc;
> + enum sec_device_type device_type;
> + /*
> + * The mutex object prevents race conditions when evaluation and
> + * application of LED pattern state.
> + */
> + struct mutex lock;
> + /*
> + * State variables representing the current LED pattern, these only to
> + * be accessed when lock is held.
> + */
> + u8 ramp_up;
> + u8 ramp_dn;
> + u8 stay_hi;
> + u8 stay_lo;
> +};
> +
> +static struct led_classdev_mc *to_s2m_mc(struct led_classdev *cdev)
> +{
> + return container_of(cdev, struct led_classdev_mc, led_cdev);
> +}
> +
> +static struct s2m_rgb *to_s2m_rgb(struct led_classdev_mc *mc)
> +{
> + return container_of(mc, struct s2m_rgb, mc);
> +}
> +
> +static const u32 s2mu005_rgb_lut_ramp[] = {
> + 0, 100, 200, 300, 400, 500, 600, 700,
> + 800, 1000, 1200, 1400, 1600, 1800, 2000, 2200,
> +};
> +
> +static const u32 s2mu005_rgb_lut_stay_hi[] = {
> + 100, 200, 300, 400, 500, 750, 1000, 1250,
> + 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250,
> +};
> +
> +static const u32 s2mu005_rgb_lut_stay_lo[] = {
> + 0, 500, 1000, 1500, 2000, 2500, 3000, 3500,
> + 4000, 4500, 5000, 6000, 7000, 8000, 10000, 12000,
> +};
> +
> +static int s2mu005_rgb_apply_params(struct s2m_rgb *rgb)
> +{
> + struct regmap *regmap = rgb->regmap;
> + unsigned int ramp_val = 0;
> + unsigned int stay_val = 0;
> + int ret;
> + int i;
> +
> + ramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_UP, rgb->ramp_up);
> + ramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_DN, rgb->ramp_dn);
> +
> + stay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_HI, rgb->stay_hi);
> + stay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_LO, rgb->stay_lo);
> +
> + ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET);
> + if (ret < 0) {
> + dev_err(rgb->dev, "failed to reset RGB LEDs\n");
> + return ret;
> + }
> +
> + for (i = 0; i < rgb->mc.num_colors; i++) {
for (int i = 0; ...)
> + ret = regmap_write(regmap, S2MU005_REG_RGB_CH_CTRL(i),
> + rgb->mc.subled_info[i].brightness);
> + if (ret < 0) {
> + dev_err(rgb->dev, "failed to set LED brightness\n");
> + return ret;
> + }
> +
> + ret = regmap_write(regmap, S2MU005_REG_RGB_CH_RAMP(i), ramp_val);
> + if (ret < 0) {
> + dev_err(rgb->dev, "failed to set ramp timings\n");
> + return ret;
> + }
> +
> + ret = regmap_write(regmap, S2MU005_REG_RGB_CH_STAY(i), stay_val);
> + if (ret < 0) {
> + dev_err(rgb->dev, "failed to set stay timings\n");
> + return ret;
> + }
> + }
> +
> + ret = regmap_update_bits(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_SLOPE,
> + S2MU005_RGB_SLOPE_SMOOTH);
> + if (ret < 0) {
> + dev_err(rgb->dev, "failed to set ramp slope\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int s2mu005_rgb_reset_params(struct s2m_rgb *rgb)
> +{
> + struct regmap *regmap = rgb->regmap;
> + int ret;
> +
> + ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET);
> + if (ret < 0) {
> + dev_err(rgb->dev, "failed to reset RGB LEDs\n");
> + return ret;
> + }
> +
> + rgb->ramp_up = 0;
> + rgb->ramp_dn = 0;
> + rgb->stay_hi = 0;
> + rgb->stay_lo = 0;
> +
> + return 0;
> +}
> +
> +static int s2m_rgb_lut_calc_timing(const u32 *lut, const size_t len,
> + const u32 req_time, u8 *idx)
> +{
> + int lo = 0;
> + int hi = len - 2;
> +
> + /* Bounds checking */
> + if (req_time < lut[0] || req_time > lut[len - 1])
> + return -EINVAL;
> +
> + /*
> + * Perform a binary search to pick the best timing from the LUT.
> + *
> + * The search algorithm picks two consecutive elements of the
> + * LUT and tries to search the pair between which the requested
> + * time lies.
> + */
> + while (lo <= hi) {
> + *idx = (lo + hi) / 2;
> +
> + if ((lut[*idx] <= req_time) && (req_time <= lut[*idx + 1]))
> + break;
> +
> + if ((req_time < lut[*idx]) && (req_time < lut[*idx + 1]))
> + hi = *idx - 1;
> + else
> + lo = *idx + 1;
> + }
> +
> + /*
> + * The searched timing is always less than the requested time. At
> + * times, the succeeding timing in the LUT is closer thus more
> + * accurate. Adjust the resulting value if that's the case.
> + */
> + if (abs(req_time - lut[*idx]) > abs(lut[*idx + 1] - req_time))
> + (*idx)++;
As much as I appreciate the comments, most of the function is pretty
unreadable. Are you able to use better variable nomenclature and layout
to better describe your aims?
> + return 0;
> +}
> +
> +static int s2m_rgb_pattern_set(struct led_classdev *cdev, struct led_pattern *pattern,
> + u32 len, int repeat)
> +{
> + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));
> + const u32 *lut_ramp_up, *lut_ramp_dn, *lut_stay_hi, *lut_stay_lo;
> + size_t lut_ramp_up_len, lut_ramp_dn_len, lut_stay_hi_len, lut_stay_lo_len;
> + int brightness_peak = 0;
> + u32 time_hi = 0, time_lo = 0;
> + bool ramp_up_en, ramp_dn_en;
> + int ret;
> + int i;
> +
> + /*
> + * The typical pattern supported by this device can be
> + * represented with the following graph:
> + *
> + * 255 T ''''''-. .-'''''''-.
> + * | '. .' '.
> + * | \ / \
> + * | '. .' '.
> + * | '-...........-' '-
> + * 0 +----------------------------------------------------> time (s)
> + *
> + * <---- HIGH ----><-- LOW --><-------- HIGH --------->
> + * <-----><-------><---------><-------><-----><------->
> + * stay_hi ramp_dn stay_lo ramp_up stay_hi ramp_dn
> + *
> + * There are two states, named HIGH and LOW. HIGH has a non-zero
> + * brightness level, while LOW is of zero brightness. The
> + * pattern provided should mention only one zero and non-zero
> + * brightness level. The hardware always starts the pattern from
> + * the HIGH state, as shown in the graph.
> + *
> + * The HIGH state can be divided in three somewhat equal timings:
> + * ramp_up, stay_hi, and ramp_dn. The LOW state has only one
> + * timing: stay_lo.
> + */
> +
> + /* Only indefinitely looping patterns are supported. */
> + if (repeat != -1)
> + return -EINVAL;
> +
> + /* Pattern should consist of at least two tuples. */
> + if (len < 2)
> + return -EINVAL;
> +
> + for (i = 0; i < len; i++) {
for (int i = 0; ...) would be preferable.
> + int brightness = pattern[i].brightness;
> + u32 delta_t = pattern[i].delta_t;
> +
> + if (brightness) {
> + /*
> + * The pattern shold define only one non-zero
> + * brightness in the HIGH state. The device
> + * doesn't have any provisions to handle
> + * multiple peak brightness levels.
> + */
> + if (brightness_peak && brightness_peak != brightness)
> + return -EINVAL;
> +
> + brightness_peak = brightness;
> + time_hi += delta_t;
> + ramp_dn_en = !!delta_t;
> + } else {
> + time_lo += delta_t;
> + ramp_up_en = !!delta_t;
> + }
> + }
> +
> + switch (rgb->device_type) {
> + case S2MU005:
> + lut_ramp_up = s2mu005_rgb_lut_ramp;
> + lut_ramp_up_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp);
> + lut_ramp_dn = s2mu005_rgb_lut_ramp;
> + lut_ramp_dn_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp);
> + lut_stay_hi = s2mu005_rgb_lut_stay_hi;
> + lut_stay_hi_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_hi);
> + lut_stay_lo = s2mu005_rgb_lut_stay_lo;
> + lut_stay_lo_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_lo);
> + break;
> + default:
> + /* execution shouldn't reach here */
Instead of a comment, perhaps a WARN_ON_ONCE(1); or similar would be
more robust here to catch unexpected device types?
> + break;
> + }
> +
> + mutex_lock(&rgb->lock);
> +
> + /*
> + * The timings ramp_up, stay_hi, and ramp_dn of the HIGH state
> + * are roughly equal. Firstly, calculate and set timings for
> + * ramp_up and ramp_dn (making sure they're exactly equal).
> + */
> + rgb->ramp_up = 0;
> + rgb->ramp_dn = 0;
> +
> + if (ramp_up_en) {
> + ret = s2m_rgb_lut_calc_timing(lut_ramp_up, lut_ramp_up_len, time_hi / 3,
> + &rgb->ramp_up);
> + if (ret < 0)
> + goto param_fail;
> + }
> +
> + if (ramp_dn_en) {
> + ret = s2m_rgb_lut_calc_timing(lut_ramp_dn, lut_ramp_dn_len, time_hi / 3,
> + &rgb->ramp_dn);
> + if (ret < 0)
> + goto param_fail;
> + }
> +
> + /*
> + * Subtract the allocated ramp timings from time_hi (and also
> + * making sure it doesn't underflow!). The remaining time is
> + * allocated to stay_hi.
> + */
> + time_hi -= min(time_hi, lut_ramp_up[rgb->ramp_up]);
> + time_hi -= min(time_hi, lut_ramp_dn[rgb->ramp_dn]);
> +
> + ret = s2m_rgb_lut_calc_timing(lut_stay_hi, lut_stay_hi_len, time_hi, &rgb->stay_hi);
> + if (ret < 0)
> + goto param_fail;
> +
> + ret = s2m_rgb_lut_calc_timing(lut_stay_lo, lut_stay_lo_len, time_lo, &rgb->stay_lo);
> + if (ret < 0)
> + goto param_fail;
> +
> + led_mc_calc_color_components(&rgb->mc, brightness_peak);
> + switch (rgb->device_type) {
> + case S2MU005:
> + ret = s2mu005_rgb_apply_params(rgb);
> + break;
> + default:
> + /* execution shouldn't reach here */
> + break;
> + }
> + if (ret < 0)
> + goto param_fail;
Are we expecting positive values in these 'ret's?
If not if (!ret) will do.
> +
> + mutex_unlock(&rgb->lock);
> +
> + return 0;
> +
> +param_fail:
> + rgb->ramp_up = 0;
> + rgb->ramp_dn = 0;
> + rgb->stay_hi = 0;
> + rgb->stay_lo = 0;
> +
> + mutex_unlock(&rgb->lock);
> +
> + return ret;
> +}
> +
> +static int s2m_rgb_pattern_clear(struct led_classdev *cdev)
> +{
> + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));
> + int ret = 0;
> +
> + mutex_lock(&rgb->lock);
> +
> + switch (rgb->device_type) {
> + case S2MU005:
> + ret = s2mu005_rgb_reset_params(rgb);
> + break;
> + default:
> + /* execution shouldn't reach here */
> + break;
As above.
And a single branch switch () makes little sense.
> + }
> +
> + mutex_unlock(&rgb->lock);
> +
> + return ret;
> +}
> +
> +static int s2m_rgb_brightness_set(struct led_classdev *cdev, enum led_brightness value)
> +{
> + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));
> + int ret = 0;
> +
> + if (!value)
> + return s2m_rgb_pattern_clear(cdev);
> +
> + mutex_lock(&rgb->lock);
> +
> + led_mc_calc_color_components(&rgb->mc, value);
> + switch (rgb->device_type) {
> + case S2MU005:
> + ret = s2mu005_rgb_apply_params(rgb);
> + break;
> + default:
> + /* execution shouldn't reach here */
> + break;
> + }
> +
> + mutex_unlock(&rgb->lock);
> +
> + return ret;
> +}
> +
> +static struct mc_subled s2mu005_rgb_subled_info[] = {
const?
> + { .channel = 0, .color_index = LED_COLOR_ID_BLUE },
> + { .channel = 1, .color_index = LED_COLOR_ID_GREEN },
> + { .channel = 2, .color_index = LED_COLOR_ID_RED },
> +};
> +
> +static int s2m_rgb_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent);
> + struct s2m_rgb *rgb;
> + struct led_init_data init_data = {};
> + int ret;
> +
> + rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL);
> + if (!rgb)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, rgb);
> + rgb->dev = dev;
> + rgb->regmap = pmic_drvdata->regmap_pmic;
> + rgb->device_type = platform_get_device_id(pdev)->driver_data;
We don't tend to use these object oriented-type constructs in the
kernel. Also, we have helper functions of extracting driver_data.
Please use them.
> +
> + switch (rgb->device_type) {
> + case S2MU005:
> + rgb->mc.subled_info = s2mu005_rgb_subled_info;
> + rgb->mc.num_colors = ARRAY_SIZE(s2mu005_rgb_subled_info);
> + break;
> + default:
> + return dev_err_probe(dev, -ENODEV, "device type %d is not supported by driver\n",
> + pmic_drvdata->device_type);
Small point, but for consistency, would it be better to print
`rgb->device_type` here, since that is the value being checked in the
switch statement?
Also, same single branch comment as before.
> + }
> +
> + rgb->mc.led_cdev.max_brightness = 255;
> + rgb->mc.led_cdev.brightness_set_blocking = s2m_rgb_brightness_set;
> + rgb->mc.led_cdev.pattern_set = s2m_rgb_pattern_set;
> + rgb->mc.led_cdev.pattern_clear = s2m_rgb_pattern_clear;
> +
> + ret = devm_mutex_init(dev, &rgb->lock);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to create mutex lock\n");
> +
> + init_data.fwnode = of_fwnode_handle(dev->of_node);
> + ret = devm_led_classdev_multicolor_register_ext(dev, &rgb->mc, &init_data);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to create LED device\n");
> +
> + return 0;
> +}
> +
> +static const struct platform_device_id s2m_rgb_id_table[] = {
> + { "s2mu005-rgb", S2MU005 },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(platform, s2m_rgb_id_table);
> +
> +static const struct of_device_id s2m_rgb_of_match_table[] = {
> + { .compatible = "samsung,s2mu005-rgb", .data = (void *)S2MU005 },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, s2m_rgb_of_match_table);
> +
> +static struct platform_driver s2m_rgb_driver = {
> + .driver = {
> + .name = "s2m-rgb",
> + },
> + .probe = s2m_rgb_probe,
> + .id_table = s2m_rgb_id_table,
> +};
> +module_platform_driver(s2m_rgb_driver);
> +
> +MODULE_DESCRIPTION("RGB LED Driver For Samsung S2M Series PMICs");
"for"
> +MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
> +MODULE_LICENSE("GPL");
--
Lee Jones
^ permalink raw reply
* Re: [PATCH v5 07/11] leds: flash: add support for Samsung S2M series PMIC flash LED device
From: Jacek Anaszewski @ 2026-05-07 19:39 UTC (permalink / raw)
To: Lee Jones, Kaustabh Chakraborty
Cc: Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
Jonathan Corbet, Shuah Khan, Nam Tran,
Łukasz Lebiedziński, linux-leds, devicetree,
linux-kernel, linux-pm, linux-samsung-soc, linux-rtc, linux-doc
In-Reply-To: <20260507164654.GS305027@google.com>
Hi Lee,
On 5/7/26 6:46 PM, Lee Jones wrote:
> On Fri, 24 Apr 2026, Kaustabh Chakraborty wrote:
>
>> Add support for flash LEDs in certain Samsung S2M series PMICs.
>> The device has two channels for LEDs, typically for the back and front
>> cameras in mobile devices. Both channels can be independently
>> controlled, and can be operated in torch or flash modes.
>>
>> The driver includes initial support for the S2MU005 PMIC flash LEDs.
>>
>> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
>> ---
>> drivers/leds/flash/Kconfig | 12 ++
>> drivers/leds/flash/Makefile | 1 +
>> drivers/leds/flash/leds-s2m-flash.c | 358 ++++++++++++++++++++++++++++++++++++
>> 3 files changed, 371 insertions(+)
>>
>> diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
>> index 5e08102a67841..be62e05277429 100644
>> --- a/drivers/leds/flash/Kconfig
>> +++ b/drivers/leds/flash/Kconfig
>> @@ -114,6 +114,18 @@ config LEDS_RT8515
>> To compile this driver as a module, choose M here: the module
>> will be called leds-rt8515.
>>
>> +config LEDS_S2M_FLASH
>> + tristate "Samsung S2M series PMICs flash/torch LED support"
>> + depends on LEDS_CLASS
>> + depends on MFD_SEC_CORE
>> + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
>
> The `|| !V4L2_FLASH_LED_CLASS` part of this dependency makes it
> unconditionally true. Was this intended? Perhaps this dependency can be
> removed entirely.
This is for a reason to allow building the driver if
V4L2_FLASH_LED_CLASS is turned off, or build it as a module
if V4L2_FLASH_LED_CLASS=m. You will get nice explanation from
Google AI if you type just
"V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS".
See e.g. [0], which fixes undefined symbol error by adding this.
[0]
https://git.paulk.fr/projects/linux.git/commit/drivers?h=sunxi/cedrus/jpeg-nv16&id=dbeb02a0bc41b9e9b9c05e460890351efecf1352
--
Best regards,
Jacek Anaszewski
^ permalink raw reply
* Re: [PATCH] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Ronan Dalton @ 2026-05-07 23:44 UTC (permalink / raw)
To: code@tyhicks.com
Cc: sashal@kernel.org, giometti@enneenne.com,
meaganlloyd@linux.microsoft.com, linux-rtc@vger.kernel.org,
linux-kernel@vger.kernel.org, alexandre.belloni@bootlin.com,
Chris Packham
In-Reply-To: <afytZzYGaSG-6V6y@yaupon>
On Thu, 2026-05-07 at 10:19 -0500, Tyler Hicks wrote:
> On 2026-05-01 16:46:10, Ronan Dalton wrote:
> > Prior to commit 6cb0d8587b96 ("rtc: ds1307: remove clear of
> > oscillator
>
> This commit hash is from the linux-6.12.y stable branch but we should
> use hashes from Linus' tree in this commit message:
>
> 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag
> (OSF) in probe")
> >
> > The commit associated with the previous commit, ae03a28e12a7 ("rtc:
>
> The commit hash referenced here should be 523923cfd5d6.
> >
> > Fixes: 6cb0d8587b96 ("rtc: ds1307: remove clear of oscillator stop
> > flag (OSF) in probe")
>
> Please adjust the commit hash here, as well. Everything else looks
> good.
Good catch. I'll send a follow-up patch with these commit hashes fixed.
>
> Reviewed-by: Tyler Hicks <code@thicks.com>
>
> Tyler
Cheers
>
^ permalink raw reply
* [PATCH v2] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Ronan Dalton @ 2026-05-08 0:17 UTC (permalink / raw)
To: alexandre.belloni
Cc: Ronan Dalton, linux-rtc, linux-kernel, Tyler Hicks, Sasha Levin,
Meagan Lloyd, Rodolfo Giometti, Chris Packham
Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator
stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was
checked during device probe for the ds1337, ds1339, ds1341, and ds3231
chips; if it was set, it would be cleared and a warning would be logged
saying "SET TIME!". Since that commit, the OSF bit is no longer cleared,
but the warning is still printed.
Directly following that commit, there was no way to get rid of this
warning because nothing cleared the OSF bit on these chips.
The commit associated with the previous commit, 523923cfd5d6 ("rtc:
ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use
of the OSF when getting and setting the time in the RTC. However, the
other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding
change made.
Given that the OSF bit is no longer cleared at probe time when it is
set, the remaining three chips should have the same handling as the
ds1341 chip has for the OSF bit.
Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the
same logic as the ds1341 has to these chips.
Note that any devices brought up between the first referenced commit and
this one may begin mistrusting the time reported by the RTC until it is
set again, if the bit was never explicitly cleared.
Note that only the ds1339 was tested with this change, but the
datasheets for the other chips contain essentially identical
descriptions of the OSF bit so the same change should work.
An alternative to this change could be just to revert the referenced two
commits and not use the OSF bit at all, apart from logging a warning and
clearing it on probe.
Signed-off-by: Ronan Dalton <ronan.dalton@alliedtelesis.co.nz>
Cc: linux-rtc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Tyler Hicks <code@tyhicks.com>
Cc: Sasha Levin <sashal@kernel.org>
Cc: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
Cc: Rodolfo Giometti <giometti@enneenne.com>
Cc: Chris Packham <chris.packham@alliedtelesis.co.nz>
Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe")
---
Changes in v2:
- Fix hashes of referenced commits
drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 7205c59ff729..edf81b975dec 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1338_BIT_OSF)
return -EINVAL;
break;
+ case ds_1337:
+ case ds_1339:
+ case ds_1341:
+ case ds_3231:
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1337_BIT_OSF)
+ return -EINVAL;
+ break;
case ds_1340:
if (tmp & DS1340_BIT_nEOSC)
return -EINVAL;
@@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1340_BIT_OSF)
return -EINVAL;
break;
- case ds_1341:
- ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
- if (ret)
- return ret;
- if (tmp & DS1337_BIT_OSF)
- return -EINVAL;
- break;
case ds_1388:
ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
if (ret)
@@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
DS1338_BIT_OSF, 0);
break;
+ case ds_1337:
+ case ds_1339:
+ case ds_1341:
+ case ds_3231:
+ regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
+ DS1337_BIT_OSF, 0);
+ break;
case ds_1340:
regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
DS1340_BIT_OSF, 0);
break;
- case ds_1341:
- regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
- DS1337_BIT_OSF, 0);
- break;
case ds_1388:
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_OSF, 0);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Meagan Lloyd @ 2026-05-08 2:09 UTC (permalink / raw)
To: Ronan Dalton, alexandre.belloni
Cc: linux-rtc, linux-kernel, Tyler Hicks, Sasha Levin,
Rodolfo Giometti, Chris Packham
In-Reply-To: <20260501044657.1003980-2-ronan.dalton@alliedtelesis.co.nz>
On 4/30/2026 9:46 PM, Ronan Dalton wrote:
> An alternative to this change could be just to revert the referenced two
> commits and not use the OSF bit at all, apart from logging a warning and
> clearing it on probe.
Can you remove this from the commit message?
^ permalink raw reply
* [PATCH v3] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Ronan Dalton @ 2026-05-08 3:24 UTC (permalink / raw)
To: alexandre.belloni
Cc: Ronan Dalton, linux-rtc, linux-kernel, Tyler Hicks, Sasha Levin,
Meagan Lloyd, Rodolfo Giometti, Chris Packham
Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator
stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was
checked during device probe for the ds1337, ds1339, ds1341, and ds3231
chips; if it was set, it would be cleared and a warning would be logged
saying "SET TIME!". Since that commit, the OSF bit is no longer cleared,
but the warning is still printed.
Directly following that commit, there was no way to get rid of this
warning because nothing cleared the OSF bit on these chips.
The commit associated with the previous commit, 523923cfd5d6 ("rtc:
ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use
of the OSF when getting and setting the time in the RTC. However, the
other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding
change made.
Given that the OSF bit is no longer cleared at probe time when it is
set, the remaining three chips should have the same handling as the
ds1341 chip has for the OSF bit.
Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the
same logic as the ds1341 has to these chips.
Note that any devices brought up between the first referenced commit and
this one may begin mistrusting the time reported by the RTC until it is
set again, if the bit was never explicitly cleared.
Note that only the ds1339 was tested with this change, but the
datasheets for the other chips contain essentially identical
descriptions of the OSF bit so the same change should work.
Signed-off-by: Ronan Dalton <ronan.dalton@alliedtelesis.co.nz>
Cc: linux-rtc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Tyler Hicks <code@tyhicks.com>
Cc: Sasha Levin <sashal@kernel.org>
Cc: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
Cc: Rodolfo Giometti <giometti@enneenne.com>
Cc: Chris Packham <chris.packham@alliedtelesis.co.nz>
Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe")
---
Changes in v3:
- Remove paragraph mentioning alternative fix from commit message
Changes in v2:
- Fix hashes of referenced commits
drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 7205c59ff729..edf81b975dec 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1338_BIT_OSF)
return -EINVAL;
break;
+ case ds_1337:
+ case ds_1339:
+ case ds_1341:
+ case ds_3231:
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1337_BIT_OSF)
+ return -EINVAL;
+ break;
case ds_1340:
if (tmp & DS1340_BIT_nEOSC)
return -EINVAL;
@@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1340_BIT_OSF)
return -EINVAL;
break;
- case ds_1341:
- ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
- if (ret)
- return ret;
- if (tmp & DS1337_BIT_OSF)
- return -EINVAL;
- break;
case ds_1388:
ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
if (ret)
@@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
DS1338_BIT_OSF, 0);
break;
+ case ds_1337:
+ case ds_1339:
+ case ds_1341:
+ case ds_3231:
+ regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
+ DS1337_BIT_OSF, 0);
+ break;
case ds_1340:
regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
DS1340_BIT_OSF, 0);
break;
- case ds_1341:
- regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
- DS1337_BIT_OSF, 0);
- break;
case ds_1388:
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_OSF, 0);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Ronan Dalton @ 2026-05-08 3:27 UTC (permalink / raw)
To: meaganlloyd@linux.microsoft.com, alexandre.belloni@bootlin.com
Cc: sashal@kernel.org, code@tyhicks.com, giometti@enneenne.com,
linux-rtc@vger.kernel.org, linux-kernel@vger.kernel.org,
Chris Packham
In-Reply-To: <0ef9de5c-4b1d-461a-98e9-b04fb430fdde@linux.microsoft.com>
On Thu, 2026-05-07 at 19:09 -0700, Meagan Lloyd wrote:
>
> On 4/30/2026 9:46 PM, Ronan Dalton wrote:
> > An alternative to this change could be just to revert the
> > referenced two
> > commits and not use the OSF bit at all, apart from logging a
> > warning and
> > clearing it on probe.
> Can you remove this from the commit message?
Done.
^ permalink raw reply
* Re: [PATCH v3] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Tyler Hicks @ 2026-05-08 15:00 UTC (permalink / raw)
To: Ronan Dalton
Cc: alexandre.belloni, linux-rtc, linux-kernel, Sasha Levin,
Meagan Lloyd, Rodolfo Giometti, Chris Packham
In-Reply-To: <20260508032518.3696705-2-ronan.dalton@alliedtelesis.co.nz>
On 2026-05-08 15:24:49, Ronan Dalton wrote:
> Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator
> stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was
> checked during device probe for the ds1337, ds1339, ds1341, and ds3231
> chips; if it was set, it would be cleared and a warning would be logged
> saying "SET TIME!". Since that commit, the OSF bit is no longer cleared,
> but the warning is still printed.
>
> Directly following that commit, there was no way to get rid of this
> warning because nothing cleared the OSF bit on these chips.
>
> The commit associated with the previous commit, 523923cfd5d6 ("rtc:
> ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use
> of the OSF when getting and setting the time in the RTC. However, the
> other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding
> change made.
>
> Given that the OSF bit is no longer cleared at probe time when it is
> set, the remaining three chips should have the same handling as the
> ds1341 chip has for the OSF bit.
>
> Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the
> same logic as the ds1341 has to these chips.
>
> Note that any devices brought up between the first referenced commit and
> this one may begin mistrusting the time reported by the RTC until it is
> set again, if the bit was never explicitly cleared.
>
> Note that only the ds1339 was tested with this change, but the
> datasheets for the other chips contain essentially identical
> descriptions of the OSF bit so the same change should work.
>
> Signed-off-by: Ronan Dalton <ronan.dalton@alliedtelesis.co.nz>
> Cc: linux-rtc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
> Cc: Tyler Hicks <code@tyhicks.com>
> Cc: Sasha Levin <sashal@kernel.org>
> Cc: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> Cc: Rodolfo Giometti <giometti@enneenne.com>
> Cc: Chris Packham <chris.packham@alliedtelesis.co.nz>
> Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe")
> ---
> Changes in v3:
> - Remove paragraph mentioning alternative fix from commit message
>
> Changes in v2:
> - Fix hashes of referenced commits
Reviewed-by: Tyler Hicks <code@tyhicks.com>
Thanks again!
Tyler
>
> drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++-----------
> 1 file changed, 17 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
> index 7205c59ff729..edf81b975dec 100644
> --- a/drivers/rtc/rtc-ds1307.c
> +++ b/drivers/rtc/rtc-ds1307.c
> @@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
> if (tmp & DS1338_BIT_OSF)
> return -EINVAL;
> break;
> + case ds_1337:
> + case ds_1339:
> + case ds_1341:
> + case ds_3231:
> + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
> + if (ret)
> + return ret;
> + if (tmp & DS1337_BIT_OSF)
> + return -EINVAL;
> + break;
> case ds_1340:
> if (tmp & DS1340_BIT_nEOSC)
> return -EINVAL;
> @@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
> if (tmp & DS1340_BIT_OSF)
> return -EINVAL;
> break;
> - case ds_1341:
> - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
> - if (ret)
> - return ret;
> - if (tmp & DS1337_BIT_OSF)
> - return -EINVAL;
> - break;
> case ds_1388:
> ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
> if (ret)
> @@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
> regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
> DS1338_BIT_OSF, 0);
> break;
> + case ds_1337:
> + case ds_1339:
> + case ds_1341:
> + case ds_3231:
> + regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
> + DS1337_BIT_OSF, 0);
> + break;
> case ds_1340:
> regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
> DS1340_BIT_OSF, 0);
> break;
> - case ds_1341:
> - regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
> - DS1337_BIT_OSF, 0);
> - break;
> case ds_1388:
> regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
> DS1388_BIT_OSF, 0);
> --
> 2.53.0
>
^ permalink raw reply
* Re: [PATCH v5 08/11] leds: rgb: add support for Samsung S2M series PMIC RGB LED device
From: Kaustabh Chakraborty @ 2026-05-08 17:27 UTC (permalink / raw)
To: Lee Jones, Kaustabh Chakraborty
Cc: Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
Jonathan Corbet, Shuah Khan, Nam Tran,
Łukasz Lebiedziński, linux-leds, devicetree,
linux-kernel, linux-pm, linux-samsung-soc, linux-rtc, linux-doc
In-Reply-To: <20260507190005.GT305027@google.com>
On 2026-05-07 20:00 +01:00, Lee Jones wrote:
> On Fri, 24 Apr 2026, Kaustabh Chakraborty wrote:
[...]
>> +
>> + switch (rgb->device_type) {
>> + case S2MU005:
>> + lut_ramp_up = s2mu005_rgb_lut_ramp;
>> + lut_ramp_up_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp);
>> + lut_ramp_dn = s2mu005_rgb_lut_ramp;
>> + lut_ramp_dn_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp);
>> + lut_stay_hi = s2mu005_rgb_lut_stay_hi;
>> + lut_stay_hi_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_hi);
>> + lut_stay_lo = s2mu005_rgb_lut_stay_lo;
>> + lut_stay_lo_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_lo);
>> + break;
>> + default:
>> + /* execution shouldn't reach here */
>
> Instead of a comment, perhaps a WARN_ON_ONCE(1); or similar would be
> more robust here to catch unexpected device types?
>
[...]
>> +static int s2m_rgb_pattern_clear(struct led_classdev *cdev)
>> +{
>> + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));
>> + int ret = 0;
>> +
>> + mutex_lock(&rgb->lock);
>> +
>> + switch (rgb->device_type) {
>> + case S2MU005:
>> + ret = s2mu005_rgb_reset_params(rgb);
>> + break;
>> + default:
>> + /* execution shouldn't reach here */
>> + break;
>
> As above.
>
> And a single branch switch () makes little sense.
Even with an `if`, since only one variant is supported we're sure that
the control would never go to `else` anyway. I will flatten this block,
and expect the switch to be added when another variant is added.
>> +static struct mc_subled s2mu005_rgb_subled_info[] = {
>
> const?
No, this is fed to (struct led_classdev_mc)::subled_info, which is not a
const pointer. Relevant snip is marked below.
"Assigning to 'struct mc_subled *' from const struct mc_subled[3]
discards qualifiers."
>> + { .channel = 0, .color_index = LED_COLOR_ID_BLUE },
>> + { .channel = 1, .color_index = LED_COLOR_ID_GREEN },
>> + { .channel = 2, .color_index = LED_COLOR_ID_RED },
>> +};
[...]
>> + switch (rgb->device_type) {
>> + case S2MU005:
>> + rgb->mc.subled_info = s2mu005_rgb_subled_info;
Here.
>> + rgb->mc.num_colors = ARRAY_SIZE(s2mu005_rgb_subled_info);
>> + break;
>> + default:
>> + return dev_err_probe(dev, -ENODEV, "device type %d is not supported by driver\n",
>> + pmic_drvdata->device_type);
^ permalink raw reply
* [PATCH v2] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Udaya Kiran Challa @ 2026-05-09 9:00 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: skhan, me, linux-rtc, devicetree, linux-kernel,
Udaya Kiran Challa
Convert the Epson RX6110 Real Time Clock devicetree binding
from the legacy text format to DT schema.
Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
---
Changelog:
Changes since v1:
- Reuse common SPI peripheral properties
- Drop redundant SPI-specific comment
- Remove unused labels from examples
Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
---
.../devicetree/bindings/rtc/epson,rx6110.yaml | 23 +++++++------------
1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
index 32d15a014f91..b51d39ef75e4 100644
--- a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
+++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
@@ -14,6 +14,9 @@ description: |
maintainers:
- Alexandre Belloni <alexandre.belloni@bootlin.com>
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
properties:
compatible:
const: epson,rx6110
@@ -21,24 +24,14 @@ properties:
reg:
maxItems: 1
- # SPI-specific properties
- spi-cs-high:
- type: boolean
- description: RX6110 needs chipselect high
-
- spi-cpha:
- type: boolean
- description: RX6110 works with SPI shifted clock phase
-
- spi-cpol:
- type: boolean
- description: RX6110 works with SPI inverse clock polarity
+ spi-cpha: true
+ spi-cpol: true
required:
- compatible
- reg
-additionalProperties: false
+unevaluatedProperties: false
examples:
# I2C mode
@@ -47,7 +40,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- rtc1: rtc@32 {
+ rtc@32 {
compatible = "epson,rx6110";
reg = <0x32>;
};
@@ -59,7 +52,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- rtc2: rtc@3 {
+ rtc@3 {
compatible = "epson,rx6110";
reg = <3>;
spi-cs-high;
--
2.43.0
^ permalink raw reply related
* [PATCH v2] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Udaya Kiran Challa @ 2026-05-09 9:47 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: skhan, me, linux-rtc, devicetree, linux-kernel,
Udaya Kiran Challa
Convert the Epson RX6110 Real Time Clock devicetree binding
from the legacy text format to DT schema.
Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
---
Changelog:
Changes since v1:
- Reuse common SPI peripheral properties
- Drop redundant SPI-specific comment
- Remove unused labels from examples
Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
---
.../devicetree/bindings/rtc/epson,rx6110.txt | 39 ------------
.../devicetree/bindings/rtc/epson,rx6110.yaml | 62 +++++++++++++++++++
2 files changed, 62 insertions(+), 39 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.txt
create mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
deleted file mode 100644
index 3dc313e01f77..000000000000
--- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Epson RX6110 Real Time Clock
-============================
-
-The Epson RX6110 can be used with SPI or I2C busses. The kind of
-bus depends on the SPISEL pin and can not be configured via software.
-
-I2C mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg : the I2C address of the device for I2C
-
-Example:
-
- rtc: rtc@32 {
- compatible = "epson,rx6110"
- reg = <0x32>;
- };
-
-SPI mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg: chip select number
- - spi-cs-high: RX6110 needs chipselect high
- - spi-cpha: RX6110 works with SPI shifted clock phase
- - spi-cpol: RX6110 works with SPI inverse clock polarity
-
-Example:
-
- rtc: rtc@3 {
- compatible = "epson,rx6110"
- reg = <3>
- spi-cs-high;
- spi-cpha;
- spi-cpol;
- };
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
new file mode 100644
index 000000000000..b51d39ef75e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Epson RX6110 Real Time Clock
+
+description: |
+ The Epson RX6110 can be used with SPI or I2C busses.
+ The kind of bus depends on the SPISEL pin and can not be
+ configured via software.
+
+maintainers:
+ - Alexandre Belloni <alexandre.belloni@bootlin.com>
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: epson,rx6110
+
+ reg:
+ maxItems: 1
+
+ spi-cpha: true
+ spi-cpol: true
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ # I2C mode
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@32 {
+ compatible = "epson,rx6110";
+ reg = <0x32>;
+ };
+ };
+
+ # SPI mode
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@3 {
+ compatible = "epson,rx6110";
+ reg = <3>;
+ spi-cs-high;
+ spi-cpha;
+ spi-cpol;
+ };
+ };
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Conor Dooley @ 2026-05-09 15:58 UTC (permalink / raw)
To: Udaya Kiran Challa
Cc: alexandre.belloni, robh, krzk+dt, conor+dt, skhan, me, linux-rtc,
devicetree, linux-kernel
In-Reply-To: <20260509090051.77603-1-challauday369@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 567 bytes --]
On Sat, May 09, 2026 at 02:30:51PM +0530, Udaya Kiran Challa wrote:
> Convert the Epson RX6110 Real Time Clock devicetree binding
> from the legacy text format to DT schema.
>
> Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
> ---
> Changelog:
> Changes since v1:
> - Reuse common SPI peripheral properties
> - Drop redundant SPI-specific comment
> - Remove unused labels from examples
>
> Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
Seemingly there are 2 v2s of this.
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH v3] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Udaya Kiran Challa @ 2026-05-09 18:18 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: skhan, me, linux-rtc, devicetree, linux-kernel,
Udaya Kiran Challa
Convert the Epson RX6110 Real Time Clock devicetree binding
from the legacy text format to DT schema.
Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
---
Changelog:
Changes since v2:
- Reference rtc.yaml for common RTC properties
- Add conditional validation for SPI mode properties
Link to v2:https://lore.kernel.org/all/20260509095713.5818-1-challauday369@gmail.com/
Changes since v1:
- Reuse common SPI peripheral properties
- Drop redundant SPI-specific comment
- Remove unused labels from examples
Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
---
.../devicetree/bindings/rtc/epson,rx6110.txt | 39 ---------
.../devicetree/bindings/rtc/epson,rx6110.yaml | 79 +++++++++++++++++++
2 files changed, 79 insertions(+), 39 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.txt
create mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
deleted file mode 100644
index 3dc313e01f77..000000000000
--- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Epson RX6110 Real Time Clock
-============================
-
-The Epson RX6110 can be used with SPI or I2C busses. The kind of
-bus depends on the SPISEL pin and can not be configured via software.
-
-I2C mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg : the I2C address of the device for I2C
-
-Example:
-
- rtc: rtc@32 {
- compatible = "epson,rx6110"
- reg = <0x32>;
- };
-
-SPI mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg: chip select number
- - spi-cs-high: RX6110 needs chipselect high
- - spi-cpha: RX6110 works with SPI shifted clock phase
- - spi-cpol: RX6110 works with SPI inverse clock polarity
-
-Example:
-
- rtc: rtc@3 {
- compatible = "epson,rx6110"
- reg = <3>
- spi-cs-high;
- spi-cpha;
- spi-cpol;
- };
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
new file mode 100644
index 000000000000..09578de45d8d
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Epson RX6110 Real Time Clock
+
+description: |
+ The Epson RX6110 can be used with SPI or I2C busses.
+ The kind of bus depends on the SPISEL pin and can not be
+ configured via software.
+
+maintainers:
+ - Alexandre Belloni <alexandre.belloni@bootlin.com>
+
+allOf:
+ - $ref: rtc.yaml#
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: epson,rx6110
+
+ reg:
+ maxItems: 1
+
+ spi-cs-high: true
+ spi-cpha: true
+ spi-cpol: true
+
+required:
+ - compatible
+ - reg
+
+if:
+ oneOf:
+ - required:
+ - spi-cs-high
+ - required:
+ - spi-cpha
+ - required:
+ - spi-cpol
+
+then:
+ required:
+ - spi-cs-high
+ - spi-cpha
+ - spi-cpol
+
+unevaluatedProperties: false
+
+examples:
+ # I2C mode
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@32 {
+ compatible = "epson,rx6110";
+ reg = <0x32>;
+ };
+ };
+
+ # SPI mode
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@3 {
+ compatible = "epson,rx6110";
+ reg = <3>;
+ spi-cs-high;
+ spi-cpha;
+ spi-cpol;
+ };
+ };
--
2.43.0
^ permalink raw reply related
* [PATCH v4] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Udaya Kiran Challa @ 2026-05-09 18:56 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: skhan, me, linux-rtc, devicetree, linux-kernel,
Udaya Kiran Challa
Convert the Epson RX6110 Real Time Clock devicetree binding
from the legacy text format to DT schema.
Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
---
Changelog:
Changes since v3:
- Add conditional validation for SPI mode properties using anyOf
Link to v3:https://lore.kernel.org/all/20260509181909.21871-1-challauday369@gmail.com/
Changes since v2:
- Reference rtc.yaml for common RTC properties
- Add conditional validation for SPI mode properties
Link to v2:https://lore.kernel.org/all/20260509095713.5818-1-challauday369@gmail.com/
Changes since v1:
- Reuse common SPI peripheral properties
- Drop redundant SPI-specific comment
- Remove unused labels from examples
Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
---
.../devicetree/bindings/rtc/epson,rx6110.txt | 39 ---------
.../devicetree/bindings/rtc/epson,rx6110.yaml | 79 +++++++++++++++++++
2 files changed, 79 insertions(+), 39 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.txt
create mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
deleted file mode 100644
index 3dc313e01f77..000000000000
--- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Epson RX6110 Real Time Clock
-============================
-
-The Epson RX6110 can be used with SPI or I2C busses. The kind of
-bus depends on the SPISEL pin and can not be configured via software.
-
-I2C mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg : the I2C address of the device for I2C
-
-Example:
-
- rtc: rtc@32 {
- compatible = "epson,rx6110"
- reg = <0x32>;
- };
-
-SPI mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg: chip select number
- - spi-cs-high: RX6110 needs chipselect high
- - spi-cpha: RX6110 works with SPI shifted clock phase
- - spi-cpol: RX6110 works with SPI inverse clock polarity
-
-Example:
-
- rtc: rtc@3 {
- compatible = "epson,rx6110"
- reg = <3>
- spi-cs-high;
- spi-cpha;
- spi-cpol;
- };
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
new file mode 100644
index 000000000000..3466f74736d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Epson RX6110 Real Time Clock
+
+description: |
+ The Epson RX6110 can be used with SPI or I2C busses.
+ The kind of bus depends on the SPISEL pin and can not be
+ configured via software.
+
+maintainers:
+ - Alexandre Belloni <alexandre.belloni@bootlin.com>
+
+allOf:
+ - $ref: rtc.yaml#
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: epson,rx6110
+
+ reg:
+ maxItems: 1
+
+ spi-cs-high: true
+ spi-cpha: true
+ spi-cpol: true
+
+required:
+ - compatible
+ - reg
+
+if:
+ anyOf:
+ - required:
+ - spi-cs-high
+ - required:
+ - spi-cpha
+ - required:
+ - spi-cpol
+
+then:
+ required:
+ - spi-cs-high
+ - spi-cpha
+ - spi-cpol
+
+unevaluatedProperties: false
+
+examples:
+ # I2C mode
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@32 {
+ compatible = "epson,rx6110";
+ reg = <0x32>;
+ };
+ };
+
+ # SPI mode
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@3 {
+ compatible = "epson,rx6110";
+ reg = <3>;
+ spi-cs-high;
+ spi-cpha;
+ spi-cpol;
+ };
+ };
--
2.43.0
^ permalink raw reply related
* [PATCH] rtc: msc313: fix NULL deref in shared IRQ handler at probe
From: Stepan Ionichev @ 2026-05-11 3:27 UTC (permalink / raw)
To: alexandre.belloni
Cc: daniel, romain.perier, linux-arm-kernel, linux-rtc, linux-kernel,
sozdayvek
msc313_rtc_probe() calls devm_request_irq() with IRQF_SHARED and
&pdev->dev as the cookie, but platform_set_drvdata() is only called
later after the clock setup. With a shared IRQ line, another device
on the same line can trigger the handler in that window. The
handler does dev_get_drvdata() on the cookie, gets NULL, and
dereferences priv->rtc_base in interrupt context.
Pass priv as the cookie directly so the handler reads it from
dev_id without the lookup, removing the dependency on probe order.
Fixes: be7d9c9161b9 ("rtc: Add support for the MSTAR MSC313 RTC")
Signed-off-by: Stepan Ionichev <sozdayvek@gmail.com>
---
drivers/rtc/rtc-msc313.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c
index 8d7737e0e..6ef9c4efd 100644
--- a/drivers/rtc/rtc-msc313.c
+++ b/drivers/rtc/rtc-msc313.c
@@ -160,7 +160,7 @@ static const struct rtc_class_ops msc313_rtc_ops = {
static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id)
{
- struct msc313_rtc *priv = dev_get_drvdata(dev_id);
+ struct msc313_rtc *priv = dev_id;
u16 reg;
reg = readw(priv->rtc_base + REG_RTC_STATUS_INT);
@@ -206,7 +206,7 @@ static int msc313_rtc_probe(struct platform_device *pdev)
priv->rtc_dev->range_max = U32_MAX;
ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED,
- dev_name(&pdev->dev), &pdev->dev);
+ dev_name(&pdev->dev), priv);
if (ret) {
dev_err(dev, "Could not request IRQ\n");
return ret;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231
From: Meagan Lloyd @ 2026-05-11 20:21 UTC (permalink / raw)
To: Ronan Dalton
Cc: alexandre.belloni, linux-rtc, linux-kernel, Tyler Hicks,
Sasha Levin, Meagan Lloyd, Rodolfo Giometti, Chris Packham
In-Reply-To: <20260508032518.3696705-2-ronan.dalton@alliedtelesis.co.nz>
On Fri, May 08, 2026 at 03:24:49PM +1200, Ronan Dalton wrote:
> Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator
> stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was
> checked during device probe for the ds1337, ds1339, ds1341, and ds3231
> chips; if it was set, it would be cleared and a warning would be logged
> saying "SET TIME!". Since that commit, the OSF bit is no longer cleared,
> but the warning is still printed.
>
> Directly following that commit, there was no way to get rid of this
> warning because nothing cleared the OSF bit on these chips.
>
> The commit associated with the previous commit, 523923cfd5d6 ("rtc:
> ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use
> of the OSF when getting and setting the time in the RTC. However, the
> other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding
> change made.
>
> Given that the OSF bit is no longer cleared at probe time when it is
> set, the remaining three chips should have the same handling as the
> ds1341 chip has for the OSF bit.
>
> Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the
> same logic as the ds1341 has to these chips.
>
> Note that any devices brought up between the first referenced commit and
> this one may begin mistrusting the time reported by the RTC until it is
> set again, if the bit was never explicitly cleared.
>
> Note that only the ds1339 was tested with this change, but the
> datasheets for the other chips contain essentially identical
> descriptions of the OSF bit so the same change should work.
>
> Signed-off-by: Ronan Dalton <ronan.dalton@alliedtelesis.co.nz>
> Cc: linux-rtc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
> Cc: Tyler Hicks <code@tyhicks.com>
> Cc: Sasha Levin <sashal@kernel.org>
> Cc: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> Cc: Rodolfo Giometti <giometti@enneenne.com>
> Cc: Chris Packham <chris.packham@alliedtelesis.co.nz>
> Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe")
> ---
> Changes in v3:
> - Remove paragraph mentioning alternative fix from commit message
>
> Changes in v2:
> - Fix hashes of referenced commits
Reviewed-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
>
> drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++-----------
> 1 file changed, 17 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
> index 7205c59ff729..edf81b975dec 100644
> --- a/drivers/rtc/rtc-ds1307.c
> +++ b/drivers/rtc/rtc-ds1307.c
> @@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
> if (tmp & DS1338_BIT_OSF)
> return -EINVAL;
> break;
> + case ds_1337:
> + case ds_1339:
> + case ds_1341:
> + case ds_3231:
> + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
> + if (ret)
> + return ret;
> + if (tmp & DS1337_BIT_OSF)
> + return -EINVAL;
> + break;
> case ds_1340:
> if (tmp & DS1340_BIT_nEOSC)
> return -EINVAL;
> @@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
> if (tmp & DS1340_BIT_OSF)
> return -EINVAL;
> break;
> - case ds_1341:
> - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
> - if (ret)
> - return ret;
> - if (tmp & DS1337_BIT_OSF)
> - return -EINVAL;
> - break;
> case ds_1388:
> ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
> if (ret)
> @@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
> regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
> DS1338_BIT_OSF, 0);
> break;
> + case ds_1337:
> + case ds_1339:
> + case ds_1341:
> + case ds_3231:
> + regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
> + DS1337_BIT_OSF, 0);
> + break;
> case ds_1340:
> regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
> DS1340_BIT_OSF, 0);
> break;
> - case ds_1341:
> - regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
> - DS1337_BIT_OSF, 0);
> - break;
> case ds_1388:
> regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
> DS1388_BIT_OSF, 0);
> --
> 2.53.0
^ permalink raw reply
* [PATCH] rtc: interface: fix typos in rtc_handle_legacy_irq() documentation
From: Yahya Saqban @ 2026-05-12 21:02 UTC (permalink / raw)
To: Alexandre Belloni; +Cc: linux-rtc, linux-kernel, Yahya Saqban
Fix spelling of 'occurence' to 'occurrence' and 'of' to 'or' in the
kernel-doc comment for rtc_handle_legacy_irq().
Signed-off-by: Yahya Saqban <yahyasaqban@gmail.com>
---
drivers/rtc/interface.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 1906f4884..d7106cb29 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -635,8 +635,8 @@ EXPORT_SYMBOL_GPL(rtc_update_irq_enable);
/**
* rtc_handle_legacy_irq - AIE, UIE and PIE event hook
* @rtc: pointer to the rtc device
- * @num: number of occurence of the event
- * @mode: type of the event, RTC_AF, RTC_UF of RTC_PF
+ * @num: number of occurrence of the event
+ * @mode: type of the event, RTC_AF, RTC_UF or RTC_PF
*
* This function is called when an AIE, UIE or PIE mode interrupt
* has occurred (or been emulated).
--
2.51.0
^ permalink raw reply related
* Re: [PATCH v5 07/11] leds: flash: add support for Samsung S2M series PMIC flash LED device
From: Lee Jones @ 2026-05-13 14:00 UTC (permalink / raw)
To: Jacek Anaszewski
Cc: Kaustabh Chakraborty, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, MyungJoo Ham, Chanwoo Choi,
Sebastian Reichel, Krzysztof Kozlowski, André Draszik,
Alexandre Belloni, Jonathan Corbet, Shuah Khan, Nam Tran,
Łukasz Lebiedziński, linux-leds, devicetree,
linux-kernel, linux-pm, linux-samsung-soc, linux-rtc, linux-doc
In-Reply-To: <80d85385-f5af-44e3-b9ed-d4489542d4da@gmail.com>
On Thu, 07 May 2026, Jacek Anaszewski wrote:
> Hi Lee,
>
> On 5/7/26 6:46 PM, Lee Jones wrote:
> > On Fri, 24 Apr 2026, Kaustabh Chakraborty wrote:
> >
> > > Add support for flash LEDs in certain Samsung S2M series PMICs.
> > > The device has two channels for LEDs, typically for the back and front
> > > cameras in mobile devices. Both channels can be independently
> > > controlled, and can be operated in torch or flash modes.
> > >
> > > The driver includes initial support for the S2MU005 PMIC flash LEDs.
> > >
> > > Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> > > ---
> > > drivers/leds/flash/Kconfig | 12 ++
> > > drivers/leds/flash/Makefile | 1 +
> > > drivers/leds/flash/leds-s2m-flash.c | 358 ++++++++++++++++++++++++++++++++++++
> > > 3 files changed, 371 insertions(+)
> > >
> > > diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
> > > index 5e08102a67841..be62e05277429 100644
> > > --- a/drivers/leds/flash/Kconfig
> > > +++ b/drivers/leds/flash/Kconfig
> > > @@ -114,6 +114,18 @@ config LEDS_RT8515
> > > To compile this driver as a module, choose M here: the module
> > > will be called leds-rt8515.
> > > +config LEDS_S2M_FLASH
> > > + tristate "Samsung S2M series PMICs flash/torch LED support"
> > > + depends on LEDS_CLASS
> > > + depends on MFD_SEC_CORE
> > > + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
> >
> > The `|| !V4L2_FLASH_LED_CLASS` part of this dependency makes it
> > unconditionally true. Was this intended? Perhaps this dependency can be
> > removed entirely.
> This is for a reason to allow building the driver if
> V4L2_FLASH_LED_CLASS is turned off, or build it as a module
> if V4L2_FLASH_LED_CLASS=m. You will get nice explanation from
> Google AI if you type just
> "V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS".
>
> See e.g. [0], which fixes undefined symbol error by adding this.
>
> [0] https://git.paulk.fr/projects/linux.git/commit/drivers?h=sunxi/cedrus/jpeg-nv16&id=dbeb02a0bc41b9e9b9c05e460890351efecf1352
I see. Thanks for the explanation.
--
Lee Jones
^ permalink raw reply
* [PATCH v1] rtc: mpfs: fix counter upload completion condition
From: Conor Dooley @ 2026-05-13 17:55 UTC (permalink / raw)
To: linux-riscv
Cc: conor, Conor Dooley, stable, Valentina.FernandezAlanis,
Daire McNamara, Alexandre Belloni, linux-rtc, linux-kernel
From: Conor Dooley <conor.dooley@microchip.com>
The condition that needs to be checked for upload completion is the
UPLOAD bit in the completion register going low. The original iterations
of this driver used a do-while and this was converted to a
read_poll_timeout() during upstreaming without the condition being
inverted as it should have been.
I suspect that this went unnoticed until now because a) the first read
was done when the bit was still set, immediately completing the
read_poll_timeout() and b) because the RTC doesn't hold time when power
is removed from the SoC reducing its utility (I for one keep it
disabled). If my first suspicion was true when the driver was
upstreamed, it's not true any longer though, hence the detection of the
problem.
Fixes: 0b31d703598dc ("rtc: Add driver for Microchip PolarFire SoC")
CC: stable@vger.kernel.org
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
CC: Valentina.FernandezAlanis@microchip.com
CC: Conor Dooley <conor.dooley@microchip.com>
CC: Daire McNamara <daire.mcnamara@microchip.com>
CC: Alexandre Belloni <alexandre.belloni@bootlin.com>
CC: linux-riscv@lists.infradead.org
CC: linux-rtc@vger.kernel.org
CC: linux-kernel@vger.kernel.org
---
drivers/rtc/rtc-mpfs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c
index 6aa3eae575d2a..ece6de4a6adbd 100644
--- a/drivers/rtc/rtc-mpfs.c
+++ b/drivers/rtc/rtc-mpfs.c
@@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm)
ctrl |= CONTROL_UPLOAD_BIT;
writel(ctrl, rtcdev->base + CONTROL_REG);
- ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US,
+ ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US,
false, rtcdev->base + CONTROL_REG);
if (ret) {
dev_err(dev, "timed out uploading time to rtc");
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v3 4/5] hwmon: (pmbus/adm1266) replace probe-time RTC seed with rtc_class device
From: Guenter Roeck @ 2026-05-14 2:14 UTC (permalink / raw)
To: Abdurrahman Hussain, Alexandru Tachici
Cc: linux-hwmon, linux-kernel, Alexandre Belloni, linux-rtc
In-Reply-To: <20260512-adm1266-v3-4-a81a479b0bb0@nexthop.ai>
On 5/12/26 11:56, Abdurrahman Hussain wrote:
> The driver currently writes the device's internal RTC at probe with
> ktime_get_seconds(), which returns CLOCK_MONOTONIC seconds since boot
> and is not a wall-clock value. The resulting timestamps embedded in
> blackbox records are therefore meaningless across reboots, defeating
> the cross-reboot record-correlation use case the field exists for.
>
> Switching the seed to ktime_get_real_seconds() does not actually fix
> this: at probe the system wall clock may not yet have been set (no
> external RTC, no userspace NTP), and seeding unconditionally also
> clobbers whatever valid time the ADM1266 retained across a warm
> reboot.
>
> The data sheet (Rev. D, p. 22) recommends "frequently send the time
> stamp to the ADM1266 to synchronize the UNIX time and reduce the time
> from drifting" when running on the internal oscillator. The clean way
> to expose that policy is an rtc_class device backed by SET_RTC, so
> that userspace tooling (hwclock, chrony, systemd-timesyncd) can drive
> the re-sync against /dev/rtcN once it trusts the system clock - with
> no driver-specific sysfs ABI.
>
> Drop the probe-time seed and adm1266_set_rtc() entirely. Add an
> rtc_class device whose ->read_time and ->set_time callbacks read and
> write the SET_RTC frame. The rtc_class API is second-precision, so
> the SET_RTC fractional-seconds bytes are always written as zero.
>
This doesn't just set the RTC time, it also acts as a real-time-clock.
That seems undesirable, since it isn't really a real-time clock.
It would also be the first (pseudo) rtc residing outside the rtc
subsystem.
It seems to me that there must be a better solution. The rtc subsystem
needs to be involved in this discussion.
Note - as sent separately - that making this driver dependent on
the RTC subsystem (as would be required by this patch) is unacceptable.
Guenter
> Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox")
> Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
> ---
> drivers/hwmon/pmbus/adm1266.c | 78 +++++++++++++++++++++++++++++++++++--------
> 1 file changed, 65 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
> index 080e7dbd0c06..05b31bb08f38 100644
> --- a/drivers/hwmon/pmbus/adm1266.c
> +++ b/drivers/hwmon/pmbus/adm1266.c
> @@ -18,8 +18,8 @@
> #include <linux/nvmem-consumer.h>
> #include <linux/nvmem-provider.h>
> #include "pmbus.h"
> +#include <linux/rtc.h>
> #include <linux/slab.h>
> -#include <linux/timekeeping.h>
>
> #define ADM1266_IC_DEVICE_REV 0xAE
> #define ADM1266_BLACKBOX_CONFIG 0xD3
> @@ -517,21 +517,73 @@ static int adm1266_config_nvmem(struct adm1266_data *data)
> return 0;
> }
>
> -static int adm1266_set_rtc(struct adm1266_data *data)
> +/*
> + * SET_RTC frame layout (datasheet Rev. D, Table 84):
> + * bytes [1:0] = fractional seconds, LSB = 1/65536 s
> + * bytes [5:2] = seconds since 1970-01-01 UTC
> + * The rtc_class API is second-precision, so the fractional bytes are
> + * always written as zero.
> + */
> +static int adm1266_write_rtc(struct i2c_client *client, time64_t secs)
> {
> - time64_t kt;
> - char write_buf[6];
> + u8 buf[6] = { 0 };
> int i;
>
> - kt = ktime_get_seconds();
> + for (i = 0; i < 4; i++)
> + buf[2 + i] = (secs >> (i * 8)) & 0xFF;
> +
> + return i2c_smbus_write_block_data(client, ADM1266_SET_RTC, sizeof(buf), buf);
> +}
> +
> +static int adm1266_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + u8 buf[I2C_SMBUS_BLOCK_MAX];
> + u32 secs;
> + int ret;
> + int i;
>
> - memset(write_buf, 0, sizeof(write_buf));
> + guard(pmbus_lock)(client);
> + ret = i2c_smbus_read_block_data(client, ADM1266_SET_RTC, buf);
> + if (ret < 0)
> + return ret;
> + if (ret != 6)
> + return -EIO;
>
> + secs = 0;
> for (i = 0; i < 4; i++)
> - write_buf[2 + i] = (kt >> (i * 8)) & 0xFF;
> + secs |= (u32)buf[2 + i] << (i * 8);
> +
> + rtc_time64_to_tm(secs, tm);
> + return 0;
> +}
>
> - return i2c_smbus_write_block_data(data->client, ADM1266_SET_RTC, sizeof(write_buf),
> - write_buf);
> +static int adm1266_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> +
> + guard(pmbus_lock)(client);
> + return adm1266_write_rtc(client, rtc_tm_to_time64(tm));
> +}
> +
> +static const struct rtc_class_ops adm1266_rtc_ops = {
> + .read_time = adm1266_rtc_read_time,
> + .set_time = adm1266_rtc_set_time,
> +};
> +
> +static int adm1266_register_rtc(struct adm1266_data *data)
> +{
> + struct rtc_device *rtc;
> +
> + rtc = devm_rtc_allocate_device(&data->client->dev);
> + if (IS_ERR(rtc))
> + return PTR_ERR(rtc);
> +
> + rtc->ops = &adm1266_rtc_ops;
> + rtc->range_min = 0;
> + rtc->range_max = U32_MAX;
> +
> + return devm_rtc_register_device(rtc);
> }
>
> static int adm1266_probe(struct i2c_client *client)
> @@ -557,10 +609,6 @@ static int adm1266_probe(struct i2c_client *client)
> if (ret < 0)
> return ret;
>
> - ret = adm1266_set_rtc(data);
> - if (ret < 0)
> - return ret;
> -
> ret = pmbus_do_probe(client, &data->info);
> if (ret)
> return ret;
> @@ -569,6 +617,10 @@ static int adm1266_probe(struct i2c_client *client)
> if (ret < 0)
> return ret;
>
> + ret = adm1266_register_rtc(data);
> + if (ret < 0)
> + return ret;
> +
> adm1266_init_debugfs(data);
>
> return 0;
>
^ permalink raw reply
* Re: [PATCH v3 4/5] hwmon: (pmbus/adm1266) replace probe-time RTC seed with rtc_class device
From: Abdurrahman Hussain @ 2026-05-14 4:25 UTC (permalink / raw)
To: Guenter Roeck, Abdurrahman Hussain, Alexandru Tachici
Cc: linux-hwmon, linux-kernel, Alexandre Belloni, linux-rtc,
Guenter Roeck
In-Reply-To: <045f1907-0180-46df-a123-cd0ce349e86b@roeck-us.net>
On Wed May 13, 2026 at 7:14 PM PDT, Guenter Roeck wrote:
> On 5/12/26 11:56, Abdurrahman Hussain wrote:
>> The driver currently writes the device's internal RTC at probe with
>> ktime_get_seconds(), which returns CLOCK_MONOTONIC seconds since boot
>> and is not a wall-clock value. The resulting timestamps embedded in
>> blackbox records are therefore meaningless across reboots, defeating
>> the cross-reboot record-correlation use case the field exists for.
>>
>> Switching the seed to ktime_get_real_seconds() does not actually fix
>> this: at probe the system wall clock may not yet have been set (no
>> external RTC, no userspace NTP), and seeding unconditionally also
>> clobbers whatever valid time the ADM1266 retained across a warm
>> reboot.
>>
>> The data sheet (Rev. D, p. 22) recommends "frequently send the time
>> stamp to the ADM1266 to synchronize the UNIX time and reduce the time
>> from drifting" when running on the internal oscillator. The clean way
>> to expose that policy is an rtc_class device backed by SET_RTC, so
>> that userspace tooling (hwclock, chrony, systemd-timesyncd) can drive
>> the re-sync against /dev/rtcN once it trusts the system clock - with
>> no driver-specific sysfs ABI.
>>
>> Drop the probe-time seed and adm1266_set_rtc() entirely. Add an
>> rtc_class device whose ->read_time and ->set_time callbacks read and
>> write the SET_RTC frame. The rtc_class API is second-precision, so
>> the SET_RTC fractional-seconds bytes are always written as zero.
>>
>
> This doesn't just set the RTC time, it also acts as a real-time-clock.
> That seems undesirable, since it isn't really a real-time clock.
> It would also be the first (pseudo) rtc residing outside the rtc
> subsystem.
>
Agreed -- calling it an RTC overstates what the chip does. The
register is a 32-bit seconds counter seeded via SET_RTC and stamped
into each blackbox record. No battery, no alarm, nothing the RTC API
normally implies; treating it as an rtc_class device was the wrong
fit.
> It seems to me that there must be a better solution. The rtc subsystem
> needs to be involved in this discussion.
>
Would you prefer I drop anything RTC-shaped from this driver
altogether and reach for something else (e.g. a read/write debugfs
file under pmbus/<dev>/, consistent with the other debugfs entries
this series adds), or would you rather I post a separate thread to
linux-rtc asking how a device like this should expose a host-driven
seconds counter and wait on their guidance before respinning?
The datasheet's intended use is for the host to frequently send the
current timestamp to the chip, so the value stamped into each blackbox
record reflects wall-clock time at the moment of the event. Either
direction works for me; my mild preference is the debugfs path since
it lets me close the loop without holding up the rest of the series.
> Note - as sent separately - that making this driver dependent on
> the RTC subsystem (as would be required by this patch) is unacceptable.
>
Understood -- I'll drop the rtc_class approach in v4 either way.
Thanks for the quick feedback,
Abdurrahman
^ permalink raw reply
* Re: [PATCH v4] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Rob Herring @ 2026-05-14 15:05 UTC (permalink / raw)
To: Udaya Kiran Challa
Cc: alexandre.belloni, krzk+dt, conor+dt, skhan, me, linux-rtc,
devicetree, linux-kernel
In-Reply-To: <20260509185735.21557-1-challauday369@gmail.com>
On Sun, May 10, 2026 at 12:26:39AM +0530, Udaya Kiran Challa wrote:
> Convert the Epson RX6110 Real Time Clock devicetree binding
> from the legacy text format to DT schema.
>
> Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
> ---
> Changelog:
> Changes since v3:
> - Add conditional validation for SPI mode properties using anyOf
>
> Link to v3:https://lore.kernel.org/all/20260509181909.21871-1-challauday369@gmail.com/
>
> Changes since v2:
> - Reference rtc.yaml for common RTC properties
> - Add conditional validation for SPI mode properties
>
> Link to v2:https://lore.kernel.org/all/20260509095713.5818-1-challauday369@gmail.com/
>
> Changes since v1:
> - Reuse common SPI peripheral properties
> - Drop redundant SPI-specific comment
> - Remove unused labels from examples
>
> Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
> ---
> .../devicetree/bindings/rtc/epson,rx6110.txt | 39 ---------
> .../devicetree/bindings/rtc/epson,rx6110.yaml | 79 +++++++++++++++++++
> 2 files changed, 79 insertions(+), 39 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.txt
> create mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
>
> diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
> deleted file mode 100644
> index 3dc313e01f77..000000000000
> --- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
> +++ /dev/null
> @@ -1,39 +0,0 @@
> -Epson RX6110 Real Time Clock
> -============================
> -
> -The Epson RX6110 can be used with SPI or I2C busses. The kind of
> -bus depends on the SPISEL pin and can not be configured via software.
> -
> -I2C mode
> ---------
> -
> -Required properties:
> - - compatible: should be: "epson,rx6110"
> - - reg : the I2C address of the device for I2C
> -
> -Example:
> -
> - rtc: rtc@32 {
> - compatible = "epson,rx6110"
> - reg = <0x32>;
> - };
> -
> -SPI mode
> ---------
> -
> -Required properties:
> - - compatible: should be: "epson,rx6110"
> - - reg: chip select number
> - - spi-cs-high: RX6110 needs chipselect high
> - - spi-cpha: RX6110 works with SPI shifted clock phase
> - - spi-cpol: RX6110 works with SPI inverse clock polarity
> -
> -Example:
> -
> - rtc: rtc@3 {
> - compatible = "epson,rx6110"
> - reg = <3>
> - spi-cs-high;
> - spi-cpha;
> - spi-cpol;
> - };
> diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
> new file mode 100644
> index 000000000000..3466f74736d2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
> @@ -0,0 +1,79 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Epson RX6110 Real Time Clock
> +
> +description: |
Don't need '|' if no formatting.
> + The Epson RX6110 can be used with SPI or I2C busses.
> + The kind of bus depends on the SPISEL pin and can not be
> + configured via software.
Wrap lines at 80 chars.
> +
> +maintainers:
> + - Alexandre Belloni <alexandre.belloni@bootlin.com>
> +
> +allOf:
> + - $ref: rtc.yaml#
> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +properties:
> + compatible:
> + const: epson,rx6110
> +
> + reg:
> + maxItems: 1
> +
> + spi-cs-high: true
> + spi-cpha: true
> + spi-cpol: true
> +
> +required:
> + - compatible
> + - reg
> +
> +if:
> + anyOf:
> + - required:
> + - spi-cs-high
> + - required:
> + - spi-cpha
> + - required:
> + - spi-cpol
> +
> +then:
> + required:
> + - spi-cs-high
> + - spi-cpha
> + - spi-cpol
This 'if' can be better expressed as:
dependencies:
spi-cs-high: [ spi-cpha, spi-cpol ]
spi-cpha: [ spi-cs-high, spi-cpol ]
spi-cpol: [ spi-cs-high, spi-cpha ]
> +
> +unevaluatedProperties: false
> +
> +examples:
> + # I2C mode
> + - |
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + rtc@32 {
> + compatible = "epson,rx6110";
> + reg = <0x32>;
> + };
> + };
> +
> + # SPI mode
> + - |
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + rtc@3 {
> + compatible = "epson,rx6110";
> + reg = <3>;
> + spi-cs-high;
> + spi-cpha;
> + spi-cpol;
> + };
> + };
> --
> 2.43.0
>
>
^ permalink raw reply
* [PATCH v5] dt-bindings: rtc: epson,rx6110: Convert to DT Schema
From: Udaya Kiran Challa @ 2026-05-14 17:33 UTC (permalink / raw)
To: alexandre.belloni, robh, krzk+dt, conor+dt
Cc: skhan, me, linux-rtc, devicetree, linux-kernel,
Udaya Kiran Challa
Convert the Epson RX6110 Real Time Clock devicetree binding
from the legacy text format to DT schema.
Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
---
Changelog:
Changes since v4:
- Replace conditional SPI mode properties with dependencies
- Simplify description formatting and wrap lines at 80 chars
Link to v4:https://lore.kernel.org/all/20260509185735.21557-1-challauday369@gmail.com/
Changes since v3:
- Add conditional validation for SPI mode properties using anyOf
Link to v3:https://lore.kernel.org/all/20260509181909.21871-1-challauday369@gmail.com/
Changes since v2:
- Reference rtc.yaml for common RTC properties
- Add conditional validation for SPI mode properties
Link to v2:https://lore.kernel.org/all/20260509095713.5818-1-challauday369@gmail.com/
Changes since v1:
- Reuse common SPI peripheral properties
- Drop redundant SPI-specific comment
- Remove unused labels from examples
Link to v1:https://lore.kernel.org/all/20260504183728.27412-1-challauday369@gmail.com/
---
.../devicetree/bindings/rtc/epson,rx6110.txt | 39 -----------
.../devicetree/bindings/rtc/epson,rx6110.yaml | 68 +++++++++++++++++++
2 files changed, 68 insertions(+), 39 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.txt
create mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
deleted file mode 100644
index 3dc313e01f77..000000000000
--- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Epson RX6110 Real Time Clock
-============================
-
-The Epson RX6110 can be used with SPI or I2C busses. The kind of
-bus depends on the SPISEL pin and can not be configured via software.
-
-I2C mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg : the I2C address of the device for I2C
-
-Example:
-
- rtc: rtc@32 {
- compatible = "epson,rx6110"
- reg = <0x32>;
- };
-
-SPI mode
---------
-
-Required properties:
- - compatible: should be: "epson,rx6110"
- - reg: chip select number
- - spi-cs-high: RX6110 needs chipselect high
- - spi-cpha: RX6110 works with SPI shifted clock phase
- - spi-cpol: RX6110 works with SPI inverse clock polarity
-
-Example:
-
- rtc: rtc@3 {
- compatible = "epson,rx6110"
- reg = <3>
- spi-cs-high;
- spi-cpha;
- spi-cpol;
- };
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
new file mode 100644
index 000000000000..55086ac7d1e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Epson RX6110 Real Time Clock
+
+description:
+ The Epson RX6110 can be used with SPI or I2C busses. The kind of bus depends
+ on the SPISEL pin and cannot be configured via software.
+
+maintainers:
+ - Alexandre Belloni <alexandre.belloni@bootlin.com>
+
+allOf:
+ - $ref: rtc.yaml#
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: epson,rx6110
+
+ reg:
+ maxItems: 1
+
+ spi-cs-high: true
+ spi-cpha: true
+ spi-cpol: true
+
+required:
+ - compatible
+ - reg
+
+dependencies:
+ spi-cs-high: [ spi-cpha, spi-cpol ]
+ spi-cpha: [ spi-cs-high, spi-cpol ]
+ spi-cpol: [ spi-cs-high, spi-cpha ]
+
+unevaluatedProperties: false
+
+examples:
+ # I2C mode
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@32 {
+ compatible = "epson,rx6110";
+ reg = <0x32>;
+ };
+ };
+
+ # SPI mode
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@3 {
+ compatible = "epson,rx6110";
+ reg = <3>;
+ spi-cs-high;
+ spi-cpha;
+ spi-cpol;
+ };
+ };
--
2.43.0
^ permalink raw reply related
* [PATCH v4 0/1] mfd: nct6694: Refactor transport layer and add HIF (eSPI) support
From: a0282524688 @ 2026-05-15 8:57 UTC (permalink / raw)
To: tmyu0, linusw, brgl, linux, andi.shyti, lee, mkl, mailhol,
alexandre.belloni, wim
Cc: linux-kernel, linux-gpio, linux-i2c, linux-can, netdev,
linux-watchdog, linux-hwmon, linux-rtc, linux-usb, Ming Yu
From: Ming Yu <a0282524688@gmail.com>
The Nuvoton NCT6694 is a peripheral expander that provides GPIO, I2C,
CAN-FD, Watchdog, HWMON, PWM, and RTC sub-devices. Currently, the
driver only supports USB as the host transport interface.
This series refactors the NCT6694 MFD core to support multiple transport
backends and adds a new Host Interface (HIF) transport driver that
communicates over eSPI using Super-I/O shared memory.
Changes since version 3:
- Remove redundant module type macro definitions from sub-device drivers
that are now provided by the shared header <linux/mfd/nct6694.h>,
fixing -Wmacro-redefined warnings.
Changes since version 2:
- Restore per-device IDA and mfd_add_hotplug_devices()/PLATFORM_DEVID_AUTO
to avoid child device ID conflicts with multiple NCT6694 chips.
- Validate irq_find_mapping() return value before dispatching IRQs.
- Check superio_enter() return value in nct6694_irq_init().
Changes since version 1:
- Reworked the Super-I/O access helpers.
Ming Yu (1):
mfd: Add Host Interface (HIF) support for Nuvoton NCT6694
MAINTAINERS | 1 +
drivers/gpio/gpio-nct6694.c | 7 -
drivers/hwmon/nct6694-hwmon.c | 21 -
drivers/i2c/busses/i2c-nct6694.c | 7 -
drivers/mfd/Kconfig | 47 +-
drivers/mfd/Makefile | 3 +-
drivers/mfd/nct6694-hif.c | 663 ++++++++++++++++++++++++++++
drivers/mfd/nct6694.c | 111 +++--
drivers/net/can/usb/nct6694_canfd.c | 6 -
drivers/rtc/rtc-nct6694.c | 7 -
drivers/watchdog/nct6694_wdt.c | 7 -
include/linux/mfd/nct6694.h | 54 ++-
12 files changed, 813 insertions(+), 121 deletions(-)
create mode 100644 drivers/mfd/nct6694-hif.c
--
2.34.1
^ permalink raw reply
* [PATCH v4 1/1] mfd: Add Host Interface (HIF) support for Nuvoton NCT6694
From: a0282524688 @ 2026-05-15 8:57 UTC (permalink / raw)
To: tmyu0, linusw, brgl, linux, andi.shyti, lee, mkl, mailhol,
alexandre.belloni, wim
Cc: linux-kernel, linux-gpio, linux-i2c, linux-can, netdev,
linux-watchdog, linux-hwmon, linux-rtc, linux-usb, Ming Yu
In-Reply-To: <20260515085746.114361-1-a0282524688@gmail.com>
From: Ming Yu <a0282524688@gmail.com>
The Nuvoton NCT6694 also provides a Host Interface (HIF) via eSPI
to the host to access its features.
Sub-devices can use the common functions nct6694_read_msg() and
nct6694_write_msg() to issue a command. They can also request
interrupts that will be called when the HIF device triggers a
shared memory interrupt.
To support multiple transports, the driver configuration is
updated to allow selecting between the USB and HIF interfaces.
Signed-off-by: Ming Yu <a0282524688@gmail.com>
---
Changes since version 3:
- Remove redundant module type macro definitions from sub-device drivers
that are now provided by the shared header <linux/mfd/nct6694.h>,
fixing -Wmacro-redefined warnings.
Changes since version 2:
- Restore per-device IDA and mfd_add_hotplug_devices()/PLATFORM_DEVID_AUTO
to avoid child device ID conflicts with multiple NCT6694 chips.
- Validate irq_find_mapping() return value before dispatching IRQs.
- Check superio_enter() return value in nct6694_irq_init().
Changes since version 1:
- Drop function pointers from Super-I/O access and use static inline
helpers with proper types.
MAINTAINERS | 1 +
drivers/gpio/gpio-nct6694.c | 7 -
drivers/hwmon/nct6694-hwmon.c | 21 -
drivers/i2c/busses/i2c-nct6694.c | 7 -
drivers/mfd/Kconfig | 47 +-
drivers/mfd/Makefile | 3 +-
drivers/mfd/nct6694-hif.c | 663 ++++++++++++++++++++++++++++
drivers/mfd/nct6694.c | 111 +++--
drivers/net/can/usb/nct6694_canfd.c | 6 -
drivers/rtc/rtc-nct6694.c | 7 -
drivers/watchdog/nct6694_wdt.c | 7 -
include/linux/mfd/nct6694.h | 54 ++-
12 files changed, 813 insertions(+), 121 deletions(-)
create mode 100644 drivers/mfd/nct6694-hif.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 6aa3fe2ee1bb..e39aad82132e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19091,6 +19091,7 @@ S: Supported
F: drivers/gpio/gpio-nct6694.c
F: drivers/hwmon/nct6694-hwmon.c
F: drivers/i2c/busses/i2c-nct6694.c
+F: drivers/mfd/nct6694-hif.c
F: drivers/mfd/nct6694.c
F: drivers/net/can/usb/nct6694_canfd.c
F: drivers/rtc/rtc-nct6694.c
diff --git a/drivers/gpio/gpio-nct6694.c b/drivers/gpio/gpio-nct6694.c
index a8607f0d9915..53bfc5983648 100644
--- a/drivers/gpio/gpio-nct6694.c
+++ b/drivers/gpio/gpio-nct6694.c
@@ -13,13 +13,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-/*
- * USB command module type for NCT6694 GPIO controller.
- * This defines the module type used for communication with the NCT6694
- * GPIO controller over the USB interface.
- */
-#define NCT6694_GPIO_MOD 0xFF
-
#define NCT6694_GPIO_VER 0x90
#define NCT6694_GPIO_VALID 0x110
#define NCT6694_GPI_DATA 0x120
diff --git a/drivers/hwmon/nct6694-hwmon.c b/drivers/hwmon/nct6694-hwmon.c
index 6dcf22ca5018..581451875f2c 100644
--- a/drivers/hwmon/nct6694-hwmon.c
+++ b/drivers/hwmon/nct6694-hwmon.c
@@ -15,13 +15,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-/*
- * USB command module type for NCT6694 report channel
- * This defines the module type used for communication with the NCT6694
- * report channel over the USB interface.
- */
-#define NCT6694_RPT_MOD 0xFF
-
/* Report channel */
/*
* The report channel is used to report the status of the hardware monitor
@@ -38,13 +31,6 @@
#define NCT6694_TIN_STS(x) (0x6A + (x))
#define NCT6694_FIN_STS(x) (0x6E + (x))
-/*
- * USB command module type for NCT6694 HWMON controller.
- * This defines the module type used for communication with the NCT6694
- * HWMON controller over the USB interface.
- */
-#define NCT6694_HWMON_MOD 0x00
-
/* Command 00h - Hardware Monitor Control */
#define NCT6694_HWMON_CONTROL 0x00
#define NCT6694_HWMON_CONTROL_SEL 0x00
@@ -53,13 +39,6 @@
#define NCT6694_HWMON_ALARM 0x02
#define NCT6694_HWMON_ALARM_SEL 0x00
-/*
- * USB command module type for NCT6694 PWM controller.
- * This defines the module type used for communication with the NCT6694
- * PWM controller over the USB interface.
- */
-#define NCT6694_PWM_MOD 0x01
-
/* PWM Command - Manual Control */
#define NCT6694_PWM_CONTROL 0x01
#define NCT6694_PWM_CONTROL_SEL 0x00
diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c
index 1413ab6f9462..ef3329f34246 100644
--- a/drivers/i2c/busses/i2c-nct6694.c
+++ b/drivers/i2c/busses/i2c-nct6694.c
@@ -12,13 +12,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-/*
- * USB command module type for NCT6694 I2C controller.
- * This defines the module type used for communication with the NCT6694
- * I2C controller over the USB interface.
- */
-#define NCT6694_I2C_MOD 0x03
-
/* Command 00h - I2C Deliver */
#define NCT6694_I2C_DELIVER 0x00
#define NCT6694_I2C_DELIVER_SEL 0x00
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7192c9d1d268..8a715ec2f79f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1164,19 +1164,46 @@ config MFD_MENF21BMC
will be called menf21bmc.
config MFD_NCT6694
- tristate "Nuvoton NCT6694 support"
+ tristate
select MFD_CORE
+ help
+ Core MFD support for the Nuvoton NCT6694 peripheral expander.
+ This provides the common APIs and shared structures used by all
+ interfaces (USB, HIF) to access the NCT6694 hardware features
+ such as GPIO, I2C, CAN-FD, Watchdog, ADC, PWM, and RTC.
+
+ It is selected automatically by the transport interface drivers.
+
+config MFD_NCT6694_HIF
+ tristate "Nuvoton NCT6694 HIF (eSPI) interface support"
+ depends on HAS_IOPORT && ACPI
+ select MFD_NCT6694
+ select REGMAP_MMIO
+ help
+ This enables support for the Nuvoton NCT6694 peripheral expander
+ connected via the Host Interface (HIF) using eSPI transport.
+
+ The transport driver uses Super-I/O mapping and shared memory to
+ communicate with the NCT6694 firmware. Enable this option if you
+ are using the NCT6694 over an eSPI interface on an ACPI platform.
+
+ To compile this driver as a module, choose M here: the module
+ will be called nct6694-hif.
+
+config MFD_NCT6694_USB
+ tristate "Nuvoton NCT6694 USB interface support"
+ select MFD_NCT6694
depends on USB
help
- This enables support for the Nuvoton USB device NCT6694, which shares
- peripherals.
- The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,
- 6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,
- PWM, and RTC.
- This driver provides core APIs to access the NCT6694 hardware
- monitoring and control features.
- Additional drivers must be enabled to utilize the specific
- functionalities of the device.
+ This enables support for the Nuvoton NCT6694 peripheral expander
+ connected via the USB interface.
+
+ The transport driver uses USB bulk and interrupt transfers to
+ communicate with the NCT6694 firmware. Enable this option if you
+ are using the NCT6694 via a USB connection.
+
+ To compile this driver as a module, choose M here: the module
+ will be called nct6694.
config MFD_OCELOT
tristate "Microsemi Ocelot External Control Support"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..4cee9b74978c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -124,7 +124,8 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_PF1550) += pf1550.o
-obj-$(CONFIG_MFD_NCT6694) += nct6694.o
+obj-$(CONFIG_MFD_NCT6694_HIF) += nct6694-hif.o
+obj-$(CONFIG_MFD_NCT6694_USB) += nct6694.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
diff --git a/drivers/mfd/nct6694-hif.c b/drivers/mfd/nct6694-hif.c
new file mode 100644
index 000000000000..ca8c08b0d2e3
--- /dev/null
+++ b/drivers/mfd/nct6694-hif.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ *
+ * Nuvoton NCT6694 host-interface (eSPI) transport driver.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/unaligned.h>
+
+#define DRVNAME "nct6694-hif"
+
+#define NCT6694_POLL_INTERVAL_US 10
+#define NCT6694_POLL_TIMEOUT_US 10000
+
+/*
+ * Super-I/O registers
+ */
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_LD_SHM 0x0F /* Logical device shared memory control */
+
+#define SIO_REG_SHM_ENABLE 0x30 /* Enable shared memory */
+#define SIO_REG_SHM_BASE_ADDR 0x60 /* Shared memory base address (2 bytes) */
+#define SIO_REG_SHM_IRQ_NR 0x70 /* Shared memory interrupt number */
+
+#define SIO_REG_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_REG_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_NCT6694B_ID 0xD029
+#define SIO_NCT6694D_ID 0x5832
+
+/*
+ * Super-I/O Shared Memory Logical Device registers
+ */
+#define NCT6694_SHM_COFS_STS 0x2E
+#define NCT6694_SHM_COFS_STS_COFS4W BIT(7)
+
+#define NCT6694_SHM_COFS_CTL2 0x3B
+#define NCT6694_SHM_COFS_CTL2_COFS4W_IE BIT(3)
+
+#define NCT6694_SHM_INTR_STATUS 0x9C /* Interrupt status register (4 bytes) */
+
+enum nct6694_chips {
+ NCT6694B = 0,
+ NCT6694D,
+};
+
+enum nct6694_module_id {
+ NCT6694_GPIO0 = 0,
+ NCT6694_GPIO1,
+ NCT6694_GPIO2,
+ NCT6694_GPIO3,
+ NCT6694_GPIO4,
+ NCT6694_GPIO5,
+ NCT6694_GPIO6,
+ NCT6694_GPIO7,
+ NCT6694_GPIO8,
+ NCT6694_GPIO9,
+ NCT6694_GPIOA,
+ NCT6694_GPIOB,
+ NCT6694_GPIOC,
+ NCT6694_GPIOD,
+ NCT6694_GPIOE,
+ NCT6694_GPIOF,
+ NCT6694_I2C0,
+ NCT6694_I2C1,
+ NCT6694_I2C2,
+ NCT6694_I2C3,
+ NCT6694_I2C4,
+ NCT6694_I2C5,
+ NCT6694_CAN0,
+ NCT6694_CAN1,
+};
+
+struct __packed nct6694_msg {
+ struct nct6694_cmd_header cmd_header;
+ struct nct6694_response_header response_header;
+ unsigned char *data;
+};
+
+struct nct6694_sio_data {
+ enum nct6694_chips chip;
+ int sioreg; /* Super-I/O index port */
+};
+
+struct nct6694_hif_data {
+ struct regmap *regmap;
+ struct mutex msg_lock;
+ struct nct6694_sio_data *sio_data;
+ void __iomem *msg_base;
+ unsigned int shm_base;
+};
+
+static const char * const nct6694_chip_names[] = {
+ "NCT6694D",
+ "NCT6694B"
+};
+
+/*
+ * Super-I/O functions.
+ */
+static inline int superio_enter(struct nct6694_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ /*
+ * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
+ */
+ if (!request_muxed_region(ioreg, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(SIO_REG_UNLOCK_KEY, ioreg);
+ outb(SIO_REG_UNLOCK_KEY, ioreg);
+
+ return 0;
+}
+
+static inline void superio_exit(struct nct6694_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(SIO_REG_LOCK_KEY, ioreg);
+
+ release_region(ioreg, 2);
+}
+
+static inline void superio_select(struct nct6694_sio_data *sio_data, int ld)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(SIO_REG_LDSEL, ioreg);
+ outb(ld, ioreg + 1);
+}
+
+static inline int superio_inb(struct nct6694_sio_data *sio_data, int reg)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static inline int superio_inw(struct nct6694_sio_data *sio_data, int reg)
+{
+ int ioreg = sio_data->sioreg;
+ int val;
+
+ outb(reg++, ioreg);
+ val = inb(ioreg + 1) << 8;
+ outb(reg, ioreg);
+ val |= inb(ioreg + 1);
+
+ return val;
+}
+
+static inline void superio_outb(struct nct6694_sio_data *sio_data, int reg, u8 val)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static int nct6694_sio_find(struct nct6694_sio_data *sio_data, u8 sioreg)
+{
+ int ret;
+ u16 devid;
+
+ sio_data->sioreg = sioreg;
+
+ ret = superio_enter(sio_data);
+ if (ret)
+ return ret;
+
+ /* Check Chip ID */
+ devid = superio_inw(sio_data, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_NCT6694B_ID:
+ sio_data->chip = NCT6694B;
+ break;
+ case SIO_NCT6694D_ID:
+ sio_data->chip = NCT6694D;
+ break;
+ default:
+ pr_err("Unsupported device 0x%04x\n", devid);
+ goto err;
+ }
+
+ pr_info("Found %s at %#x\n", nct6694_chip_names[sio_data->chip], sio_data->sioreg);
+
+ superio_exit(sio_data);
+
+ return 0;
+
+err:
+ superio_exit(sio_data);
+ return -ENODEV;
+}
+
+static const struct mfd_cell_acpi_match nct6694_acpi_match_gpio[] = {
+ { .adr = NCT6694_GPIO0 },
+ { .adr = NCT6694_GPIO1 },
+ { .adr = NCT6694_GPIO2 },
+ { .adr = NCT6694_GPIO3 },
+ { .adr = NCT6694_GPIO4 },
+ { .adr = NCT6694_GPIO5 },
+ { .adr = NCT6694_GPIO6 },
+ { .adr = NCT6694_GPIO7 },
+ { .adr = NCT6694_GPIO8 },
+ { .adr = NCT6694_GPIO9 },
+ { .adr = NCT6694_GPIOA },
+ { .adr = NCT6694_GPIOB },
+ { .adr = NCT6694_GPIOC },
+ { .adr = NCT6694_GPIOD },
+ { .adr = NCT6694_GPIOE },
+ { .adr = NCT6694_GPIOF },
+};
+
+static const struct mfd_cell_acpi_match nct6694_acpi_match_i2c[] = {
+ { .adr = NCT6694_I2C0 },
+ { .adr = NCT6694_I2C1 },
+ { .adr = NCT6694_I2C2 },
+ { .adr = NCT6694_I2C3 },
+ { .adr = NCT6694_I2C4 },
+ { .adr = NCT6694_I2C5 },
+};
+
+static const struct mfd_cell_acpi_match nct6694_acpi_match_can[] = {
+ { .adr = NCT6694_CAN0 },
+ { .adr = NCT6694_CAN1 },
+};
+
+static const struct mfd_cell nct6694_devs[] = {
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[0]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[1]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[2]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[3]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[4]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[5]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[6]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[7]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[8]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[9]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[10]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[11]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[12]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[13]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[14]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[15]),
+
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[0]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[1]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[2]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[3]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[4]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[5]),
+
+ MFD_CELL_ACPI("nct6694-canfd", NULL, NULL, 0, 0, &nct6694_acpi_match_can[0]),
+ MFD_CELL_ACPI("nct6694-canfd", NULL, NULL, 0, 0, &nct6694_acpi_match_can[1]),
+};
+
+static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
+{
+ switch (err_status) {
+ case NCT6694_NO_ERROR:
+ return 0;
+ case NCT6694_NOT_SUPPORT_ERROR:
+ dev_err(nct6694->dev, "Command is not supported!\n");
+ break;
+ case NCT6694_NO_RESPONSE_ERROR:
+ dev_warn(nct6694->dev, "Command received no response!\n");
+ break;
+ case NCT6694_TIMEOUT_ERROR:
+ dev_warn(nct6694->dev, "Command timed out!\n");
+ break;
+ case NCT6694_PENDING:
+ dev_err(nct6694->dev, "Command is pending!\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return -EIO;
+}
+
+static int nct6694_xfer_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ u8 hctrl, void *buf)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ void __iomem *hdr = hdata->msg_base + offsetof(struct nct6694_msg, cmd_header);
+ struct nct6694_cmd_header cmd = *cmd_hd;
+ struct nct6694_response_header resp;
+ u16 len = le16_to_cpu(cmd.len);
+ u8 status;
+ int ret;
+
+ guard(mutex)(&hdata->msg_lock);
+
+ /* Wait until the previous command is completed */
+ ret = readb_poll_timeout(hdr + offsetof(struct nct6694_cmd_header, hctrl),
+ status, status == 0, NCT6694_POLL_INTERVAL_US,
+ NCT6694_POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /*
+ * Write cmd header fields, but skip hctrl — writing to it triggers
+ * firmware command processing and must be deferred until data is ready.
+ */
+ memcpy_toio(hdr, &cmd, offsetof(struct nct6694_cmd_header, hctrl));
+ memcpy_toio(hdr + offsetof(struct nct6694_cmd_header, rsv2), &cmd.rsv2,
+ sizeof(cmd) - offsetof(struct nct6694_cmd_header, rsv2));
+
+ if (hctrl == NCT6694_HCTRL_SET && len)
+ memcpy_toio(hdata->msg_base + offsetof(struct nct6694_msg, data),
+ buf, len);
+
+ /* Write hctrl last to trigger command processing */
+ writeb(hctrl, hdr + offsetof(struct nct6694_cmd_header, hctrl));
+
+ ret = readb_poll_timeout(hdr + offsetof(struct nct6694_cmd_header, hctrl),
+ status, status == 0, NCT6694_POLL_INTERVAL_US,
+ NCT6694_POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ memcpy_fromio(&resp, hdata->msg_base + offsetof(struct nct6694_msg, response_header),
+ sizeof(resp));
+
+ ret = nct6694_response_err_handling(nct6694, resp.sts);
+ if (ret)
+ return ret;
+
+ if (le16_to_cpu(resp.len))
+ memcpy_fromio(buf, hdata->msg_base + offsetof(struct nct6694_msg, data),
+ min(len, le16_to_cpu(resp.len)));
+
+ return 0;
+}
+
+/**
+ * nct6694_hif_read_msg() - Send a command and read response data via HIF
+ * @nct6694: NCT6694 device data
+ * @cmd_hd: command header
+ * @buf: buffer to store response data
+ *
+ * Return: 0 on success or negative errno on failure.
+ */
+static int nct6694_hif_read_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+
+ if (cmd_hd->mod == NCT6694_RPT_MOD)
+ return regmap_bulk_read(hdata->regmap,
+ le16_to_cpu(cmd_hd->offset),
+ buf, le16_to_cpu(cmd_hd->len));
+ return nct6694_xfer_msg(nct6694, cmd_hd, NCT6694_HCTRL_GET, buf);
+}
+
+/**
+ * nct6694_hif_write_msg() - Send a command with data payload via HIF
+ * @nct6694: NCT6694 device data
+ * @cmd_hd: command header
+ * @buf: buffer containing data to send
+ *
+ * Return: 0 on success or negative errno on failure.
+ */
+static int nct6694_hif_write_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+
+ if (cmd_hd->mod == NCT6694_RPT_MOD)
+ return regmap_bulk_write(hdata->regmap,
+ le16_to_cpu(cmd_hd->offset),
+ buf, le16_to_cpu(cmd_hd->len));
+ return nct6694_xfer_msg(nct6694, cmd_hd, NCT6694_HCTRL_SET, buf);
+}
+
+static const struct regmap_config nct6694_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_stride = 1,
+};
+
+static irqreturn_t nct6694_irq_handler(int irq, void *data)
+{
+ struct nct6694 *nct6694 = data;
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ u8 reg_data[4];
+ u32 intr_status;
+ int ret;
+
+ /* Check interrupt status is set */
+ if (!(inb(hdata->shm_base + NCT6694_SHM_COFS_STS) & NCT6694_SHM_COFS_STS_COFS4W))
+ return IRQ_NONE;
+
+ /* Clear interrupt status */
+ outb(NCT6694_SHM_COFS_STS_COFS4W, hdata->shm_base + NCT6694_SHM_COFS_STS);
+
+ ret = regmap_bulk_read(hdata->regmap, NCT6694_SHM_INTR_STATUS,
+ reg_data, ARRAY_SIZE(reg_data));
+ if (ret)
+ return IRQ_NONE;
+
+ intr_status = get_unaligned_le32(reg_data);
+
+ while (intr_status) {
+ int hwirq = __ffs(intr_status);
+ unsigned int virq = irq_find_mapping(nct6694->domain, hwirq);
+
+ if (virq)
+ generic_handle_irq_safe(virq);
+ intr_status &= ~BIT(hwirq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void nct6694_irq_release(void *data)
+{
+ struct nct6694 *nct6694 = data;
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ unsigned char cofs_ctl2;
+
+ /* Disable SIRQ interrupt */
+ cofs_ctl2 = inb(hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+ cofs_ctl2 &= ~NCT6694_SHM_COFS_CTL2_COFS4W_IE;
+ outb(cofs_ctl2, hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+}
+
+static int nct6694_irq_init(struct nct6694 *nct6694, int irq)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ struct nct6694_sio_data *sio_data = hdata->sio_data;
+ unsigned char cofs_ctl2;
+ int ret;
+
+ /* Set SIRQ number */
+ ret = superio_enter(sio_data);
+ if (ret)
+ return ret;
+
+ superio_select(sio_data, SIO_REG_LD_SHM);
+
+ if (!superio_inb(sio_data, SIO_REG_SHM_ENABLE)) {
+ superio_exit(sio_data);
+ return -EIO;
+ }
+
+ hdata->shm_base = superio_inw(sio_data, SIO_REG_SHM_BASE_ADDR);
+
+ superio_outb(sio_data, SIO_REG_SHM_IRQ_NR, irq);
+
+ superio_exit(sio_data);
+
+ /* Enable SIRQ interrupt */
+ cofs_ctl2 = inb(hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+ cofs_ctl2 |= NCT6694_SHM_COFS_CTL2_COFS4W_IE;
+ outb(cofs_ctl2, hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+
+ return 0;
+}
+
+static void nct6694_irq_enable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable |= BIT(hwirq);
+}
+
+static void nct6694_irq_disable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable &= ~BIT(hwirq);
+}
+
+static const struct irq_chip nct6694_irq_chip = {
+ .name = "nct6694-irq",
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+ .irq_enable = nct6694_irq_enable,
+ .irq_disable = nct6694_irq_disable,
+};
+
+static void nct6694_irq_domain_remove(void *data)
+{
+ struct nct6694 *nct6694 = data;
+
+ irq_domain_remove(nct6694->domain);
+}
+
+static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ struct nct6694 *nct6694 = d->host_data;
+
+ irq_set_chip_data(irq, nct6694);
+ irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops nct6694_irq_domain_ops = {
+ .map = nct6694_irq_domain_map,
+ .unmap = nct6694_irq_domain_unmap,
+};
+
+static void nct6694_ida_destroy(void *data)
+{
+ struct nct6694 *nct6694 = data;
+
+ ida_destroy(&nct6694->wdt_ida);
+ ida_destroy(&nct6694->canfd_ida);
+ ida_destroy(&nct6694->i2c_ida);
+ ida_destroy(&nct6694->gpio_ida);
+}
+
+static const u8 sio_addrs[] = { 0x2e, 0x4e };
+
+static int nct6694_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6694_sio_data *sio_data;
+ struct nct6694_hif_data *hdata;
+ struct nct6694 *data;
+ void __iomem *rpt_base, *msg_base;
+ int ret, i, irq;
+
+ sio_data = devm_kzalloc(dev, sizeof(*sio_data), GFP_KERNEL);
+ if (!sio_data)
+ return -ENOMEM;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
+ if (!hdata)
+ return -ENOMEM;
+
+ rpt_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rpt_base))
+ return PTR_ERR(rpt_base);
+ msg_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(msg_base))
+ return PTR_ERR(msg_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ for (i = 0; i < ARRAY_SIZE(sio_addrs); i++) {
+ ret = nct6694_sio_find(sio_data, sio_addrs[i]);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ hdata->sio_data = sio_data;
+ hdata->msg_base = msg_base;
+ hdata->regmap = devm_regmap_init_mmio(dev, rpt_base,
+ &nct6694_regmap_config);
+ if (IS_ERR(hdata->regmap))
+ return PTR_ERR(hdata->regmap);
+
+ data->dev = dev;
+ data->priv = hdata;
+ data->read_msg = nct6694_hif_read_msg;
+ data->write_msg = nct6694_hif_write_msg;
+
+ spin_lock_init(&data->irq_lock);
+
+ ida_init(&data->gpio_ida);
+ ida_init(&data->i2c_ida);
+ ida_init(&data->canfd_ida);
+ ida_init(&data->wdt_ida);
+
+ ret = devm_add_action_or_reset(dev, nct6694_ida_destroy, data);
+ if (ret)
+ return ret;
+
+ data->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
+ &nct6694_irq_domain_ops,
+ data);
+ if (!data->domain)
+ return -ENODEV;
+
+ ret = devm_add_action_or_reset(dev, nct6694_irq_domain_remove, data);
+ if (ret)
+ return ret;
+
+ ret = nct6694_irq_init(data, irq);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, nct6694_irq_release, data);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, nct6694_irq_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ dev_name(dev), data);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &hdata->msg_lock);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, nct6694_devs,
+ ARRAY_SIZE(nct6694_devs), NULL, 0, NULL);
+}
+
+static const struct acpi_device_id nct6694_acpi_ids[] = {
+ { "NTN0538", 0 },
+ {}
+};
+
+static struct platform_driver nct6694_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .acpi_match_table = nct6694_acpi_ids,
+ },
+ .probe = nct6694_probe,
+};
+module_platform_driver(nct6694_driver);
+
+MODULE_DESCRIPTION("Nuvoton NCT6694 host-interface transport driver");
+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/nct6694.c b/drivers/mfd/nct6694.c
index 308b2fda3055..cf3457c2b3ed 100644
--- a/drivers/mfd/nct6694.c
+++ b/drivers/mfd/nct6694.c
@@ -10,8 +10,8 @@
*/
#include <linux/bits.h>
-#include <linux/interrupt.h>
#include <linux/idr.h>
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
@@ -22,6 +22,27 @@
#include <linux/spinlock.h>
#include <linux/usb.h>
+#define NCT6694_VENDOR_ID 0x0416
+#define NCT6694_PRODUCT_ID 0x200B
+#define NCT6694_INT_IN_EP 0x81
+#define NCT6694_BULK_IN_EP 0x02
+#define NCT6694_BULK_OUT_EP 0x03
+
+#define NCT6694_URB_TIMEOUT 1000
+
+union __packed nct6694_usb_msg {
+ struct nct6694_cmd_header cmd_header;
+ struct nct6694_response_header response_header;
+};
+
+struct nct6694_usb_data {
+ struct mutex access_lock;
+ struct urb *int_in_urb;
+ struct usb_device *udev;
+ union nct6694_usb_msg *usb_msg;
+ __le32 *int_buffer;
+};
+
static const struct mfd_cell nct6694_devs[] = {
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
@@ -58,7 +79,8 @@ static const struct mfd_cell nct6694_devs[] = {
MFD_CELL_NAME("nct6694-rtc"),
};
-static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
+static int nct6694_usb_err_handling(struct nct6694 *nct6694,
+ unsigned char err_status)
{
switch (err_status) {
case NCT6694_NO_ERROR:
@@ -83,7 +105,7 @@ static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char
}
/**
- * nct6694_read_msg() - Read message from NCT6694 device
+ * nct6694_usb_read_msg() - Read message from NCT6694 device via USB
* @nct6694: NCT6694 device pointer
* @cmd_hd: command header structure
* @buf: buffer to store the response data
@@ -94,13 +116,16 @@ static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char
*
* Return: Negative value on error or 0 on success.
*/
-int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
+static int nct6694_usb_read_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
{
- union nct6694_usb_msg *msg = nct6694->usb_msg;
- struct usb_device *udev = nct6694->udev;
+ struct nct6694_usb_data *udata = nct6694->priv;
+ union nct6694_usb_msg *msg = udata->usb_msg;
+ struct usb_device *udev = udata->udev;
int tx_len, rx_len, ret;
- guard(mutex)(&nct6694->access_lock);
+ guard(mutex)(&udata->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_GET;
@@ -129,12 +154,11 @@ int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *c
return -EIO;
}
- return nct6694_response_err_handling(nct6694, msg->response_header.sts);
+ return nct6694_usb_err_handling(nct6694, msg->response_header.sts);
}
-EXPORT_SYMBOL_GPL(nct6694_read_msg);
/**
- * nct6694_write_msg() - Write message to NCT6694 device
+ * nct6694_usb_write_msg() - Write message to NCT6694 device via USB
* @nct6694: NCT6694 device pointer
* @cmd_hd: command header structure
* @buf: buffer containing the data to be sent
@@ -144,13 +168,16 @@ EXPORT_SYMBOL_GPL(nct6694_read_msg);
*
* Return: Negative value on error or 0 on success.
*/
-int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
+static int nct6694_usb_write_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
{
- union nct6694_usb_msg *msg = nct6694->usb_msg;
- struct usb_device *udev = nct6694->udev;
+ struct nct6694_usb_data *udata = nct6694->priv;
+ union nct6694_usb_msg *msg = udata->usb_msg;
+ struct usb_device *udev = udata->udev;
int tx_len, rx_len, ret;
- guard(mutex)(&nct6694->access_lock);
+ guard(mutex)(&udata->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
@@ -185,9 +212,8 @@ int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *
return -EIO;
}
- return nct6694_response_err_handling(nct6694, msg->response_header.sts);
+ return nct6694_usb_err_handling(nct6694, msg->response_header.sts);
}
-EXPORT_SYMBOL_GPL(nct6694_write_msg);
static void usb_int_callback(struct urb *urb)
{
@@ -210,10 +236,12 @@ static void usb_int_callback(struct urb *urb)
int_status = le32_to_cpu(*status_le);
while (int_status) {
- int irq = __ffs(int_status);
+ int hwirq = __ffs(int_status);
+ unsigned int virq = irq_find_mapping(nct6694->domain, hwirq);
- generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));
- int_status &= ~BIT(irq);
+ if (virq)
+ generic_handle_irq_safe(virq);
+ int_status &= ~BIT(hwirq);
}
resubmit:
@@ -277,6 +305,7 @@ static int nct6694_usb_probe(struct usb_interface *iface,
struct usb_endpoint_descriptor *int_endpoint;
struct usb_host_interface *interface;
struct device *dev = &iface->dev;
+ struct nct6694_usb_data *udata;
struct nct6694 *nct6694;
int ret;
@@ -284,18 +313,28 @@ static int nct6694_usb_probe(struct usb_interface *iface,
if (!nct6694)
return -ENOMEM;
- nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);
- if (!nct6694->usb_msg)
+ udata = devm_kzalloc(dev, sizeof(*udata), GFP_KERNEL);
+ if (!udata)
return -ENOMEM;
- nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
- if (!nct6694->int_buffer)
+ udata->usb_msg = devm_kzalloc(dev, sizeof(*udata->usb_msg), GFP_KERNEL);
+ if (!udata->usb_msg)
return -ENOMEM;
- nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!nct6694->int_in_urb)
+ udata->int_buffer = devm_kzalloc(dev, sizeof(*udata->int_buffer), GFP_KERNEL);
+ if (!udata->int_buffer)
return -ENOMEM;
+ udata->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!udata->int_in_urb)
+ return -ENOMEM;
+
+ udata->udev = udev;
+
+ nct6694->priv = udata;
+ nct6694->read_msg = nct6694_usb_read_msg;
+ nct6694->write_msg = nct6694_usb_write_msg;
+
nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
&nct6694_irq_domain_ops,
nct6694);
@@ -305,16 +344,15 @@ static int nct6694_usb_probe(struct usb_interface *iface,
}
nct6694->dev = dev;
- nct6694->udev = udev;
+
+ spin_lock_init(&nct6694->irq_lock);
ida_init(&nct6694->gpio_ida);
ida_init(&nct6694->i2c_ida);
ida_init(&nct6694->canfd_ida);
ida_init(&nct6694->wdt_ida);
- spin_lock_init(&nct6694->irq_lock);
-
- ret = devm_mutex_init(dev, &nct6694->access_lock);
+ ret = devm_mutex_init(dev, &udata->access_lock);
if (ret)
goto err_ida;
@@ -326,11 +364,11 @@ static int nct6694_usb_probe(struct usb_interface *iface,
goto err_ida;
}
- usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
- nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,
+ usb_fill_int_urb(udata->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
+ udata->int_buffer, sizeof(*udata->int_buffer), usb_int_callback,
nct6694, int_endpoint->bInterval);
- ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
+ ret = usb_submit_urb(udata->int_in_urb, GFP_KERNEL);
if (ret)
goto err_ida;
@@ -343,7 +381,7 @@ static int nct6694_usb_probe(struct usb_interface *iface,
return 0;
err_mfd:
- usb_kill_urb(nct6694->int_in_urb);
+ usb_kill_urb(udata->int_in_urb);
err_ida:
ida_destroy(&nct6694->wdt_ida);
ida_destroy(&nct6694->canfd_ida);
@@ -351,22 +389,23 @@ static int nct6694_usb_probe(struct usb_interface *iface,
ida_destroy(&nct6694->gpio_ida);
irq_domain_remove(nct6694->domain);
err_urb:
- usb_free_urb(nct6694->int_in_urb);
+ usb_free_urb(udata->int_in_urb);
return ret;
}
static void nct6694_usb_disconnect(struct usb_interface *iface)
{
struct nct6694 *nct6694 = usb_get_intfdata(iface);
+ struct nct6694_usb_data *udata = nct6694->priv;
mfd_remove_devices(nct6694->dev);
- usb_kill_urb(nct6694->int_in_urb);
+ usb_kill_urb(udata->int_in_urb);
ida_destroy(&nct6694->wdt_ida);
ida_destroy(&nct6694->canfd_ida);
ida_destroy(&nct6694->i2c_ida);
ida_destroy(&nct6694->gpio_ida);
irq_domain_remove(nct6694->domain);
- usb_free_urb(nct6694->int_in_urb);
+ usb_free_urb(udata->int_in_urb);
}
static const struct usb_device_id nct6694_ids[] = {
diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c
index e5f7f8849a73..262b4c26c9d4 100644
--- a/drivers/net/can/usb/nct6694_canfd.c
+++ b/drivers/net/can/usb/nct6694_canfd.c
@@ -18,12 +18,6 @@
#define DEVICE_NAME "nct6694-canfd"
-/* USB command module type for NCT6694 CANfd controller.
- * This defines the module type used for communication with the NCT6694
- * CANfd controller over the USB interface.
- */
-#define NCT6694_CANFD_MOD 0x05
-
/* Command 00h - CAN Setting and Initialization */
#define NCT6694_CANFD_SETTING 0x00
#define NCT6694_CANFD_SETTING_ACTIVE_CTRL1 BIT(0)
diff --git a/drivers/rtc/rtc-nct6694.c b/drivers/rtc/rtc-nct6694.c
index 35401a0d9cf5..c06902f150c9 100644
--- a/drivers/rtc/rtc-nct6694.c
+++ b/drivers/rtc/rtc-nct6694.c
@@ -14,13 +14,6 @@
#include <linux/rtc.h>
#include <linux/slab.h>
-/*
- * USB command module type for NCT6694 RTC controller.
- * This defines the module type used for communication with the NCT6694
- * RTC controller over the USB interface.
- */
-#define NCT6694_RTC_MOD 0x08
-
/* Command 00h - RTC Time */
#define NCT6694_RTC_TIME 0x0000
#define NCT6694_RTC_TIME_SEL 0x00
diff --git a/drivers/watchdog/nct6694_wdt.c b/drivers/watchdog/nct6694_wdt.c
index bc3689bd4b6b..4c06ac105562 100644
--- a/drivers/watchdog/nct6694_wdt.c
+++ b/drivers/watchdog/nct6694_wdt.c
@@ -20,13 +20,6 @@
#define NCT6694_WDT_MAX_DEVS 2
-/*
- * USB command module type for NCT6694 WDT controller.
- * This defines the module type used for communication with the NCT6694
- * WDT controller over the USB interface.
- */
-#define NCT6694_WDT_MOD 0x07
-
/* Command 00h - WDT Setup */
#define NCT6694_WDT_SETUP 0x00
#define NCT6694_WDT_SETUP_SEL(idx) (idx ? 0x01 : 0x00)
diff --git a/include/linux/mfd/nct6694.h b/include/linux/mfd/nct6694.h
index 6eb9be2cd4a0..fd645bf20fcd 100644
--- a/include/linux/mfd/nct6694.h
+++ b/include/linux/mfd/nct6694.h
@@ -2,22 +2,29 @@
/*
* Copyright (C) 2025 Nuvoton Technology Corp.
*
- * Nuvoton NCT6694 USB transaction and data structure.
+ * Nuvoton NCT6694 core definitions shared by all transport drivers
+ * and sub-device drivers.
*/
#ifndef __MFD_NCT6694_H
#define __MFD_NCT6694_H
-#define NCT6694_VENDOR_ID 0x0416
-#define NCT6694_PRODUCT_ID 0x200B
-#define NCT6694_INT_IN_EP 0x81
-#define NCT6694_BULK_IN_EP 0x02
-#define NCT6694_BULK_OUT_EP 0x03
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
#define NCT6694_HCTRL_SET 0x40
#define NCT6694_HCTRL_GET 0x80
-#define NCT6694_URB_TIMEOUT 1000
+#define NCT6694_HWMON_MOD 0x00
+#define NCT6694_PWM_MOD 0x01
+#define NCT6694_I2C_MOD 0x03
+#define NCT6694_CANFD_MOD 0x05
+#define NCT6694_WDT_MOD 0x07
+#define NCT6694_RTC_MOD 0x08
+#define NCT6694_RPT_MOD 0xFF
+#define NCT6694_GPIO_MOD NCT6694_RPT_MOD
enum nct6694_irq_id {
NCT6694_IRQ_GPIO0 = 0,
@@ -75,11 +82,6 @@ struct __packed nct6694_response_header {
__le16 len;
};
-union __packed nct6694_usb_msg {
- struct nct6694_cmd_header cmd_header;
- struct nct6694_response_header response_header;
-};
-
struct nct6694 {
struct device *dev;
struct ida gpio_ida;
@@ -87,16 +89,30 @@ struct nct6694 {
struct ida canfd_ida;
struct ida wdt_ida;
struct irq_domain *domain;
- struct mutex access_lock;
spinlock_t irq_lock;
- struct urb *int_in_urb;
- struct usb_device *udev;
- union nct6694_usb_msg *usb_msg;
- __le32 *int_buffer;
unsigned int irq_enable;
+
+ void *priv;
+ int (*read_msg)(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf);
+ int (*write_msg)(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf);
};
-int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf);
-int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf);
+static inline int nct6694_read_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ return nct6694->read_msg(nct6694, cmd_hd, buf);
+}
+
+static inline int nct6694_write_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ return nct6694->write_msg(nct6694, cmd_hd, buf);
+}
#endif
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox