From patchwork Sun Dec 6 01:27:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Ciocaltea X-Patchwork-Id: 338944 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82BC1C1B0D8 for ; Sun, 6 Dec 2020 01:28:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4B93A23132 for ; Sun, 6 Dec 2020 01:28:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726342AbgLFB17 (ORCPT ); Sat, 5 Dec 2020 20:27:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49706 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725920AbgLFB14 (ORCPT ); Sat, 5 Dec 2020 20:27:56 -0500 Received: from mail-ej1-x641.google.com (mail-ej1-x641.google.com [IPv6:2a00:1450:4864:20::641]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 85419C0613D4; Sat, 5 Dec 2020 17:27:15 -0800 (PST) Received: by mail-ej1-x641.google.com with SMTP id bo9so14218272ejb.13; Sat, 05 Dec 2020 17:27:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Hhr+PSpxdrsAQvt323Tq1/gTyUseyObVcoODids+lqA=; b=VIdVUCWnufid9JZlpun9gF+LCvqCnlUjubxxJehhofR3y/3C7BRVg41DStuHKhM12Y qSAmwSxyBG5frrV/6Bq0VyVEMyUj9/GfadRX7Vdk4vE/7pMHdc17HSFNv1E84oORslC4 3YyqUxf7zuAc2shTr5enMJEeX/G081pw0hKGQOMcnHx5htE3eRMfsmJbygsMFiiBVkOv mPosECN8ZMXiDV69X5XnJC+XtvXNtlT7ahH7XN0WKqr25oMb66QWrwejHtHhtgCIwAaH G97P+HtIIdapiOQZN80V0jmkYC2cyDmnx2Vgz5xdu4zQHhXKq3fhBBHSDaN829HpQEyB nA+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Hhr+PSpxdrsAQvt323Tq1/gTyUseyObVcoODids+lqA=; b=TRQ0TB0Z1wfqChppu1M7dxM0sw9LzTvwPPTTzVyEipXttxMGQ7H7HtGlReahIuKQmB 3E+tSfC245Zxz643ejzmtukJmIJi/t9JXt3+9mWOml91hHRNeytH601g6c0v/a5PgsaV rcp1c3pQMNphTkW3rjXDq0DAjBsOqsk/BQqf9/HzRMtYCuA26K9ENrI0LUSU4bfslL4/ BpXcbQxkHnPP2jOgGczR4dwz7OQz7HhNVV3JyuE6AQ4APC9Atf21whHgN7yJcYDppIVC f/WJ/mj26pgkso6HW7Q6ozUaSknLIMPVUIB2hl9gXj4/MnBlHphugrFQa/8YZJh3iFRa kUOQ== X-Gm-Message-State: AOAM530E3g3sJI0O3/Wtrh0P0jAopxnR7M6Frx6tKky8d231DGBbKugA L2RP+vAlPTd53rXB5R6Z/cI= X-Google-Smtp-Source: ABdhPJxJCPKxxJZgH3tiYHf0CObocUm0OhfT/xethdm+fsQSsEXUrjha6DRhbhlKDbrxp06PPfGWTQ== X-Received: by 2002:a17:907:9627:: with SMTP id gb39mr13621713ejc.267.1607218034248; Sat, 05 Dec 2020 17:27:14 -0800 (PST) Received: from localhost.localdomain ([188.24.159.61]) by smtp.gmail.com with ESMTPSA id a10sm6157023ejk.92.2020.12.05.17.27.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Dec 2020 17:27:13 -0800 (PST) From: Cristian Ciocaltea To: Lee Jones , Rob Herring , Dmitry Torokhov , Sebastian Reichel , Mark Brown , Manivannan Sadhasivam , Liam Girdwood , =?utf-8?q?Andreas_F=C3=A4rber?= Cc: linux-actions@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v3 2/7] dt-bindings: mfd: Add Actions Semi ATC260x PMIC binding Date: Sun, 6 Dec 2020 03:27:02 +0200 Message-Id: X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Add devicetree binding for Actions Semi ATC260x PMICs. Signed-off-by: Cristian Ciocaltea Reviewed-by: Rob Herring --- Changes in v3 (according to Rob's review): - Dropped the 'pwrc' and 'onkey' nodes - Used a common 'reset-time-sec' property .../bindings/mfd/actions,atc260x.yaml | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/actions,atc260x.yaml diff --git a/Documentation/devicetree/bindings/mfd/actions,atc260x.yaml b/Documentation/devicetree/bindings/mfd/actions,atc260x.yaml new file mode 100644 index 000000000000..86aab77ba688 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/actions,atc260x.yaml @@ -0,0 +1,181 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/actions,atc260x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Actions Semi ATC260x Power Management IC bindings + +maintainers: + - Manivannan Sadhasivam + - Cristian Ciocaltea + +description: | + ATC260x series PMICs integrates Audio Codec, Power Management, RTC, IR + and GPIO controller blocks. Currently only the PM related functionalities + (i.e. regulators and system power-off/reboot) for the ATC2603C and ATC2609A + chip variants are supported. + ATC2603C includes 3 programmable DC-DC converters and 9 LDO regulators. + ATC2609A includes 5 programmable DC-DC converters and 10 LDO regulators. + +allOf: + - $ref: ../input/input.yaml + +properties: + compatible: + enum: + - actions,atc2603c + - actions,atc2609a + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-time-sec: + description: | + Duration in seconds which the key should be kept pressed for device + to reset automatically. The hardware default is 8. Use 0 to disable + this functionality. + enum: [0, 6, 8, 10, 12] + + regulators: + type: object + description: | + List of child nodes specifying the regulators, depending on chip variant: + * ATC2603C: dcdc[1-3], ldo[1-3,5-8,11], switchldo1 + * ATC2609A: dcdc[0-4], ldo[0-9] + + properties: + compatible: + enum: + - actions,atc2603c-regulator + - actions,atc2609a-regulator + + switchldo1: + type: object + $ref: ../regulator/regulator.yaml + + properties: + regulator-name: true + regulator-boot-on: true + regulator-always-on: true + regulator-min-microvolt: true + regulator-max-microvolt: true + regulator-allow-bypass: true + regulator-active-discharge: true + + additionalProperties: false + + patternProperties: + "^(dcdc[0-4]|ldo[0-9]|ldo11|switchldo1)-supply$": + description: ATC260x voltage regulators supplies + + "^(dcdc[0-4]|ldo[0-9]|ldo11)$": + type: object + $ref: ../regulator/regulator.yaml + + properties: + regulator-name: true + regulator-boot-on: true + regulator-always-on: true + regulator-min-microvolt: true + regulator-max-microvolt: true + regulator-allow-bypass: true + + additionalProperties: false + + allOf: + - if: + properties: + compatible: + contains: + const: actions,atc2603c-regulator + then: + patternProperties: + "^(dcdc[0,4]|ldo[0,4,9])(-supply)?$": false + + "^(ldo|dcdc)": + properties: + regulator-allow-bypass: false + - if: + properties: + compatible: + contains: + const: actions,atc2609a-regulator + then: + patternProperties: + "^(ldo11|switchldo1)(-supply)?$": false + + "^(dcdc|ldo[3-9])": + properties: + regulator-allow-bypass: false + + required: + - compatible + + additionalProperties: false + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + pmic@65 { + compatible = "actions,atc2603c"; + reg = <0x65>; + interrupt-parent = <&sirq>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + + reset-time-sec = <6>; + + regulators { + compatible = "actions,atc2603c-regulator"; + + dcdc1-supply = <®_5v0>; + dcdc3-supply = <®_5v0>; + ldo5-supply = <®_5v0>; + switchldo1-supply = <&vcc>; + + vdd_cpu: dcdc1 { + regulator-name = "VDD_CPU"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + }; + + vcc: dcdc3 { + regulator-name = "VCC"; + regulator-min-microvolt = <2600000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vcc_3v1: ldo5 { + regulator-name = "VCC_3V1"; + regulator-min-microvolt = <2600000>; + regulator-max-microvolt = <3300000>; + }; + + sd_vcc: switchldo1 { + regulator-name = "SD_VCC"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; + }; + +... From patchwork Sun Dec 6 01:27:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Ciocaltea X-Patchwork-Id: 338943 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F03B0C2BB3F for ; Sun, 6 Dec 2020 01:28:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C67902333E for ; Sun, 6 Dec 2020 01:28:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726731AbgLFB2B (ORCPT ); Sat, 5 Dec 2020 20:28:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49720 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725920AbgLFB2A (ORCPT ); Sat, 5 Dec 2020 20:28:00 -0500 Received: from mail-ej1-x642.google.com (mail-ej1-x642.google.com [IPv6:2a00:1450:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3FF36C061A51; Sat, 5 Dec 2020 17:27:19 -0800 (PST) Received: by mail-ej1-x642.google.com with SMTP id jx16so14259319ejb.10; Sat, 05 Dec 2020 17:27:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=TsVKFsOeLdK2j1YtPacILbkAMjBSkNql8cYyZO9ql44=; b=mmSH6522AWrHY9OgkfXiFC0Pf61NdGBBh8RzL9TxUqvin24TSBTg3CfVhGjl+0gApj 93hr3Y04+8LXzJgJLhAc8BHv5I0t4hyDjq0y6tmRWeYrTkhPFPTrFt2fgXeFuxUp3Oik 2i8AfvKk5vvbu5B8Rh0VBZXgHrZ4GkMX3FUdtjgdPRsUH+BdWVvbPhm9wVf3Fj3FnW3v Tzv9TkvP3If/CnGIA7eVVaamcz0AWVmkCUICI1zWKKC7gQMKNYT5hDWqNtx4st9ksHqe 05sbMagwRqP6cBO/beoePZ4dPKGcKCpRwclMWU/SutujTjhJfl8LK6Q50OBi5pa6GYgJ ylHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=TsVKFsOeLdK2j1YtPacILbkAMjBSkNql8cYyZO9ql44=; b=AAnZZX9p663KSO0mAH0T5OQIj/P1Xz52WglPnMCS/gm20b4PoS5aQUSfU6VMoe4aw/ YjT2XsSGbt79nKntt5ZzBSKP/yHppbntQUJNJIbZAKcmFqpqVuTTnTGTPMw2ch2s3eio yMUtHUMI0LfC17Be+SZ4uqB1ne9stMgX1QbduEExU0BesjJHcyOcPjXMCOAc8PhGRl8t QwY+kcVq202DQ2ZVQ62tgFBjoR/TymHm5oyfVk+/panfCxBQasfXi49ah4RfmVkx1UOF u7oNQcEZ/3MqoXw+bLUN21q6SZBcnGlcwD0QqeZSMchDtnC6lYabGfSYfRb7QhcDE2QK f4JQ== X-Gm-Message-State: AOAM533eN0ZCEZ6bIazNNqkQIQYiYofzhO+Zn6Qh3v/fjUSpteyLod8c LTXzq93fov5kaH5V1AfpbNE= X-Google-Smtp-Source: ABdhPJwFSu5WrcdnRwqiqYucrE8xh4b0lZ3/Bfo23Ahi2uvE4rwGthHZ5exbna0vUJJv0Hivuej3Gw== X-Received: by 2002:a17:907:20cc:: with SMTP id qq12mr14082021ejb.316.1607218037949; Sat, 05 Dec 2020 17:27:17 -0800 (PST) Received: from localhost.localdomain ([188.24.159.61]) by smtp.gmail.com with ESMTPSA id a10sm6157023ejk.92.2020.12.05.17.27.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Dec 2020 17:27:17 -0800 (PST) From: Cristian Ciocaltea To: Lee Jones , Rob Herring , Dmitry Torokhov , Sebastian Reichel , Mark Brown , Manivannan Sadhasivam , Liam Girdwood , =?utf-8?q?Andreas_F=C3=A4rber?= Cc: linux-actions@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v3 4/7] regulator: Add regulator driver for ATC260x PMICs Date: Sun, 6 Dec 2020 03:27:04 +0200 Message-Id: X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Add support for the DC-DC converters and LDO regulators found in the ATC2603C and ATC2609A chip variants of the Actions Semi ATC260x family of PMICs. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea --- Changes in v3: - Dropped 'last_dcdc_reg_id' from 'atc260x_regulator_data' and, instead, provided separate ops for DCDCs and LDOs, as recommended by Mark - Removed the unnecessary compatibles, per Mark's review - Added 'Co-developed-by' tag in commit message and dropped [cristian: ...] line drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/atc260x-regulator.c | 524 ++++++++++++++++++++++++++ 3 files changed, 533 insertions(+) create mode 100644 drivers/regulator/atc260x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 020a00d6696b..50505de6be70 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -170,6 +170,14 @@ config REGULATOR_AS3722 AS3722 PMIC. This will enable support for all the software controllable DCDC/LDO regulators. +config REGULATOR_ATC260X + tristate "Actions Semi ATC260x PMIC Regulators" + depends on MFD_ATC260X + help + This driver provides support for the voltage regulators on the + ATC260x PMICs. This will enable support for all the software + controllable DCDC/LDO regulators. + config REGULATOR_AXP20X tristate "X-POWERS AXP20X PMIC Regulators" depends on MFD_AXP20X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6ebae516258e..673ff5895aae 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o +obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o diff --git a/drivers/regulator/atc260x-regulator.c b/drivers/regulator/atc260x-regulator.c new file mode 100644 index 000000000000..8bdfc8643886 --- /dev/null +++ b/drivers/regulator/atc260x-regulator.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Regulator driver for ATC260x PMICs +// +// Copyright (C) 2019 Manivannan Sadhasivam +// Copyright (C) 2020 Cristian Ciocaltea + +#include +#include +#include +#include +#include + +struct atc260x_regulator_data { + int voltage_time_dcdc; + int voltage_time_ldo; +}; + +static const struct linear_range atc2603c_dcdc_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(1300000, 0, 13, 50000), + REGULATOR_LINEAR_RANGE(1950000, 14, 15, 100000), +}; + +static const struct linear_range atc2609a_dcdc_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 127, 6250), + REGULATOR_LINEAR_RANGE(1400000, 128, 232, 25000), +}; + +static const struct linear_range atc2609a_ldo_voltage_ranges0[] = { + REGULATOR_LINEAR_RANGE(700000, 0, 15, 100000), + REGULATOR_LINEAR_RANGE(2100000, 16, 28, 100000), +}; + +static const struct linear_range atc2609a_ldo_voltage_ranges1[] = { + REGULATOR_LINEAR_RANGE(850000, 0, 15, 100000), + REGULATOR_LINEAR_RANGE(2100000, 16, 27, 100000), +}; + +static const unsigned int atc260x_ldo_voltage_range_sel[] = { + 0x0, 0x1, +}; + +static int atc260x_dcdc_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct atc260x_regulator_data *data = rdev_get_drvdata(rdev); + + if (new_selector > old_selector) + return data->voltage_time_dcdc; + + return 0; +} + +static int atc260x_ldo_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct atc260x_regulator_data *data = rdev_get_drvdata(rdev); + + if (new_selector > old_selector) + return data->voltage_time_ldo; + + return 0; +} + +static const struct regulator_ops atc260x_dcdc_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_bypass_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, +}; + +static const struct regulator_ops atc260x_ldo_bypass_discharge_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, +}; + +static const struct regulator_ops atc260x_dcdc_range_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_range_pick_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_pickable_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_pickable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, + .set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_dcdc_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel, +}; + +/* + * ATC2603C notes: + * - LDO8 is not documented in datasheet (v2.4), but supported + * in the vendor's driver implementation (xapp-le-kernel). + * - LDO12 mentioned in datasheet is not programmable, hence not + * handled in this driver. + */ +enum atc2603c_reg_ids { + ATC2603C_ID_DCDC1, + ATC2603C_ID_DCDC2, + ATC2603C_ID_DCDC3, + ATC2603C_ID_LDO1, + ATC2603C_ID_LDO2, + ATC2603C_ID_LDO3, + ATC2603C_ID_LDO5, + ATC2603C_ID_LDO6, + ATC2603C_ID_LDO7, + ATC2603C_ID_LDO8, + ATC2603C_ID_LDO11, + ATC2603C_ID_SWITCHLDO1, + ATC2603C_ID_MAX, +}; + +#define atc2603c_reg_desc_dcdc(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_DCDC##num, \ + .ops = &atc260x_dcdc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .enable_mask = BIT(15), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_dcdc_range(num, vsel_h, vsel_l) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_DCDC##num, \ + .ops = &atc260x_dcdc_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .n_voltages = 16, \ + .linear_ranges = atc2603c_dcdc_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(atc2603c_dcdc_voltage_ranges), \ + .vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .enable_mask = BIT(15), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_dcdc_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_DCDC##num, \ + .ops = &atc260x_dcdc_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_LDO##num, \ + .ops = &atc260x_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_LDO##num##_CTL, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_LDO##num, \ + .ops = &atc260x_ldo_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo_switch(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "SWITCHLDO"#num, \ + .supply_name = "switchldo"#num, \ + .of_match = of_match_ptr("switchldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_SWITCHLDO##num, \ + .ops = &atc260x_ldo_bypass_discharge_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_SWITCH_CTL, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_SWITCH_CTL, \ + .enable_mask = BIT(15), \ + .enable_is_inverted = true, \ + .enable_time = 2000, \ + .bypass_reg = ATC2603C_PMU_SWITCH_CTL, \ + .bypass_mask = BIT(5), \ + .active_discharge_reg = ATC2603C_PMU_SWITCH_CTL, \ + .active_discharge_mask = BIT(1), \ + .owner = THIS_MODULE, \ +} + +static const struct regulator_desc atc2603c_reg[] = { + atc2603c_reg_desc_dcdc_fixed(1, 700000, 25000, 29, 11, 7), + atc2603c_reg_desc_dcdc_range(2, 12, 8), + atc2603c_reg_desc_dcdc_fixed(3, 2600000, 100000, 8, 11, 9), + atc2603c_reg_desc_ldo_fixed(1, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_fixed(2, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_fixed(3, 1500000, 100000, 6, 15, 13), + atc2603c_reg_desc_ldo(5, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_fixed(6, 700000, 25000, 29, 15, 11), + atc2603c_reg_desc_ldo(7, 1500000, 100000, 6, 15, 13), + atc2603c_reg_desc_ldo(8, 2300000, 100000, 11, 15, 12), + atc2603c_reg_desc_ldo_fixed(11, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_switch(1, 3000000, 100000, 4, 4, 3), +}; + +static const struct regulator_desc atc2603c_reg_dcdc2_ver_b = + atc2603c_reg_desc_dcdc(2, 1000000, 50000, 18, 12, 8); + +enum atc2609a_reg_ids { + ATC2609A_ID_DCDC0, + ATC2609A_ID_DCDC1, + ATC2609A_ID_DCDC2, + ATC2609A_ID_DCDC3, + ATC2609A_ID_DCDC4, + ATC2609A_ID_LDO0, + ATC2609A_ID_LDO1, + ATC2609A_ID_LDO2, + ATC2609A_ID_LDO3, + ATC2609A_ID_LDO4, + ATC2609A_ID_LDO5, + ATC2609A_ID_LDO6, + ATC2609A_ID_LDO7, + ATC2609A_ID_LDO8, + ATC2609A_ID_LDO9, + ATC2609A_ID_MAX, +}; + +#define atc2609a_reg_desc_dcdc(num, en_bit) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_DCDC##num, \ + .ops = &atc260x_dcdc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 600000, \ + .uV_step = 6250, \ + .n_voltages = 256, \ + .vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(15, 8), \ + .enable_reg = ATC2609A_PMU_DC_OSC, \ + .enable_mask = BIT(en_bit), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_dcdc_range(num, en_bit) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_DCDC##num, \ + .ops = &atc260x_dcdc_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .n_voltages = 233, \ + .linear_ranges = atc2609a_dcdc_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(atc2609a_dcdc_voltage_ranges), \ + .vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(15, 8), \ + .enable_reg = ATC2609A_PMU_DC_OSC, \ + .enable_mask = BIT(en_bit), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo(num) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 700000, \ + .uV_step = 100000, \ + .n_voltages = 16, \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_mask = GENMASK(4, 1), \ + .enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo_bypass(num) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_bypass_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 2300000, \ + .uV_step = 100000, \ + .n_voltages = 12, \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_mask = GENMASK(5, 2), \ + .enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .bypass_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .bypass_mask = BIT(1), \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo_range_pick(num, n_range) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_range_pick_ops, \ + .type = REGULATOR_VOLTAGE, \ + .linear_ranges = atc2609a_ldo_voltage_ranges##n_range, \ + .n_linear_ranges = ARRAY_SIZE(atc2609a_ldo_voltage_ranges##n_range), \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_mask = GENMASK(4, 1), \ + .vsel_range_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_range_mask = BIT(5), \ + .linear_range_selectors = atc260x_ldo_voltage_range_sel, \ + .enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo_fixed(num) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 2600000, \ + .uV_step = 100000, \ + .n_voltages = 8, \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL, \ + .vsel_mask = GENMASK(15, 13), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +static const struct regulator_desc atc2609a_reg[] = { + atc2609a_reg_desc_dcdc(0, 4), + atc2609a_reg_desc_dcdc(1, 5), + atc2609a_reg_desc_dcdc(2, 6), + atc2609a_reg_desc_dcdc_range(3, 7), + atc2609a_reg_desc_dcdc(4, 8), + atc2609a_reg_desc_ldo_bypass(0), + atc2609a_reg_desc_ldo_bypass(1), + atc2609a_reg_desc_ldo_bypass(2), + atc2609a_reg_desc_ldo_range_pick(3, 0), + atc2609a_reg_desc_ldo_range_pick(4, 0), + atc2609a_reg_desc_ldo(5), + atc2609a_reg_desc_ldo_range_pick(6, 1), + atc2609a_reg_desc_ldo_range_pick(7, 0), + atc2609a_reg_desc_ldo_range_pick(8, 0), + atc2609a_reg_desc_ldo_fixed(9), +}; + +static int atc260x_regulator_probe(struct platform_device *pdev) +{ + struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent); + struct device *dev = atc260x->dev; + struct atc260x_regulator_data *atc260x_data; + struct regulator_config config = {}; + struct regulator_dev *atc260x_rdev; + const struct regulator_desc *regulators; + bool atc2603c_ver_b = false; + int i, nregulators; + + atc260x_data = devm_kzalloc(&pdev->dev, sizeof(*atc260x_data), GFP_KERNEL); + if (!atc260x_data) + return -ENOMEM; + + atc260x_data->voltage_time_dcdc = 350; + atc260x_data->voltage_time_ldo = 800; + + switch (atc260x->ic_type) { + case ATC2603C: + regulators = atc2603c_reg; + nregulators = ATC2603C_ID_MAX; + atc2603c_ver_b = atc260x->ic_ver == ATC260X_B; + break; + case ATC2609A: + atc260x_data->voltage_time_dcdc = 250; + regulators = atc2609a_reg; + nregulators = ATC2609A_ID_MAX; + break; + default: + dev_err(dev, "unsupported ATC260X ID %d\n", atc260x->ic_type); + return -EINVAL; + } + + config.dev = dev; + config.regmap = atc260x->regmap; + config.driver_data = atc260x_data; + + /* Instantiate the regulators */ + for (i = 0; i < nregulators; i++) { + if (atc2603c_ver_b && regulators[i].id == ATC2603C_ID_DCDC2) + atc260x_rdev = devm_regulator_register(&pdev->dev, + &atc2603c_reg_dcdc2_ver_b, + &config); + else + atc260x_rdev = devm_regulator_register(&pdev->dev, + ®ulators[i], + &config); + if (IS_ERR(atc260x_rdev)) { + dev_err(dev, "failed to register regulator: %d\n", i); + return PTR_ERR(atc260x_rdev); + } + } + + return 0; +} + +static struct platform_driver atc260x_regulator_driver = { + .probe = atc260x_regulator_probe, + .driver = { + .name = "atc260x-regulator", + }, +}; + +module_platform_driver(atc260x_regulator_driver); + +MODULE_DESCRIPTION("Regulator driver for ATC260x PMICs"); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_AUTHOR("Cristian Ciocaltea "); +MODULE_LICENSE("GPL"); From patchwork Sun Dec 6 01:27:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Ciocaltea X-Patchwork-Id: 338941 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9A9E9C2BB40 for ; Sun, 6 Dec 2020 01:28:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 72B492332F for ; Sun, 6 Dec 2020 01:28:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727077AbgLFB2d (ORCPT ); Sat, 5 Dec 2020 20:28:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726263AbgLFB2d (ORCPT ); Sat, 5 Dec 2020 20:28:33 -0500 Received: from mail-ej1-x642.google.com (mail-ej1-x642.google.com [IPv6:2a00:1450:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C6FEBC061A52; Sat, 5 Dec 2020 17:27:20 -0800 (PST) Received: by mail-ej1-x642.google.com with SMTP id f23so14319317ejk.2; Sat, 05 Dec 2020 17:27:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=X0mFFW8+RqfafceYKLBpkIqdwXWiDGAdq1UWNTRc/Mc=; b=jAH96Dajq1QM49qItr8KKOnmRaPcjvygJZVvvjPC6fOu5xaFD2EP69H3aITiwKH1Gj pPZH9zWLZnTSI2SDzTV42/hjzphsrfLZnfp6nSn1ebdj0zPJP4UbwKMpPwJ+jG7K39Ks ik9TLUPVSU/gqjLxra+OtEAT6z4kAbMEhzU54L6fjDYJazoQbekqUXDpUVyvH98+EDQj DAOn6CGKI4vJQXfeOd8LgQ5dA2sA0oXCCKyaBOG0oKepBlQhQCae7tyqFiFf3Cr1BKji kJATObx2a7C7nCv0Jyg31p4ZgrhTK2vOEDjBe4t+MLrTm2mue5HcGXNxdYI4IXZduCUC 6cNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=X0mFFW8+RqfafceYKLBpkIqdwXWiDGAdq1UWNTRc/Mc=; b=GyFv4FXya8gFqNzyJQxGrRylVk6AtMXYF9tAr4a7gdBAUo/hkhAQgeRglkcCqh5WXA +ky+Md7EKjFAPbaqI/W66LnL9LKN/DUat/jLF0hTDChz7v8+1fBlmkwGEg4fG1NbWja/ Bj4FZnWt6BFtPLgThURcsilk7GLbpNzEuryxx40ryHGdn1UggUD/nk8HGN3R8KLi9Sto FOEjbzFt9k2MBFpYMrj848Jefweibps2Kb/ErIWKGv72slbrWcQn/cvBoipdr2VgImja YeSYlxXBiCIWUD7DMAiRO5jWIt6hqLC+N4YuA1hcPOUHPT2i6fjmUImznBkcBrL6Kh3r a5bA== X-Gm-Message-State: AOAM530j5an37SChG3xO3VSESfbWDlzB2C5WrDq59EImykRFHbykh6LC 3uwL/kJae5tD2tf3/rQjegs= X-Google-Smtp-Source: ABdhPJyX/gnvE+bYD+TQk07MXixRnm8nLhpRgXiXFkENelvfRa3mA/kAMT+eE0MyacpTasiAjQr0Ag== X-Received: by 2002:a17:906:7f11:: with SMTP id d17mr13505368ejr.534.1607218039535; Sat, 05 Dec 2020 17:27:19 -0800 (PST) Received: from localhost.localdomain ([188.24.159.61]) by smtp.gmail.com with ESMTPSA id a10sm6157023ejk.92.2020.12.05.17.27.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Dec 2020 17:27:18 -0800 (PST) From: Cristian Ciocaltea To: Lee Jones , Rob Herring , Dmitry Torokhov , Sebastian Reichel , Mark Brown , Manivannan Sadhasivam , Liam Girdwood , =?utf-8?q?Andreas_F=C3=A4rber?= Cc: linux-actions@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v3 5/7] power: reset: Add poweroff driver for ATC260x PMICs Date: Sun, 6 Dec 2020 03:27:05 +0200 Message-Id: X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org 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 --- Changes in v3: - Removed the unnecessary driver compatibles drivers/power/reset/Kconfig | 8 +- drivers/power/reset/Makefile | 1 + drivers/power/reset/atc260x-poweroff.c | 263 +++++++++++++++++++++++++ 3 files changed, 271 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 d55b3727e00e..87cc1a4f43b8 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 @@ -285,4 +292,3 @@ config NVMEM_REBOOT_MODE action according to the mode. endif - diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index c51eceba9ea3..829df1157540 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..81b050f99302 --- /dev/null +++ b/drivers/power/reset/atc260x-poweroff.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Poweroff & reset driver for Actions Semi ATC260x PMICs + * + * Copyright (c) 2020 Cristian Ciocaltea + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_LICENSE("GPL"); From patchwork Sun Dec 6 01:27:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Ciocaltea X-Patchwork-Id: 338942 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C17C6C433FE for ; Sun, 6 Dec 2020 01:28:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9531F2313B for ; Sun, 6 Dec 2020 01:28:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727108AbgLFB2d (ORCPT ); Sat, 5 Dec 2020 20:28:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49824 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726408AbgLFB2d (ORCPT ); Sat, 5 Dec 2020 20:28:33 -0500 Received: from mail-ej1-x643.google.com (mail-ej1-x643.google.com [IPv6:2a00:1450:4864:20::643]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 75D69C061A53; Sat, 5 Dec 2020 17:27:22 -0800 (PST) Received: by mail-ej1-x643.google.com with SMTP id ce23so10518545ejb.8; Sat, 05 Dec 2020 17:27:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=tF4tSIpvjbqoEmCAjZzirlpixzPvlM5k05FCh/y/0oM=; b=OetViarZUy2cMEU4fl8iUVcS76skY655d6j3vu3v8J+R8LD2UGcGRu48NZFJI66JYr D0Utsm6qpvGJgb8HBQof4YPAdperNW9QW6F4Or0wBZcmFs2gnc5txe0lGcpoHoW20Jcf 1crZtQCB2eCJmpUPDg21nBESwuKXCRHYaoQgThhnSpki1vipYmUZIWzbpI6KQi2H3rPM HPBKiCXmfRYZI04uho4CxjcBolx6iSe9Hc2jYisIzc71KvrvWPFB/AIm4vUhbJAMlAF2 Zz63WdR7gLAtlgmitdEfX3+gMp397FsWr2nE4aTx51ds5LWoODYVWFVHvmESydI9Bv90 QYqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=tF4tSIpvjbqoEmCAjZzirlpixzPvlM5k05FCh/y/0oM=; b=H8adoogTuTYBAv+NSNWz2JTeGMrzYFUPShffIPAdrFx+f8mbjFFeDC/myynMuc1ozO 9t1NdXpB6Q63WjDnUqkf3pNVGBJ9vW+ZwpIS6VW6p6mu7TVSaWDDgaA89szhukbJ6u25 hv/STGkIX/WO/e4JhDMbk+7wD99TcE4pO53Z1cdxushqzD3QnunFgKNt66kkX5Wy5Ttr YYhGXTYB7dABnPk6q+wrmsZ4H4zkh8BKThoMbX9s4k8KGJyJ5KA37Qi+PwXzQWOLDl3E kNHyQqi1x4TfIYADTolSK/XXnVQt8ik/4iMqZiTJvFmzBGgSPSKyJP7/1RP79bA+Q5wO B7wg== X-Gm-Message-State: AOAM533JBgka/OrFX3qFY3vNY2bbxRPUvPjVgpTuDNZnfFJhLVUStKs8 rQt8z6hxILaHvqujzPGgYsk= X-Google-Smtp-Source: ABdhPJxseB0+EJPxnt0jhk+QLd7WO2UsTWwpE0QEcDFdBBLc0ujyQvXScTczMs2qvweHtTGjOg3FEw== X-Received: by 2002:a17:906:175b:: with SMTP id d27mr13565040eje.260.1607218041095; Sat, 05 Dec 2020 17:27:21 -0800 (PST) Received: from localhost.localdomain ([188.24.159.61]) by smtp.gmail.com with ESMTPSA id a10sm6157023ejk.92.2020.12.05.17.27.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Dec 2020 17:27:20 -0800 (PST) From: Cristian Ciocaltea To: Lee Jones , Rob Herring , Dmitry Torokhov , Sebastian Reichel , Mark Brown , Manivannan Sadhasivam , Liam Girdwood , =?utf-8?q?Andreas_F=C3=A4rber?= Cc: linux-actions@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v3 6/7] input: atc260x: Add onkey driver for ATC260x PMICs Date: Sun, 6 Dec 2020 03:27:06 +0200 Message-Id: X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The Actions Semi ATC260x PMICs are able to manage an onkey button. This driver exposes the ATC260x onkey as an input device. It can also be configured to force a system reset on a long key-press with an adjustable duration. The currently supported chip variants are ATC2603C and ATC2609A. Signed-off-by: Cristian Ciocaltea --- Changes in v3: - Integrated Dmitry's review: * Dropped the logic around the check for 'pdev->dev.of_node' * Renamed 'ret' variable in 'atc260x_onkey_probe()' to 'error' * Used 'dev_dbg' instead of 'dev_info' on status KEY_RESET_DISABLED * Dropped 'input_dev->evbit[0] = BIT_MASK(EV_KEY);' * Removed IRQF_TRIGGER_HIGH flag on 'devm_request_threaded_irq()' * Implemented open/close to mitigate the racing issue in 'atc260x_onkey_remove()' which has been dropped now - Removed the unnecessary driver compatibles - Used 'reset-time-sec' property of the parent device instead of 'actions,reset-time-sec' of now obsolete and removed 'onkey' DT node drivers/input/misc/Kconfig | 11 ++ drivers/input/misc/Makefile | 2 +- drivers/input/misc/atc260x-onkey.c | 305 +++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 drivers/input/misc/atc260x-onkey.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 362e8a01980c..9e297ebdea57 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -83,6 +83,17 @@ config INPUT_ARIZONA_HAPTICS To compile this driver as a module, choose M here: the module will be called arizona-haptics. +config INPUT_ATC260X_ONKEY + tristate "Actions Semi ATC260x PMIC ONKEY" + depends on MFD_ATC260X + help + Support the ONKEY of ATC260x PMICs as an input device reporting + power button status. ONKEY can be used to wakeup from low power + modes and force a reset on long press. + + To compile this driver as a module, choose M here: the + module will be called atc260x-onkey. + config INPUT_ATMEL_CAPTOUCH tristate "Atmel Capacitive Touch Button Driver" depends on OF || COMPILE_TEST diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a48e5f2d859d..7f854c6ecefa 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o +obj-$(CONFIG_INPUT_ATC260X_ONKEY) += atc260x-onkey.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o @@ -84,4 +85,3 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o - diff --git a/drivers/input/misc/atc260x-onkey.c b/drivers/input/misc/atc260x-onkey.c new file mode 100644 index 000000000000..999aabf9dcbd --- /dev/null +++ b/drivers/input/misc/atc260x-onkey.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Onkey driver for Actions Semi ATC260x PMICs. + * + * Copyright (c) 2020 Cristian Ciocaltea + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* <2s for short press, >2s for long press */ +#define KEY_PRESS_TIME_SEC 2 + +/* Driver internals */ +enum atc260x_onkey_reset_status { + KEY_RESET_HW_DEFAULT, + KEY_RESET_DISABLED, + KEY_RESET_USER_SEL, +}; + +struct atc260x_onkey_params { + u32 reg_int_ctl; + u32 kdwn_state_bm; + u32 long_int_pnd_bm; + u32 short_int_pnd_bm; + u32 kdwn_int_pnd_bm; + u32 press_int_en_bm; + u32 kdwn_int_en_bm; + u32 press_time_bm; + u32 reset_en_bm; + u32 reset_time_bm; +}; + +struct atc260x_onkey { + struct atc260x *atc260x; + const struct atc260x_onkey_params *params; + struct input_dev *input_dev; + struct delayed_work work; + int irq; +}; + +static const struct atc260x_onkey_params atc2603c_onkey_params = { + .reg_int_ctl = ATC2603C_PMU_SYS_CTL2, + .long_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS, + .short_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS, + .kdwn_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD, + .press_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN, + .kdwn_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN, + .kdwn_state_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS, + .press_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME, + .reset_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN, + .reset_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, +}; + +static const struct atc260x_onkey_params atc2609a_onkey_params = { + .reg_int_ctl = ATC2609A_PMU_SYS_CTL2, + .long_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS, + .short_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS, + .kdwn_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD, + .press_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN, + .kdwn_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN, + .kdwn_state_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS, + .press_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME, + .reset_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN, + .reset_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, +}; + +static int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey, + enum atc260x_onkey_reset_status reset_status, + u32 reset_time, u32 press_time) +{ + u32 reg_bm, reg_val; + + reg_bm = onkey->params->long_int_pnd_bm | + onkey->params->short_int_pnd_bm | + onkey->params->kdwn_int_pnd_bm | + onkey->params->press_int_en_bm | + onkey->params->kdwn_int_en_bm; + + reg_val = reg_bm | press_time; + reg_bm |= onkey->params->press_time_bm; + + if (reset_status == KEY_RESET_DISABLED) { + reg_bm |= onkey->params->reset_en_bm; + } else if (reset_status == KEY_RESET_USER_SEL) { + reg_bm |= onkey->params->reset_en_bm | + onkey->params->reset_time_bm; + reg_val |= onkey->params->reset_en_bm | reset_time; + } + + return regmap_update_bits(onkey->atc260x->regmap, + onkey->params->reg_int_ctl, reg_bm, reg_val); +} + +static void atc260x_onkey_query(struct atc260x_onkey *onkey) +{ + u32 reg_bits; + int ret, key_down; + + ret = regmap_read(onkey->atc260x->regmap, + onkey->params->reg_int_ctl, &key_down); + if (ret) { + key_down = 1; + dev_err(onkey->atc260x->dev, + "Failed to read onkey status: %d\n", ret); + } else { + key_down &= onkey->params->kdwn_state_bm; + } + + /* + * The hardware generates interrupt only when the onkey pin is + * asserted. Hence, the deassertion of the pin is simulated through + * work queue. + */ + if (key_down) { + schedule_delayed_work(&onkey->work, msecs_to_jiffies(200)); + return; + } + + /* + * The key-down status bit is cleared when the On/Off button + * is released. + */ + input_report_key(onkey->input_dev, KEY_POWER, 0); + input_sync(onkey->input_dev); + + reg_bits = onkey->params->long_int_pnd_bm | + onkey->params->short_int_pnd_bm | + onkey->params->kdwn_int_pnd_bm | + onkey->params->press_int_en_bm | + onkey->params->kdwn_int_en_bm; + + /* Clear key press pending events and enable key press interrupts. */ + regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl, + reg_bits, reg_bits); +} + +static void atc260x_onkey_work(struct work_struct *work) +{ + struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey, + work.work); + atc260x_onkey_query(onkey); +} + +static irqreturn_t atc260x_onkey_irq(int irq, void *data) +{ + struct atc260x_onkey *onkey = data; + int ret; + + /* Disable key press interrupts. */ + ret = regmap_update_bits(onkey->atc260x->regmap, + onkey->params->reg_int_ctl, + onkey->params->press_int_en_bm | + onkey->params->kdwn_int_en_bm, 0); + if (ret) + dev_err(onkey->atc260x->dev, + "Failed to disable interrupts: %d\n", ret); + + input_report_key(onkey->input_dev, KEY_POWER, 1); + input_sync(onkey->input_dev); + + atc260x_onkey_query(onkey); + + return IRQ_HANDLED; +} + +static int atc260x_onkey_open(struct input_dev *dev) +{ + struct atc260x_onkey *onkey = input_get_drvdata(dev); + + enable_irq(onkey->irq); + + return 0; +} + +static void atc260x_onkey_close(struct input_dev *dev) +{ + struct atc260x_onkey *onkey = input_get_drvdata(dev); + + disable_irq(onkey->irq); + cancel_delayed_work_sync(&onkey->work); +} + +static int atc260x_onkey_probe(struct platform_device *pdev) +{ + struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent); + struct atc260x_onkey *onkey; + struct input_dev *input_dev; + enum atc260x_onkey_reset_status reset_status; + u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0; + int val, error; + + onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL); + if (!onkey) + return -ENOMEM; + + error = device_property_read_u32(pdev->dev.parent, + "reset-time-sec", &val); + if (error) { + reset_status = KEY_RESET_HW_DEFAULT; + } else if (val) { + if (val < 6 || val > 12) { + dev_err(&pdev->dev, "reset-time-sec out of range\n"); + return -EINVAL; + } + + reset_status = KEY_RESET_USER_SEL; + reset_time = (val - 6) / 2; + } else { + reset_status = KEY_RESET_DISABLED; + dev_dbg(&pdev->dev, "Disabled reset on long-press\n"); + } + + switch (atc260x->ic_type) { + case ATC2603C: + onkey->params = &atc2603c_onkey_params; + press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME, + press_time); + reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, + reset_time); + break; + case ATC2609A: + onkey->params = &atc2609a_onkey_params; + press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME, + press_time); + reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, + reset_time); + break; + default: + dev_err(&pdev->dev, + "OnKey not supported for ATC260x PMIC type: %u\n", + atc260x->ic_type); + return -EINVAL; + } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + onkey->input_dev = input_dev; + onkey->atc260x = atc260x; + + input_dev->name = "atc260x-onkey"; + input_dev->phys = "atc260x-onkey/input0"; + input_dev->open = atc260x_onkey_open; + input_dev->close = atc260x_onkey_close; + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_drvdata(input_dev, onkey); + + INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work); + + onkey->irq = platform_get_irq(pdev, 0); + if (onkey->irq < 0) + return onkey->irq; + + error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL, + atc260x_onkey_irq, IRQF_ONESHOT, + dev_name(&pdev->dev), onkey); + if (error) { + dev_err(&pdev->dev, + "Failed to register IRQ %d: %d\n", onkey->irq, error); + return error; + } + + /* Keep IRQ disabled until atc260x_onkey_open() is called. */ + disable_irq(onkey->irq); + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "Failed to register input device: %d\n", error); + return error; + } + + error = atc2603x_onkey_hw_init(onkey, reset_status, + reset_time, press_time); + if (error) + return error; + + device_init_wakeup(&pdev->dev, true); + + return 0; +} + +static struct platform_driver atc260x_onkey_driver = { + .probe = atc260x_onkey_probe, + .driver = { + .name = "atc260x-onkey", + }, +}; + +module_platform_driver(atc260x_onkey_driver); + +MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs"); +MODULE_AUTHOR("Cristian Ciocaltea "); +MODULE_LICENSE("GPL");