mbox series

[0/4] Add nxp bbnsm module support

Message ID 20221121065144.3667658-1-ping.bai@nxp.com
Headers show
Series Add nxp bbnsm module support | expand

Message

Jacky Bai Nov. 21, 2022, 6:51 a.m. UTC
NXP BBNSM (Battery-Backed Non-Secure Module) serves as non-volatile
logic and storage for the system. it provides some similar functions
like RTC and ON/OFF support as previous SNVS module found on legacy
i.MX SoCs. The BBNSM is replacement of previous SNVS module, and more
likely it will be used on all the future i.MX SoC or other SoCs from
NXP.

This patchset add the basic support for BBNSM that found on i.MX93.

Jacky Bai (4):
  dt-bindings: mfd: nxp,bbnsm: Add binding for nxp bbnsm
  input: bbnsm_pwrkey: Add bbnsm power key support
  rtc: bbnsm: Add the bbnsm rtc support
  arm64: dts: imx93: Add the bbnsm dts node

 .../devicetree/bindings/mfd/nxp,bbnsm.yaml    | 103 ++++++++
 arch/arm64/boot/dts/freescale/imx93.dtsi      |  18 ++
 drivers/input/keyboard/Kconfig                |  11 +
 drivers/input/keyboard/Makefile               |   1 +
 drivers/input/keyboard/bbnsm_pwrkey.c         | 196 +++++++++++++++
 drivers/rtc/Kconfig                           |  12 +
 drivers/rtc/Makefile                          |   1 +
 drivers/rtc/rtc-bbnsm.c                       | 223 ++++++++++++++++++
 8 files changed, 565 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/nxp,bbnsm.yaml
 create mode 100644 drivers/input/keyboard/bbnsm_pwrkey.c
 create mode 100644 drivers/rtc/rtc-bbnsm.c

Comments

Alexandre Belloni Nov. 22, 2022, 6:18 p.m. UTC | #1
On 21/11/2022 14:51:43+0800, Jacky Bai wrote:
> The BBNSM module includes a real time counter with alarm.
> Add a RTC driver for this function.
> 
> Signed-off-by: Jacky Bai <ping.bai@nxp.com>
> Reviewed-by: Peng Fan <peng.fan@nxp.com>
> ---
>  drivers/rtc/Kconfig     |  12 +++
>  drivers/rtc/Makefile    |   1 +
>  drivers/rtc/rtc-bbnsm.c | 223 ++++++++++++++++++++++++++++++++++++++++

I'd prefer that filename to include imx or mxc if this is only available
on those SoCs.

> diff --git a/drivers/rtc/rtc-bbnsm.c b/drivers/rtc/rtc-bbnsm.c
> new file mode 100644
> index 000000000000..4157b238ed9a
> --- /dev/null
> +++ b/drivers/rtc/rtc-bbnsm.c
> @@ -0,0 +1,223 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +//
> +// Copyright 2022 NXP.
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_wakeirq.h>
> +#include <linux/rtc.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>

This list should be sorted alphabetically


> +
> +#define BBNSM_CTRL	0x8
> +#define BBNSM_INT_EN	0x10
> +#define BBNSM_EVENTS	0x14
> +#define BBNSM_RTC_LS	0x40
> +#define BBNSM_RTC_MS	0x44
> +#define BBNSM_TA	0x50
> +
> +#define RTC_EN		0x2
> +#define RTC_EN_MSK	0x3
> +#define TA_EN		(0x2 << 2)
> +#define TA_DIS		(0x1 << 2)
> +#define TA_EN_MSK	(0x3 << 2)
> +#define RTC_INT_EN	0x2
> +#define TA_INT_EN	(0x2 << 2)
> +
> +#define BBNSM_EVENT_TA	TA_EN
> +

I'm not clear why this define is needed

> +static irqreturn_t bbnsm_rtc_irq_handler(int irq, void *dev_id)
> +{
> +	struct device *dev = dev_id;
> +	struct bbnsm_rtc  *bbnsm = dev_get_drvdata(dev);
> +	u32 val;
> +	u32 event = 0;

You can rework this function to remove this variable because it is
either 0 or RTC_AF | RTC_IRQF

> +
> +	regmap_read(bbnsm->regmap, BBNSM_EVENTS, &val);
> +	if (val & BBNSM_EVENT_TA) {
> +		event |= (RTC_AF | RTC_IRQF);
> +		bbnsm_rtc_alarm_irq_enable(dev, false);
> +		/* clear the alarm event */
> +		regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, TA_EN_MSK, BBNSM_EVENT_TA);
> +		rtc_update_irq(bbnsm->rtc, 1, event);
> +	}
> +
> +	return event ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int bbnsm_rtc_probe(struct platform_device *pdev)
> +{
> +	struct bbnsm_rtc *bbnsm;
> +	int ret;
> +
> +	bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL);
> +	if (!bbnsm)
> +		return -ENOMEM;
> +
> +	bbnsm->rtc = devm_rtc_allocate_device(&pdev->dev);
> +
> +	bbnsm->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
> +	if (IS_ERR(bbnsm->regmap)) {
> +		dev_err(&pdev->dev, "bbnsm get regmap failed\n");
> +		return PTR_ERR(bbnsm->regmap);
> +	}
> +
> +	bbnsm->irq = platform_get_irq(pdev, 0);
> +	if (bbnsm->irq < 0)
> +		return bbnsm->irq;
> +
> +	platform_set_drvdata(pdev, bbnsm);
> +
> +	/* clear all the pending events */
> +	regmap_write(bbnsm->regmap, BBNSM_EVENTS, 0x7A);
> +
> +	/* Enable the Real-Time counter */
> +	regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, RTC_EN);
> +

Please don't do that, this removes an important piece of information.
Instead, let .set_time enable it and check it in .read_time as if this
is not set, you now you are out of PoR and the time is invalid

> +	device_init_wakeup(&pdev->dev, true);
> +	ret = dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq);
> +	if (ret)
> +		dev_err(&pdev->dev, "Failed to enable the irq wakeup\n");

dev_err but the function is not failing. Maybe just dev_warn? Also, is
this message really necessary?

> +
> +	ret = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_rtc_irq_handler,
> +			IRQF_SHARED, "rtc alarm", &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to request irq %d: %d\n",
> +			bbnsm->irq, ret);
> +		return ret;
> +	}
> +
> +	bbnsm->rtc->ops = &bbnsm_rtc_ops;
> +	bbnsm->rtc->range_max = U32_MAX;
> +
> +	return devm_rtc_register_device(bbnsm->rtc);
> +}
> +
> +static const struct of_device_id bbnsm_dt_ids[] = {
> +	{ .compatible = "nxp,bbnsm-rtc", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, bbnsm_dt_ids);
> +
> +static struct platform_driver bbnsm_rtc_driver = {
> +	.driver = {
> +		.name = "bbnsm_rtc",
> +		.of_match_table = bbnsm_dt_ids,
> +	},
> +	.probe = bbnsm_rtc_probe,
> +};
> +module_platform_driver(bbnsm_rtc_driver);
> +
> +MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
> +MODULE_DESCRIPTION("NXP BBNSM RTC Driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.37.1
>
Dmitry Torokhov Nov. 22, 2022, 11:32 p.m. UTC | #2
Hi Jacky,

On Mon, Nov 21, 2022 at 02:51:42PM +0800, Jacky Bai wrote:
> The ON/OFF logic inside the BBNSM allows for connecting directly
> into a PMIC or other voltage regulator device. The module has an
> button input signal and a wakeup request input signal. It also
> has two interrupts (set_pwr_off_irq and set_pwr_on_irq) and an
> active-low PMIC enable (pmic_en_b) output.
> 
> Add the power key support for the ON/OFF button function found in
> BBNSM module.
> 
> Signed-off-by: Jacky Bai <ping.bai@nxp.com>
> Reviewed-by: Peng Fan <peng.fan@nxp.com>
> ---
>  drivers/input/keyboard/Kconfig        |  11 ++
>  drivers/input/keyboard/Makefile       |   1 +
>  drivers/input/keyboard/bbnsm_pwrkey.c | 196 ++++++++++++++++++++++++++
>  3 files changed, 208 insertions(+)
>  create mode 100644 drivers/input/keyboard/bbnsm_pwrkey.c
> 
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 00292118b79b..8efcd95492b3 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -456,6 +456,17 @@ config KEYBOARD_SNVS_PWRKEY
>  	  To compile this driver as a module, choose M here; the
>  	  module will be called snvs_pwrkey.
>  
> +config KEYBOARD_BBNSM_PWRKEY
> +	tristate "NXP BBNSM Power Key Driver"
> +	depends on ARCH_MXC || COMPILE_TEST
> +	depends on OF
> +	help
> +	  This is the bbnsm powerkey driver for the NXP i.MX application
> +	  processors.
> +
> +	  To compile this driver as a module, choose M here; the
> +	  module will be called bbnsm_pwrkey.
> +
>  config KEYBOARD_IMX
>  	tristate "IMX keypad support"
>  	depends on ARCH_MXC || COMPILE_TEST
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 5f67196bb2c1..0bc101e004ae 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
>  obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
>  obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
>  obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)	+= snvs_pwrkey.o
> +obj-$(CONFIG_KEYBOARD_BBNSM_PWRKEY)	+= bbnsm_pwrkey.o
>  obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
>  obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
>  obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
> diff --git a/drivers/input/keyboard/bbnsm_pwrkey.c b/drivers/input/keyboard/bbnsm_pwrkey.c
> new file mode 100644
> index 000000000000..288ee6844000
> --- /dev/null
> +++ b/drivers/input/keyboard/bbnsm_pwrkey.c
> @@ -0,0 +1,196 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +//
> +// Copyright 2022 NXP.
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_wakeirq.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#define BBNSM_CTRL		0x8
> +#define BBNSM_INT_EN		0x10
> +#define BBNSM_EVENTS		0x14
> +#define BBNSM_PAD_CTRL		0x24
> +
> +#define BBNSM_BTN_PRESSED	BIT(7)
> +#define BBNSM_PWR_ON		BIT(6)
> +#define BBNSM_BTN_OFF		BIT(5)
> +#define BBNSM_EMG_OFF		BIT(4)
> +#define BBNSM_PWRKEY_EVENTS	(BBNSM_PWR_ON | BBNSM_BTN_OFF | BBNSM_EMG_OFF)
> +#define BBNSM_DP_EN		BIT(24)
> +
> +#define DEBOUNCE_TIME		30
> +#define REPEAT_INTERVAL		60
> +
> +struct bbnsm_pwrkey {
> +	struct regmap *regmap;
> +	int irq;
> +	int keycode;
> +	int keystate;  /* 1:pressed */
> +	struct timer_list check_timer;
> +	struct input_dev *input;
> +};
> +
> +static void bbnsm_pwrkey_check_for_events(struct timer_list *t)
> +{
> +	struct bbnsm_pwrkey *bbnsm = from_timer(bbnsm, t, check_timer);
> +	struct input_dev *input = bbnsm->input;
> +	u32 state;
> +
> +	regmap_read(bbnsm->regmap, BBNSM_EVENTS, &state);

Can this fail?

> +
> +	state = state & BBNSM_BTN_PRESSED ? 1 : 0;
> +
> +	/* only report new event if status changed */
> +	if (state ^ bbnsm->keystate) {
> +		bbnsm->keystate = state;
> +		input_event(input, EV_KEY, bbnsm->keycode, state);
> +		input_sync(input);
> +		pm_relax(bbnsm->input->dev.parent);
> +	}
> +
> +	/* repeat check if pressed long */
> +	if (state) {
> +		mod_timer(&bbnsm->check_timer,
> +			  jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
> +	}

So interrupt is only generated once when key is pressed, but not on
release?

> +}
> +
> +static irqreturn_t bbnsm_pwrkey_interrupt(int irq, void *dev_id)
> +{
> +	struct platform_device *pdev = dev_id;
> +	struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
> +	struct input_dev *input = bbnsm->input;
> +	u32 event;
> +
> +	regmap_read(bbnsm->regmap, BBNSM_EVENTS, &event);
> +	if (event & BBNSM_BTN_OFF)
> +		mod_timer(&bbnsm->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
> +	else
> +		return IRQ_NONE;
> +
> +	pm_wakeup_event(input->dev.parent, 0);
> +
> +	/* clear PWR OFF */
> +	regmap_write(bbnsm->regmap, BBNSM_EVENTS, BBNSM_BTN_OFF);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void bbnsm_pwrkey_act(void *pdata)
> +{
> +	struct bbnsm_pwrkey *bbnsm = pdata;
> +
> +	del_timer_sync(&bbnsm->check_timer);
> +}
> +
> +static int bbnsm_pwrkey_probe(struct platform_device *pdev)
> +{
> +	struct bbnsm_pwrkey *bbnsm;
> +	struct input_dev *input;
> +	struct device_node *np = pdev->dev.of_node;
> +	int error;
> +
> +	bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL);
> +	if (!bbnsm)
> +		return -ENOMEM;
> +
> +	bbnsm->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
> +	if (IS_ERR(bbnsm->regmap)) {
> +		dev_err(&pdev->dev, "bbnsm pwerkey get regmap failed\n");
> +		return PTR_ERR(bbnsm->regmap);
> +	}
> +
> +	if (of_property_read_u32(np, "linux,code", &bbnsm->keycode)) {

Please use device_property_read_u32() here.

> +		bbnsm->keycode = KEY_POWER;
> +		dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
> +	}
> +
> +	bbnsm->irq = platform_get_irq(pdev, 0);
> +	if (bbnsm->irq < 0)
> +		return -EINVAL;
> +
> +	/* config the BBNSM power related register */
> +	regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, BBNSM_DP_EN, BBNSM_DP_EN);
> +
> +	/* clear the unexpected interrupt before driver ready */
> +	regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, BBNSM_PWRKEY_EVENTS, BBNSM_PWRKEY_EVENTS);
> +
> +	timer_setup(&bbnsm->check_timer, bbnsm_pwrkey_check_for_events, 0);
> +
> +	input = devm_input_allocate_device(&pdev->dev);
> +	if (!input) {
> +		dev_err(&pdev->dev, "failed to allocate the input device\n");
> +		error = -ENOMEM;
> +		goto error_probe;

Please return directly here and below, since there is not explicit
cleanup.

> +	}
> +
> +	input->name = pdev->name;
> +	input->phys = "bbnsm-pwrkey/input0";
> +	input->id.bustype = BUS_HOST;
> +
> +	input_set_capability(input, EV_KEY, bbnsm->keycode);
> +
> +	/* input customer action to cancel release timer */
> +	error = devm_add_action(&pdev->dev, bbnsm_pwrkey_act, bbnsm);
> +	if (error) {
> +		dev_err(&pdev->dev, "failed to register remove action\n");
> +		goto error_probe;
> +	}
> +
> +	bbnsm->input = input;
> +	platform_set_drvdata(pdev, bbnsm);
> +
> +	error = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_pwrkey_interrupt,
> +			       IRQF_SHARED, pdev->name, pdev);
> +	if (error) {
> +		dev_err(&pdev->dev, "interrupt not available.\n");
> +		goto error_probe;
> +	}
> +
> +	error = input_register_device(input);
> +	if (error < 0) {
> +		dev_err(&pdev->dev, "failed to register input device\n");
> +		goto error_probe;
> +	}
> +
> +	device_init_wakeup(&pdev->dev, true);
> +	error = dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq);
> +	if (error)
> +		dev_err(&pdev->dev, "irq wake enable failed.\n");
> +
> +	return 0;
> +
> +error_probe:
> +	return error;
> +}
> +
> +static const struct of_device_id bbnsm_pwrkey_ids[] = {
> +	{ .compatible = "nxp,bbnsm-pwrkey" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, bbnsm_pwrkey_ids);
> +
> +static struct platform_driver bbnsm_pwrkey_driver = {
> +	.driver = {
> +		.name = "bbnsm_pwrkey",
> +		.of_match_table = bbnsm_pwrkey_ids,
> +	},
> +	.probe = bbnsm_pwrkey_probe,
> +};
> +module_platform_driver(bbnsm_pwrkey_driver);
> +
> +MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
> +MODULE_DESCRIPTION("NXP bbnsm power key Driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.37.1
> 

Thanks.
Jacky Bai Nov. 23, 2022, 9:25 a.m. UTC | #3
> Subject: Re: [PATCH 3/4] rtc: bbnsm: Add the bbnsm rtc support
> 
> On 21/11/2022 14:51:43+0800, Jacky Bai wrote:
> > The BBNSM module includes a real time counter with alarm.
> > Add a RTC driver for this function.
> >
> > Signed-off-by: Jacky Bai <ping.bai@nxp.com>
> > Reviewed-by: Peng Fan <peng.fan@nxp.com>
> > ---
> >  drivers/rtc/Kconfig     |  12 +++
> >  drivers/rtc/Makefile    |   1 +
> >  drivers/rtc/rtc-bbnsm.c | 223
> > ++++++++++++++++++++++++++++++++++++++++
> 
> I'd prefer that filename to include imx or mxc if this is only available on those
> SoCs.
>

For now, it is used on i.MX SoC, not sure if it will be used on NXP other SoC. I will
update the file name to include 'imx' in v2.

> > diff --git a/drivers/rtc/rtc-bbnsm.c b/drivers/rtc/rtc-bbnsm.c new
> > file mode 100644 index 000000000000..4157b238ed9a
> > --- /dev/null
> > +++ b/drivers/rtc/rtc-bbnsm.c
> > @@ -0,0 +1,223 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +//
> > +// Copyright 2022 NXP.
> > +
> > +#include <linux/init.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_wakeirq.h>
> > +#include <linux/rtc.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/regmap.h>
> 
> This list should be sorted alphabetically
> 

Thx, will fix it in V2.

> 
> > +
> > +#define BBNSM_CTRL	0x8
> > +#define BBNSM_INT_EN	0x10
> > +#define BBNSM_EVENTS	0x14
> > +#define BBNSM_RTC_LS	0x40
> > +#define BBNSM_RTC_MS	0x44
> > +#define BBNSM_TA	0x50
> > +
> > +#define RTC_EN		0x2
> > +#define RTC_EN_MSK	0x3
> > +#define TA_EN		(0x2 << 2)
> > +#define TA_DIS		(0x1 << 2)
> > +#define TA_EN_MSK	(0x3 << 2)
> > +#define RTC_INT_EN	0x2
> > +#define TA_INT_EN	(0x2 << 2)
> > +
> > +#define BBNSM_EVENT_TA	TA_EN
> > +
> 
> I'm not clear why this define is needed

This define is for RTC alarm event status check, as it use the same bitfield offset as TA_EN,
I just did the above define. It seems introduce some unnecessary confusion. 

> 
> > +static irqreturn_t bbnsm_rtc_irq_handler(int irq, void *dev_id) {
> > +	struct device *dev = dev_id;
> > +	struct bbnsm_rtc  *bbnsm = dev_get_drvdata(dev);
> > +	u32 val;
> > +	u32 event = 0;
> 
> You can rework this function to remove this variable because it is either 0 or
> RTC_AF | RTC_IRQF
> 

Ok, will refine in V2.

> > +
> > +	regmap_read(bbnsm->regmap, BBNSM_EVENTS, &val);
> > +	if (val & BBNSM_EVENT_TA) {
> > +		event |= (RTC_AF | RTC_IRQF);
> > +		bbnsm_rtc_alarm_irq_enable(dev, false);
> > +		/* clear the alarm event */
> > +		regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, TA_EN_MSK,
> BBNSM_EVENT_TA);
> > +		rtc_update_irq(bbnsm->rtc, 1, event);
> > +	}
> > +
> > +	return event ? IRQ_HANDLED : IRQ_NONE; }
> > +
> > +static int bbnsm_rtc_probe(struct platform_device *pdev) {
> > +	struct bbnsm_rtc *bbnsm;
> > +	int ret;
> > +
> > +	bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL);
> > +	if (!bbnsm)
> > +		return -ENOMEM;
> > +
> > +	bbnsm->rtc = devm_rtc_allocate_device(&pdev->dev);
> > +
> > +	bbnsm->regmap =
> syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
> > +	if (IS_ERR(bbnsm->regmap)) {
> > +		dev_err(&pdev->dev, "bbnsm get regmap failed\n");
> > +		return PTR_ERR(bbnsm->regmap);
> > +	}
> > +
> > +	bbnsm->irq = platform_get_irq(pdev, 0);
> > +	if (bbnsm->irq < 0)
> > +		return bbnsm->irq;
> > +
> > +	platform_set_drvdata(pdev, bbnsm);
> > +
> > +	/* clear all the pending events */
> > +	regmap_write(bbnsm->regmap, BBNSM_EVENTS, 0x7A);
> > +
> > +	/* Enable the Real-Time counter */
> > +	regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK,
> RTC_EN);
> > +
> 
> Please don't do that, this removes an important piece of information.
> Instead, let .set_time enable it and check it in .read_time as if this is not set,
> you now you are out of PoR and the time is invalid
> 

Will remove it. set_time has the enable operation. I will add enable check in read_time callback.

> > +	device_init_wakeup(&pdev->dev, true);
> > +	ret = dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq);
> > +	if (ret)
> > +		dev_err(&pdev->dev, "Failed to enable the irq wakeup\n");
> 
> dev_err but the function is not failing. Maybe just dev_warn? Also, is this
> message really necessary?
> 

Not too necessary, I can drop the above log print.

BR

> > +
> > +	ret = devm_request_irq(&pdev->dev, bbnsm->irq,
> bbnsm_rtc_irq_handler,
> > +			IRQF_SHARED, "rtc alarm", &pdev->dev);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "failed to request irq %d: %d\n",
> > +			bbnsm->irq, ret);
> > +		return ret;
> > +	}
> > +
> > +	bbnsm->rtc->ops = &bbnsm_rtc_ops;
> > +	bbnsm->rtc->range_max = U32_MAX;
> > +
> > +	return devm_rtc_register_device(bbnsm->rtc);
> > +}
> > +
> > +static const struct of_device_id bbnsm_dt_ids[] = {
> > +	{ .compatible = "nxp,bbnsm-rtc", },
> > +	{ /* sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(of, bbnsm_dt_ids);
> > +
> > +static struct platform_driver bbnsm_rtc_driver = {
> > +	.driver = {
> > +		.name = "bbnsm_rtc",
> > +		.of_match_table = bbnsm_dt_ids,
> > +	},
> > +	.probe = bbnsm_rtc_probe,
> > +};
> > +module_platform_driver(bbnsm_rtc_driver);
> > +
> > +MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
> > +MODULE_DESCRIPTION("NXP BBNSM RTC Driver");
> MODULE_LICENSE("GPL");
> > --
> > 2.37.1
> >
> 
> --
Jacky Bai Nov. 23, 2022, 9:39 a.m. UTC | #4
> Subject: Re: [PATCH 2/4] input: bbnsm_pwrkey: Add bbnsm power key
> support
> 
> Hi Jacky,
> 
> On Mon, Nov 21, 2022 at 02:51:42PM +0800, Jacky Bai wrote:
> > The ON/OFF logic inside the BBNSM allows for connecting directly into
> > a PMIC or other voltage regulator device. The module has an button
> > input signal and a wakeup request input signal. It also has two
> > interrupts (set_pwr_off_irq and set_pwr_on_irq) and an active-low PMIC
> > enable (pmic_en_b) output.
> >
> > Add the power key support for the ON/OFF button function found in
> > BBNSM module.
> >
> > Signed-off-by: Jacky Bai <ping.bai@nxp.com>
> > Reviewed-by: Peng Fan <peng.fan@nxp.com>
> > ---
> >  drivers/input/keyboard/Kconfig        |  11 ++
> >  drivers/input/keyboard/Makefile       |   1 +
> >  drivers/input/keyboard/bbnsm_pwrkey.c | 196
> > ++++++++++++++++++++++++++
> >  3 files changed, 208 insertions(+)
> >  create mode 100644 drivers/input/keyboard/bbnsm_pwrkey.c
> >
> > diff --git a/drivers/input/keyboard/Kconfig
> > b/drivers/input/keyboard/Kconfig index 00292118b79b..8efcd95492b3
> > 100644
> > --- a/drivers/input/keyboard/Kconfig
> > +++ b/drivers/input/keyboard/Kconfig
> > @@ -456,6 +456,17 @@ config KEYBOARD_SNVS_PWRKEY
> >  	  To compile this driver as a module, choose M here; the
> >  	  module will be called snvs_pwrkey.
> >
> > +config KEYBOARD_BBNSM_PWRKEY
> > +	tristate "NXP BBNSM Power Key Driver"
> > +	depends on ARCH_MXC || COMPILE_TEST
> > +	depends on OF
> > +	help
> > +	  This is the bbnsm powerkey driver for the NXP i.MX application
> > +	  processors.
> > +
> > +	  To compile this driver as a module, choose M here; the
> > +	  module will be called bbnsm_pwrkey.
> > +
> >  config KEYBOARD_IMX
> >  	tristate "IMX keypad support"
> >  	depends on ARCH_MXC || COMPILE_TEST
> > diff --git a/drivers/input/keyboard/Makefile
> > b/drivers/input/keyboard/Makefile index 5f67196bb2c1..0bc101e004ae
> > 100644
> > --- a/drivers/input/keyboard/Makefile
> > +++ b/drivers/input/keyboard/Makefile
> > @@ -62,6 +62,7 @@ obj-$(CONFIG_KEYBOARD_QT2160)		+=
> qt2160.o
> >  obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
> >  obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
> >  obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)	+= snvs_pwrkey.o
> > +obj-$(CONFIG_KEYBOARD_BBNSM_PWRKEY)	+= bbnsm_pwrkey.o
> >  obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
> >  obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
> >  obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
> > diff --git a/drivers/input/keyboard/bbnsm_pwrkey.c
> > b/drivers/input/keyboard/bbnsm_pwrkey.c
> > new file mode 100644
> > index 000000000000..288ee6844000
> > --- /dev/null
> > +++ b/drivers/input/keyboard/bbnsm_pwrkey.c

...

> > +
> > +static void bbnsm_pwrkey_check_for_events(struct timer_list *t) {
> > +	struct bbnsm_pwrkey *bbnsm = from_timer(bbnsm, t, check_timer);
> > +	struct input_dev *input = bbnsm->input;
> > +	u32 state;
> > +
> > +	regmap_read(bbnsm->regmap, BBNSM_EVENTS, &state);
> 
> Can this fail?

Should no chance to fail. Any more tips?

> 
> > +
> > +	state = state & BBNSM_BTN_PRESSED ? 1 : 0;
> > +
> > +	/* only report new event if status changed */
> > +	if (state ^ bbnsm->keystate) {
> > +		bbnsm->keystate = state;
> > +		input_event(input, EV_KEY, bbnsm->keycode, state);
> > +		input_sync(input);
> > +		pm_relax(bbnsm->input->dev.parent);
> > +	}
> > +
> > +	/* repeat check if pressed long */
> > +	if (state) {
> > +		mod_timer(&bbnsm->check_timer,
> > +			  jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
> > +	}
> 
> So interrupt is only generated once when key is pressed, but not on release?
> 

Yes, at lease from my test, this interrupt can only be triggered when pressed.

> > +}
> > +
> > +static irqreturn_t bbnsm_pwrkey_interrupt(int irq, void *dev_id) {
> > +	struct platform_device *pdev = dev_id;
> > +	struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
> > +	struct input_dev *input = bbnsm->input;
> > +	u32 event;
> > +
> > +	regmap_read(bbnsm->regmap, BBNSM_EVENTS, &event);
> > +	if (event & BBNSM_BTN_OFF)
> > +		mod_timer(&bbnsm->check_timer, jiffies +
> msecs_to_jiffies(DEBOUNCE_TIME));
> > +	else
> > +		return IRQ_NONE;
> > +
> > +	pm_wakeup_event(input->dev.parent, 0);
> > +
> > +	/* clear PWR OFF */
> > +	regmap_write(bbnsm->regmap, BBNSM_EVENTS, BBNSM_BTN_OFF);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static void bbnsm_pwrkey_act(void *pdata) {
> > +	struct bbnsm_pwrkey *bbnsm = pdata;
> > +
> > +	del_timer_sync(&bbnsm->check_timer);
> > +}
> > +
> > +static int bbnsm_pwrkey_probe(struct platform_device *pdev) {
> > +	struct bbnsm_pwrkey *bbnsm;
> > +	struct input_dev *input;
> > +	struct device_node *np = pdev->dev.of_node;
> > +	int error;
> > +
> > +	bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL);
> > +	if (!bbnsm)
> > +		return -ENOMEM;
> > +
> > +	bbnsm->regmap =
> syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
> > +	if (IS_ERR(bbnsm->regmap)) {
> > +		dev_err(&pdev->dev, "bbnsm pwerkey get regmap failed\n");
> > +		return PTR_ERR(bbnsm->regmap);
> > +	}
> > +
> > +	if (of_property_read_u32(np, "linux,code", &bbnsm->keycode)) {
> 
> Please use device_property_read_u32() here.

Ok, will fix in V2.

> 
> > +		bbnsm->keycode = KEY_POWER;
> > +		dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
> > +	}
> > +
> > +	bbnsm->irq = platform_get_irq(pdev, 0);
> > +	if (bbnsm->irq < 0)
> > +		return -EINVAL;
> > +
> > +	/* config the BBNSM power related register */
> > +	regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, BBNSM_DP_EN,
> > +BBNSM_DP_EN);
> > +
> > +	/* clear the unexpected interrupt before driver ready */
> > +	regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS,
> BBNSM_PWRKEY_EVENTS,
> > +BBNSM_PWRKEY_EVENTS);
> > +
> > +	timer_setup(&bbnsm->check_timer, bbnsm_pwrkey_check_for_events,
> 0);
> > +
> > +	input = devm_input_allocate_device(&pdev->dev);
> > +	if (!input) {
> > +		dev_err(&pdev->dev, "failed to allocate the input device\n");
> > +		error = -ENOMEM;
> > +		goto error_probe;
> 
> Please return directly here and below, since there is not explicit cleanup.
> 

Thx, will fix in V2.

BR

> > +	}
> > +
> > +	input->name = pdev->name;
> > +	input->phys = "bbnsm-pwrkey/input0";
> > +	input->id.bustype = BUS_HOST;
> > +
> > +	input_set_capability(input, EV_KEY, bbnsm->keycode);
> > +
> > +	/* input customer action to cancel release timer */
> > +	error = devm_add_action(&pdev->dev, bbnsm_pwrkey_act, bbnsm);
> > +	if (error) {
> > +		dev_err(&pdev->dev, "failed to register remove action\n");
> > +		goto error_probe;
> > +	}
> > +
> > +	bbnsm->input = input;
> > +	platform_set_drvdata(pdev, bbnsm);
> > +
> > +	error = devm_request_irq(&pdev->dev, bbnsm->irq,
> bbnsm_pwrkey_interrupt,
> > +			       IRQF_SHARED, pdev->name, pdev);
> > +	if (error) {
> > +		dev_err(&pdev->dev, "interrupt not available.\n");
> > +		goto error_probe;
> > +	}
> > +
> > +	error = input_register_device(input);
> > +	if (error < 0) {
> > +		dev_err(&pdev->dev, "failed to register input device\n");
> > +		goto error_probe;
> > +	}
> > +
> > +	device_init_wakeup(&pdev->dev, true);
> > +	error = dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq);
> > +	if (error)
> > +		dev_err(&pdev->dev, "irq wake enable failed.\n");
> > +
> > +	return 0;
> > +
> > +error_probe:
> > +	return error;
> > +}
> > +
> > +static const struct of_device_id bbnsm_pwrkey_ids[] = {
> > +	{ .compatible = "nxp,bbnsm-pwrkey" },
> > +	{ /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, bbnsm_pwrkey_ids);
> > +
> > +static struct platform_driver bbnsm_pwrkey_driver = {
> > +	.driver = {
> > +		.name = "bbnsm_pwrkey",
> > +		.of_match_table = bbnsm_pwrkey_ids,
> > +	},
> > +	.probe = bbnsm_pwrkey_probe,
> > +};
> > +module_platform_driver(bbnsm_pwrkey_driver);
> > +
> > +MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
> > +MODULE_DESCRIPTION("NXP bbnsm power key Driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.37.1
> >
> 
> Thanks.
> 
> --
> Dmitry