mbox series

[v5,0/7] Add initial support for ATC260x PMICs

Message ID cover.1610534765.git.cristian.ciocaltea@gmail.com
Headers show
Series Add initial support for ATC260x PMICs | expand

Message

Cristian Ciocaltea Jan. 13, 2021, 11:05 a.m. UTC
The ATC260x family of PMICs integrates Audio Codec, Power management,
Clock generation and GPIO controller blocks. There are currently 3
variants: ATC2603A, ATC2603C and ATC2609A.

This is re-spin of the v1 patch series submitted some time ago by
Mani, who provided the MFD and regulator drivers for ATC2609A:
https://lore.kernel.org/lkml/20190617155011.15376-1-manivannan.sadhasivam@linaro.org/

Since v2, I added support for ATC2603C, together with some new
functionalities for both chips: power controller and onkey input.
The ATC2603A chip type remains unsupported for the moment.

This has been tested on RoseapplePi, a SBC based on the Actions Semi S500
SoC, which integrates the ATC2603C variant of the PMIC.

Note that enabling the ATC260x PMICs on compatible Actions Semi Owl SoC
based boards depends on:

* the Actions Semi SIRQ driver (for PMIC DTS setup), merged in v5.10:
  https://lore.kernel.org/lkml/cover.1600114378.git.cristian.ciocaltea@gmail.com/

* the atomic transfers in Owl I2C driver (for power controller), merged in v5.11:
  https://lore.kernel.org/lkml/cover.1602190168.git.cristian.ciocaltea@gmail.com/

Additionally, please note that I have taken the authorship for the MFD
and regulator drivers patches, considering the original code has been
modified to a large extent.

Thanks,
Cristi

Changes in v5:
- Removed an unnecessary '#include' line in the power-off driver,
  as noticed by Sebastian
- Rebased patchset on v5.11-rc3

Changes in v4:
- Updated MFD driver according to Lee's review
- Handled ATC2603C's LDO12 fixed regulator per Mark's suggestion
- Rebased patchset on v5.11-rc1

Changes in v3:
- Integrated feedback from Mani, Rob, Mark, Sebastian, Dmitry
- Fixed issues reported by Lee's kernel test robot
- Added new patch for 'reset-time-sec' DT binding property
- Rebased patchset on v5.10-rc6

Changes in v2:
- Reworked MFD core & I2C driver
  * Integrated Lee's feedback
  * Added support for using the regmap within atomic contexts
  * Added support for ATC2603C chip variant
  * Reorganized KConfig entries
- Improved regulator driver
  * Added support for ATC2603C variant
  * Used helper macros for more compact specification of regulator_desc items
  * Added more regulator capabilities
- Added power controller driver
  * Provides system poweroff/reboot functionalities
  * Depends on atomic transfers in the Owl I2C driver
- Added onkey driver: exposes the power button as an input device
- Added yaml binding doc
- Rebased patchset on kernel v5.9-rc1

Cristian Ciocaltea (6):
  dt-bindings: input: Add reset-time-sec common property
  dt-bindings: mfd: Add Actions Semi ATC260x PMIC binding
  mfd: Add MFD driver for ATC260x PMICs
  regulator: Add regulator driver for ATC260x PMICs
  power: reset: Add poweroff driver for ATC260x PMICs
  input: atc260x: Add onkey driver for ATC260x PMICs

Manivannan Sadhasivam (1):
  MAINTAINERS: Add entry for ATC260x PMIC

 .../devicetree/bindings/input/input.yaml      |   7 +
 .../bindings/mfd/actions,atc260x.yaml         | 183 ++++++
 MAINTAINERS                                   |  12 +
 drivers/input/misc/Kconfig                    |  11 +
 drivers/input/misc/Makefile                   |   2 +-
 drivers/input/misc/atc260x-onkey.c            | 305 ++++++++++
 drivers/mfd/Kconfig                           |  18 +
 drivers/mfd/Makefile                          |   3 +
 drivers/mfd/atc260x-core.c                    | 293 ++++++++++
 drivers/mfd/atc260x-i2c.c                     |  64 +++
 drivers/power/reset/Kconfig                   |   8 +-
 drivers/power/reset/Makefile                  |   1 +
 drivers/power/reset/atc260x-poweroff.c        | 262 +++++++++
 drivers/regulator/Kconfig                     |   8 +
 drivers/regulator/Makefile                    |   1 +
 drivers/regulator/atc260x-regulator.c         | 539 ++++++++++++++++++
 include/linux/mfd/atc260x/atc2603c.h          | 281 +++++++++
 include/linux/mfd/atc260x/atc2609a.h          | 308 ++++++++++
 include/linux/mfd/atc260x/core.h              |  58 ++
 19 files changed, 2362 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/actions,atc260x.yaml
 create mode 100644 drivers/input/misc/atc260x-onkey.c
 create mode 100644 drivers/mfd/atc260x-core.c
 create mode 100644 drivers/mfd/atc260x-i2c.c
 create mode 100644 drivers/power/reset/atc260x-poweroff.c
 create mode 100644 drivers/regulator/atc260x-regulator.c
 create mode 100644 include/linux/mfd/atc260x/atc2603c.h
 create mode 100644 include/linux/mfd/atc260x/atc2609a.h
 create mode 100644 include/linux/mfd/atc260x/core.h

Comments

Sebastian Reichel Jan. 13, 2021, 9:20 p.m. UTC | #1
Hi,

On Wed, Jan 13, 2021 at 01:05:55PM +0200, Cristian Ciocaltea wrote:
> This driver provides poweroff and reboot support for a system through
> the ATC2603C and ATC2609A chip variants of the Actions Semi ATC260x
> family of PMICs.
> 
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
> ---

Thanks, queued to power-supply's for-next branch.

-- Sebastian

> Changes in v5:
>  - Drop unneeded '#include <linux/of.h>'
> 
> Changes in v4:
>  - None
> 
> Changes in v3:
>  - Removed the unnecessary driver compatibles
> 
>  drivers/power/reset/Kconfig            |   8 +-
>  drivers/power/reset/Makefile           |   1 +
>  drivers/power/reset/atc260x-poweroff.c | 262 +++++++++++++++++++++++++
>  3 files changed, 270 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/power/reset/atc260x-poweroff.c
> 
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index b22c4fdb2561..1737e227b16e 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -39,6 +39,13 @@ config POWER_RESET_AT91_SAMA5D2_SHDWC
>  	  This driver supports the alternate shutdown controller for some Atmel
>  	  SAMA5 SoCs. It is present for example on SAMA5D2 SoC.
>  
> +config POWER_RESET_ATC260X
> +	tristate "Actions Semi ATC260x PMIC power-off driver"
> +	depends on MFD_ATC260X
> +	help
> +	  This driver provides power-off and restart support for a system
> +	  through Actions Semi ATC260x series PMICs.
> +
>  config POWER_RESET_AXXIA
>  	bool "LSI Axxia reset driver"
>  	depends on ARCH_AXXIA
> @@ -292,4 +299,3 @@ config NVMEM_REBOOT_MODE
>  	  action according to the mode.
>  
>  endif
> -
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index 9dc49d3a57ff..b4601c0a96ed 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
>  obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
>  obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
>  obj-$(CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC) += at91-sama5d2_shdwc.o
> +obj-$(CONFIG_POWER_RESET_ATC260X) += atc260x-poweroff.o
>  obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
>  obj-$(CONFIG_POWER_RESET_BRCMKONA) += brcm-kona-reset.o
>  obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
> diff --git a/drivers/power/reset/atc260x-poweroff.c b/drivers/power/reset/atc260x-poweroff.c
> new file mode 100644
> index 000000000000..98f20251a6d1
> --- /dev/null
> +++ b/drivers/power/reset/atc260x-poweroff.c
> @@ -0,0 +1,262 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Poweroff & reset driver for Actions Semi ATC260x PMICs
> + *
> + * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/mfd/atc260x/core.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/reboot.h>
> +#include <linux/regmap.h>
> +
> +struct atc260x_pwrc {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct notifier_block restart_nb;
> +	int (*do_poweroff)(const struct atc260x_pwrc *pwrc, bool restart);
> +};
> +
> +/* Global variable needed only for pm_power_off */
> +static struct atc260x_pwrc *atc260x_pwrc_data;
> +
> +static int atc2603c_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart)
> +{
> +	int ret, deep_sleep = 0;
> +	uint reg_mask, reg_val;
> +
> +	/* S4-Deep Sleep Mode is NOT available for WALL/USB power */
> +	if (!restart && !power_supply_is_system_supplied()) {
> +		deep_sleep = 1;
> +		dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode");
> +	}
> +
> +	/* Update wakeup sources */
> +	reg_val = ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN |
> +		  (restart ? ATC2603C_PMU_SYS_CTL0_RESET_WK_EN
> +			   : ATC2603C_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN);
> +
> +	ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0,
> +				 ATC2603C_PMU_SYS_CTL0_WK_ALL, reg_val);
> +	if (ret)
> +		dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
> +
> +	/* Update power mode */
> +	reg_mask = ATC2603C_PMU_SYS_CTL3_EN_S2 | ATC2603C_PMU_SYS_CTL3_EN_S3;
> +
> +	ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3, reg_mask,
> +				 deep_sleep ? 0 : ATC2603C_PMU_SYS_CTL3_EN_S3);
> +	if (ret) {
> +		dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Trigger poweroff / restart sequence */
> +	reg_mask = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN
> +			   : ATC2603C_PMU_SYS_CTL1_EN_S1;
> +	reg_val = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN : 0;
> +
> +	ret = regmap_update_bits(pwrc->regmap,
> +				 restart ? ATC2603C_PMU_SYS_CTL0 : ATC2603C_PMU_SYS_CTL1,
> +				 reg_mask, reg_val);
> +	if (ret) {
> +		dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n",
> +			restart ? 0 : 1, ret);
> +		return ret;
> +	}
> +
> +	/* Wait for trigger completion */
> +	mdelay(200);
> +
> +	return 0;
> +}
> +
> +static int atc2609a_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart)
> +{
> +	int ret, deep_sleep = 0;
> +	uint reg_mask, reg_val;
> +
> +	/* S4-Deep Sleep Mode is NOT available for WALL/USB power */
> +	if (!restart && !power_supply_is_system_supplied()) {
> +		deep_sleep = 1;
> +		dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode");
> +	}
> +
> +	/* Update wakeup sources */
> +	reg_val = ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN |
> +		  (restart ? ATC2609A_PMU_SYS_CTL0_RESET_WK_EN
> +			   : ATC2609A_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN);
> +
> +	ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0,
> +				 ATC2609A_PMU_SYS_CTL0_WK_ALL, reg_val);
> +	if (ret)
> +		dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
> +
> +	/* Update power mode */
> +	reg_mask = ATC2609A_PMU_SYS_CTL3_EN_S2 | ATC2609A_PMU_SYS_CTL3_EN_S3;
> +
> +	ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL3, reg_mask,
> +				 deep_sleep ? 0 : ATC2609A_PMU_SYS_CTL3_EN_S3);
> +	if (ret) {
> +		dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Trigger poweroff / restart sequence */
> +	reg_mask = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN
> +			   : ATC2609A_PMU_SYS_CTL1_EN_S1;
> +	reg_val = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN : 0;
> +
> +	ret = regmap_update_bits(pwrc->regmap,
> +				 restart ? ATC2609A_PMU_SYS_CTL0 : ATC2609A_PMU_SYS_CTL1,
> +				 reg_mask, reg_val);
> +	if (ret) {
> +		dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n",
> +			restart ? 0 : 1, ret);
> +		return ret;
> +	}
> +
> +	/* Wait for trigger completion */
> +	mdelay(200);
> +
> +	return 0;
> +}
> +
> +static int atc2603c_init(const struct atc260x_pwrc *pwrc)
> +{
> +	int ret;
> +
> +	/*
> +	 * Delay transition from S2/S3 to S1 in order to avoid
> +	 * DDR init failure in Bootloader.
> +	 */
> +	ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3,
> +				 ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN,
> +				 ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN);
> +	if (ret)
> +		dev_warn(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
> +
> +	/* Set wakeup sources */
> +	ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0,
> +				 ATC2603C_PMU_SYS_CTL0_WK_ALL,
> +				 ATC2603C_PMU_SYS_CTL0_HDSW_WK_EN |
> +				 ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN);
> +	if (ret)
> +		dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int atc2609a_init(const struct atc260x_pwrc *pwrc)
> +{
> +	int ret;
> +
> +	/* Set wakeup sources */
> +	ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0,
> +				 ATC2609A_PMU_SYS_CTL0_WK_ALL,
> +				 ATC2609A_PMU_SYS_CTL0_HDSW_WK_EN |
> +				 ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN);
> +	if (ret)
> +		dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static void atc260x_pwrc_pm_handler(void)
> +{
> +	atc260x_pwrc_data->do_poweroff(atc260x_pwrc_data, false);
> +
> +	WARN_ONCE(1, "Unable to power off system\n");
> +}
> +
> +static int atc260x_pwrc_restart_handler(struct notifier_block *nb,
> +					unsigned long mode, void *cmd)
> +{
> +	struct atc260x_pwrc *pwrc = container_of(nb, struct atc260x_pwrc,
> +						 restart_nb);
> +	pwrc->do_poweroff(pwrc, true);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int atc260x_pwrc_probe(struct platform_device *pdev)
> +{
> +	struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
> +	struct atc260x_pwrc *priv;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +	priv->regmap = atc260x->regmap;
> +	priv->restart_nb.notifier_call = atc260x_pwrc_restart_handler;
> +	priv->restart_nb.priority = 192;
> +
> +	switch (atc260x->ic_type) {
> +	case ATC2603C:
> +		priv->do_poweroff = atc2603c_do_poweroff;
> +		ret = atc2603c_init(priv);
> +		break;
> +	case ATC2609A:
> +		priv->do_poweroff = atc2609a_do_poweroff;
> +		ret = atc2609a_init(priv);
> +		break;
> +	default:
> +		dev_err(priv->dev,
> +			"Poweroff not supported for ATC260x PMIC type: %u\n",
> +			atc260x->ic_type);
> +		return -EINVAL;
> +	}
> +
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	if (!pm_power_off) {
> +		atc260x_pwrc_data = priv;
> +		pm_power_off = atc260x_pwrc_pm_handler;
> +	} else {
> +		dev_warn(priv->dev, "Poweroff callback already assigned\n");
> +	}
> +
> +	ret = register_restart_handler(&priv->restart_nb);
> +	if (ret)
> +		dev_err(priv->dev, "failed to register restart handler: %d\n",
> +			ret);
> +
> +	return ret;
> +}
> +
> +static int atc260x_pwrc_remove(struct platform_device *pdev)
> +{
> +	struct atc260x_pwrc *priv = platform_get_drvdata(pdev);
> +
> +	if (atc260x_pwrc_data == priv) {
> +		pm_power_off = NULL;
> +		atc260x_pwrc_data = NULL;
> +	}
> +
> +	unregister_restart_handler(&priv->restart_nb);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver atc260x_pwrc_driver = {
> +	.probe = atc260x_pwrc_probe,
> +	.remove = atc260x_pwrc_remove,
> +	.driver = {
> +		.name = "atc260x-pwrc",
> +	},
> +};
> +
> +module_platform_driver(atc260x_pwrc_driver);
> +
> +MODULE_DESCRIPTION("Poweroff & reset driver for ATC260x PMICs");
> +MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.30.0
>