From patchwork Fri May 16 18:50:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Kayode X-Patchwork-Id: 890795 Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C144F1C07C4; Fri, 16 May 2025 18:50:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=208.88.110.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421444; cv=none; b=uurRKFC6t2bUQb0eNH1T2upP3ZZFCuxyZgWRnoQpW/nGMpxmiRtJjKKZH25dq0hLcHv8tCsnt5D+8Q1moUvYRavt7FaBEgUFWd5TtPeaB78az8n4doXeXPufU92ei6m984zrxLnULXU67PJzcFUeTyHvlA9Pv81Z5YrnrROydgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421444; c=relaxed/simple; bh=hAiZr/LrH8Xjb2ZGwWO9jhrBh+oBiAjAWL90TRbTYoA=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=YMrFbZWBxyQmwzzZNYPIsor4iQizIA0uIcGzHc6fMlu3RKwR5wpJFlzpIJBtbPfqpCg2W4kB7+bs8s/qbtHeMFvn4inpHAEmI6kY/f7P96okks9bOSDcvY/Xd4csfkq91J0AhtEPmmZ2XeI5qKxP2bq2lyQMuWoEr5+hAXDdP+Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com; spf=pass smtp.mailfrom=savoirfairelinux.com; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b=hkA+trwf; arc=none smtp.client-ip=208.88.110.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b="hkA+trwf" Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id BEBC29C904D; Fri, 16 May 2025 14:50:40 -0400 (EDT) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10032) with ESMTP id aSn8XPjRTwdz; Fri, 16 May 2025 14:50:40 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 3B9779C904E; Fri, 16 May 2025 14:50:40 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com 3B9779C904E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1747421440; bh=rd2/deZALZ3wtz5IdGVW0BxEhJ80xK8S7SFDzAOgLFo=; h=Date:From:To:Message-ID:MIME-Version; b=hkA+trwfCtDlqD5I16ihcNBH/U2BCKLuyN8efmeA5EepjC8YIG2etWQh+Ge2ir8CC D571Kxt9IHCfKyNB0zpmRYk2LpSMnuJqb/fqigpJdV3xFcvcptZDrk0cxuzL5df0mP Kg1TXlgaPLQ2cKi15bh9CGy4p366vy+5BIQbS7QbUK1K09RAFnWcn5ivxgaMv8yBOh EY8UittGcg0Ry8B9CgQjO5DeIpWXbvQy6Xvtk+r55TU24732nZQhB4dSTS2CfiSMI3 i+Hl9ryOwjnQCGqB7HgA7VnJAuwBuP7R4aPtnK61rENz4Ky/JACVh94kG6EWebskRN HUZh7pajUmDtQ== X-Virus-Scanned: amavis at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10026) with ESMTP id w8gP9-8bJzAQ; Fri, 16 May 2025 14:50:40 -0400 (EDT) Received: from fedora (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id EE9069C904D; Fri, 16 May 2025 14:50:39 -0400 (EDT) Date: Fri, 16 May 2025 14:50:38 -0400 From: Samuel Kayode To: Lee Jones , Liam Girdwood , Mark Brown , Dmitry Torokho , Sebastian Reichel , Robin Gong Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-imx@nxp.com, linux-input@vger.kernel.org, Abel Vesa , Abel Vesa , Robin Gong , Enric Balletbo Serra Subject: [PATCH v2 2/9] dt-bindings: regulator: add pf1550 Message-ID: <7c8ceab2aa66bf221981f689025d0bd29df8d596.1747409892.git.samuel.kayode@savoirfairelinux.com> References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add the DT binding document for pf1550 regulators. Signed-off-by: Samuel Kayode --- .../devicetree/bindings/regulator/pf1550.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/pf1550.yaml diff --git a/Documentation/devicetree/bindings/regulator/pf1550.yaml b/Documentation/devicetree/bindings/regulator/pf1550.yaml new file mode 100644 index 000000000000..a684ab974496 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/pf1550.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/pf1550.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Regulators for PF1550 PMIC from NXP. + +maintainers: + - Samuel Kayode + +description: | + This module is part of the PF1550 MFD device. For more details + see Documentation/devicetree/bindings/mfd/pf1550.yaml. + + The regulator controller is represented as a sub-node of the PMIC node + on the device tree. + + The device has three LDO regulators, three buck converters and a DDR + termination reference voltage. + +properties: + compatible: + const: fsl,pf1550-regulator + +patternProperties: + "^(LDO[1-3]|SW[1-3]|VREFADDR)$": + $ref: regulator.yaml# + unevaluatedProperties: false + +required: + - compatible +additionalProperties: false + +... From patchwork Fri May 16 18:53:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Kayode X-Patchwork-Id: 890794 Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4AB482E628; Fri, 16 May 2025 18:53:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=208.88.110.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421601; cv=none; b=HxhByyQVHpNbovGgaK6HnPSOhRv0AtOoV0dKMT1sWPFoI/gZPLhbo/mxdEVswZCcDfNLUKtVXRBotQVdF4ILLupjS8ohAXO6cyQYIYGuOAOyd1Mc5EZ/eAhe0zqR0T0xueQaM8MC8vUxoOgZjP/NKunemnQW1yY4tqHE+9InjdM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421601; c=relaxed/simple; bh=auORJAMqkW2h97zukNJ43gKm0zDET/Sz9RCe8inQFbM=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=V0MtREk+kxMvN1/NGYGql2Iw4i8GI5HqNqTCah51/KMIbC/7lPD8MFMHjqkSrvRftSG5ocLTyqvxxgffqRU2/fZXGFGyDIBp/TjCC+GjEg8IzX9X62V5qL4Y3uzH3CpFGVHwHNrDmzOEwxCAWX8EWvHYvCN4ay/tauo97ptH8jM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com; spf=pass smtp.mailfrom=savoirfairelinux.com; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b=YTZMSVx7; arc=none smtp.client-ip=208.88.110.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b="YTZMSVx7" Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id DC38F9C905E; Fri, 16 May 2025 14:53:17 -0400 (EDT) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10032) with ESMTP id 2HgMfIKfwhng; Fri, 16 May 2025 14:53:17 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 5128C9C9065; Fri, 16 May 2025 14:53:17 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com 5128C9C9065 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1747421597; bh=Rms8zTfvA6EKXoU5BydiP3qdBBh9CEpPYmc6CeTUcmo=; h=Date:From:To:Message-ID:MIME-Version; b=YTZMSVx7YT0vIcke0p4GYUJFxMX4K7DfXYJXgGpPu0+1p6AJHgWpN4R2WonPjvevu EJsHufWneODd12r6BadVnwhQSKvcDWQumipRXYm1Ep6kmf7UTTyAqxYK6h86aR7SKo fTfVUpYOBSseDSEmIrL0OzkqOOMb9yC4deMG8TebttKGtfxc0Ig1nCg/JCVahXeKkG n4/yjMPOk3Zn8ovIONHg+JvVqKtpxGeQ9fOkI1JarroGXNmA1hFuEvQk7NXTB1VANH fvj/el7YAT6GS8yNFICyDQDMMZo6CfQghN0JvW0fT/Q0WqGkh6BdyO3bHPf5DD5EKO gaZgPCDxs0Aqg== X-Virus-Scanned: amavis at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10026) with ESMTP id JDOYzqqA9BTJ; Fri, 16 May 2025 14:53:17 -0400 (EDT) Received: from fedora (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id 2BB9C9C905E; Fri, 16 May 2025 14:53:17 -0400 (EDT) Date: Fri, 16 May 2025 14:53:16 -0400 From: Samuel Kayode To: Lee Jones , Liam Girdwood , Mark Brown , Dmitry Torokhov , Sebastian Reichel , Robin Gong Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-imx@nxp.com, inux-input@vger.kernel.org, Abel Vesa , Abel Vesa , Robin Gong , Enric Balletbo Serra Subject: [PATCH v2 4/9] dt-bindings: mfd: add pf1550 Message-ID: <8be1626f970c9fab8b50ae9ad45e0ddd88fa36bf.1747409892.git.samuel.kayode@savoirfairelinux.com> References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add a DT binding document for pf1550 PMIC. This describes the core mfd device. Signed-off-by: Samuel Kayode --- .../devicetree/bindings/mfd/pf1550.yaml | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/pf1550.yaml diff --git a/Documentation/devicetree/bindings/mfd/pf1550.yaml b/Documentation/devicetree/bindings/mfd/pf1550.yaml new file mode 100644 index 000000000000..461bc13513eb --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/pf1550.yaml @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/pf1550.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: PF1550 low power PMIC from NXP. + +maintainers: + - Samuel Kayode + +description: | + PF1550 is a low power PMIC providing battery charging and power supply for + low power IoT and wearable applications. + + For device-tree bindings of other sub-modules (regulator, power supply and + onkey) refer to the binding documents under the respective sub-system + directories. + +properties: + compatible: + const: fsl,pf1550 + + reg: + description: + I2C device address. + maxItems: 1 + + interrupts: + maxItems: 1 + + regulators: + $ref: /schemas/regulator/pf1550.yaml + + charger: + $ref: /schemas/power/supply/pf1550_charger.yaml + + onkey: + $ref: /schemas/input/pf1550_onkey.yaml + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@8 { + compatible = "fsl,pf1550"; + reg = <0x8>; + + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + onkey { + compatible = "fsl,pf1550-onkey"; + linux,keycodes = ; + }; + + charger { + compatible = "fsl,pf1550-charger"; + + fsl,min-system-microvolt = <3700000>; + fsl,thermal-regulation = <75>; + }; + + regulators { + compatible = "fsl,pf1550-regulator"; + + sw1_reg: SW1 { + regulator-name = "SW1"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1387500>; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: SW2 { + regulator-name = "SW2"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1387500>; + regulator-always-on; + }; + + sw3_reg: SW3 { + regulator-name = "SW3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vldo1_reg: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vldo2_reg: LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vldo3_reg: LDO3 { + regulator-name = "LDO3"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; + }; From patchwork Fri May 16 18:55:57 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Kayode X-Patchwork-Id: 890793 Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1FE4E27D781; Fri, 16 May 2025 18:56:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=208.88.110.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421763; cv=none; b=MeJ54VHyPWo5qisqQM29XqGswpEjClbTUaVUNAv3HAsz6E/ZzjSZ5cs9ylZLrwzeSYGQoNOFetmWAwcHmCer+7uV8GT0ma98jvMfwfWpL7qJKtboFVoj2ukyVRS3aIxr3gqlUYYrNeeX3YRRSt4Bwm/jGRGS3hfb/szhar9QqmE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421763; c=relaxed/simple; bh=YcSBNtTWEN+1Njluqvk1eZewsWgnA+YCrAVwP6mP6GI=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=lSEvgLAiHpjG8mEX+hYYh+0nJjLg5cT4l3H+SSX8lld3PW1P5KdoWCZgq3UCW0CYlJFPQYYFibo+pXGvNJ3wJeGotex5Eqs/1bXmBEO1tvYwKjx8UC4E4R+TlgcGnuULmXa+m+GN9g5t+jpDjTpIhgBbGqizUNcS4nvm+vlYmBo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com; spf=pass smtp.mailfrom=savoirfairelinux.com; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b=yKghKIlT; arc=none smtp.client-ip=208.88.110.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b="yKghKIlT" Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 2E0C89C6885; Fri, 16 May 2025 14:56:00 -0400 (EDT) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10032) with ESMTP id 5WF-8qgso41Z; Fri, 16 May 2025 14:55:59 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 086E29C8AFC; Fri, 16 May 2025 14:55:59 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com 086E29C8AFC DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1747421759; bh=LElRp6m8b2exy8+zvoLctFh6JXMrdYfI5BTl1vyDZcM=; h=Date:From:To:Message-ID:MIME-Version; b=yKghKIlTPPYXI0ms9hk2RHV8vE3QsJWQvQnTEN68lGRvwjAZJpzDBuGCrOZwGtNEk UO6Q/58qj/OFbAfHGdKB8g/qhbCPqd25VBpOiIRNFcnVo3l+5r3jnL3q5gBU81wEhT 9FM33TYF6f9TApPsifwP623umACoHmrwwF26NHFubVjtGlnX/FwUaStZoujBYB34Qn 5XCek1XjxBODRaVPqoIDC7Jjo2CoQS4mylCbAwzNW7BY6IihoCaLwNk4PaR0wLNhdC NzeRAwCGC1Mp+M6TsCCF/UTaGpoK2a8nXJoywM8ACjLAVkIiHCvj1WWTmmaiWU9lSY j2x6hbUFDH53g== X-Virus-Scanned: amavis at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10026) with ESMTP id MvNcu0qfzPEy; Fri, 16 May 2025 14:55:58 -0400 (EDT) Received: from fedora (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id CD9DF9C6885; Fri, 16 May 2025 14:55:58 -0400 (EDT) Date: Fri, 16 May 2025 14:55:57 -0400 From: Samuel Kayode To: Lee Jones , Liam Girdwood , Mark Brown , Dmitry Torokhov , Sebastian Reichel , Robin Gong Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-imx@nxp.com, linux-input@vger.kernel.org, Abel Vesa , Abel Vesa , Robin Gong , Enric Balletbo Serra Subject: [PATCH v2 6/9] regulator: pf1550: add support for regulator Message-ID: <4dd316e06a66634b5af13f1faedc985753b061bc.1747409892.git.samuel.kayode@savoirfairelinux.com> References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add regulator support for the pf1550 PMIC. Signed-off-by: Samuel Kayode --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/pf1550.c | 380 +++++++++++++++++++++++++++++++++++++ 3 files changed, 388 insertions(+) create mode 100644 drivers/regulator/pf1550.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 05e32d764028..cdce1658957f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1038,6 +1038,13 @@ config REGULATOR_PV88090 Say y here to support the voltage regulators and convertors on PV88090 +config REGULATOR_PF1550 + tristate "Freescale PF1550 regulator" + depends on MFD_PF1550 + help + This driver controls a PF1550 regulator via I2C bus. + The regulators include three switch and three ldo. + config REGULATOR_PWM tristate "PWM voltage regulator" depends on PWM diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 524e026c0273..2d11c069b0ca 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -124,6 +124,7 @@ obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o obj-$(CONFIG_REGULATOR_PF9453) += pf9453-regulator.o +obj-$(CONFIG_REGULATOR_PF1550) += pf1550.o obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o diff --git a/drivers/regulator/pf1550.c b/drivers/regulator/pf1550.c new file mode 100644 index 000000000000..e7a9c3b43c12 --- /dev/null +++ b/drivers/regulator/pf1550.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pf1550.c - regulator driver for the PF1550 + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Robin Gong + * + * This driver is based on pfuze100-regulator.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PF1550_MAX_REGULATOR 7 + +struct pf1550_desc { + struct regulator_desc desc; + unsigned char stby_reg; + unsigned char stby_mask; +}; + +struct pf1550_regulator_info { + struct device *dev; + struct pf1550_dev *pf1550; + struct pf1550_desc regulator_descs[PF1550_MAX_REGULATOR]; + int irq; +}; + +static struct pf1550_irq_info pf1550_regulator_irqs[] = { + { PF1550_PMIC_IRQ_SW1_LS, "sw1-lowside" }, + { PF1550_PMIC_IRQ_SW2_LS, "sw2-lowside" }, + { PF1550_PMIC_IRQ_SW3_LS, "sw3-lowside" }, + + { PF1550_PMIC_IRQ_SW1_HS, "sw1-highside" }, + { PF1550_PMIC_IRQ_SW2_HS, "sw2-highside" }, + { PF1550_PMIC_IRQ_SW3_HS, "sw3-highside" }, + + { PF1550_PMIC_IRQ_LDO1_FAULT, "ldo1-fault" }, + { PF1550_PMIC_IRQ_LDO2_FAULT, "ldo2-fault" }, + { PF1550_PMIC_IRQ_LDO3_FAULT, "ldo3-fault" }, + + { PF1550_PMIC_IRQ_TEMP_110, "temp-110" }, + { PF1550_PMIC_IRQ_TEMP_125, "temp-125" }, +}; + +static const int pf1550_sw12_volts[] = { + 1100000, 1200000, 1350000, 1500000, 1800000, 2500000, 3000000, 3300000, +}; + +static const int pf1550_ldo13_volts[] = { + 750000, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, +}; + +static int pf1550_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + int id = rdev_get_id(rdev); + unsigned int ramp_bits; + int ret; + + if (id > PF1550_VREFDDR) + return -EACCES; + + ramp_delay = 6250 / ramp_delay; + ramp_bits = ramp_delay >> 1; + ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg + 4, 0x10, + ramp_bits << 4); + if (ret < 0) + dev_err(&rdev->dev, "ramp failed, err %d\n", ret); + + return ret; +} + +static const struct regulator_ops pf1550_sw1_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = pf1550_set_ramp_delay, +}; + +static const struct regulator_ops pf1550_sw2_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 = regulator_set_voltage_time_sel, + .set_ramp_delay = pf1550_set_ramp_delay, +}; + +static const struct regulator_ops pf1550_ldo1_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf1550_ldo2_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, +}; + +static const struct regulator_ops pf1550_fixed_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +#define PF_VREF(_chip, _name, voltage) { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 1, \ + .ops = &pf1550_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + .enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \ + .enable_mask = 0x1, \ + }, \ + .stby_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \ + .stby_mask = 0x2, \ +} + +#define PF_SW1(_chip, _name, mask, voltages) { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf1550_sw1_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \ + .vsel_mask = (mask), \ + }, \ + .stby_reg = _chip ## _PMIC_REG_ ## _name ## _STBY_VOLT, \ + .stby_mask = (mask), \ +} + +#define PF_SW3(_chip, _name, min, max, mask, step) { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pf1550_sw2_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \ + .vsel_mask = (mask), \ + }, \ + .stby_reg = _chip ## _PMIC_REG_ ## _name ## _STBY_VOLT, \ + .stby_mask = (mask), \ +} + +#define PF_LDO1(_chip, _name, mask, voltages) { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf1550_ldo1_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \ + .vsel_mask = (mask), \ + .enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \ + .enable_mask = 0x1, \ + }, \ + .stby_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \ + .stby_mask = 0x2, \ +} + +#define PF_LDO2(_chip, _name, mask, min, max, step) { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pf1550_ldo2_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \ + .vsel_mask = (mask), \ + .enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \ + .enable_mask = 0x1, \ + }, \ + .stby_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \ + .stby_mask = 0x2, \ +} + +static struct pf1550_desc pf1550_regulators[] = { + PF_SW3(PF1550, SW1, 600000, 1387500, 0x3f, 12500), + PF_SW3(PF1550, SW2, 600000, 1387500, 0x3f, 12500), + PF_SW3(PF1550, SW3, 1800000, 3300000, 0xf, 100000), + PF_VREF(PF1550, VREFDDR, 1200000), + PF_LDO1(PF1550, LDO1, 0x1f, pf1550_ldo13_volts), + PF_LDO2(PF1550, LDO2, 0xf, 1800000, 3300000, 100000), + PF_LDO1(PF1550, LDO3, 0x1f, pf1550_ldo13_volts), +}; + +static irqreturn_t pf1550_regulator_irq_handler(int irq, void *data) +{ + struct pf1550_regulator_info *info = data; + int i, irq_type = -1; + + info->irq = irq; + + for (i = 0; i < ARRAY_SIZE(pf1550_regulator_irqs); i++) + if (info->irq == pf1550_regulator_irqs[i].virq) + irq_type = pf1550_regulator_irqs[i].irq; + + switch (irq_type) { + case PF1550_PMIC_IRQ_SW1_LS: + case PF1550_PMIC_IRQ_SW2_LS: + case PF1550_PMIC_IRQ_SW3_LS: + dev_info(info->dev, "lowside interrupt triggered! irq_type=%d\n", + irq_type); + break; + case PF1550_PMIC_IRQ_SW1_HS: + case PF1550_PMIC_IRQ_SW2_HS: + case PF1550_PMIC_IRQ_SW3_HS: + dev_info(info->dev, "highside interrupt triggered! irq_type=%d\n", + irq_type); + break; + case PF1550_PMIC_IRQ_LDO1_FAULT: + case PF1550_PMIC_IRQ_LDO2_FAULT: + case PF1550_PMIC_IRQ_LDO3_FAULT: + dev_info(info->dev, "ldo fault triggered! irq_type=%d\n", + irq_type); + break; + case PF1550_PMIC_IRQ_TEMP_110: + case PF1550_PMIC_IRQ_TEMP_125: + dev_info(info->dev, "thermal exception triggered! irq_type=%d\n", + irq_type); + break; + default: + dev_err(info->dev, "regulator interrupt: irq %d occurred\n", + irq_type); + } + + return IRQ_HANDLED; +} + +static int pf1550_regulator_probe(struct platform_device *pdev) +{ + struct pf1550_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct device_node *np = pdev->dev.of_node; + struct pf1550_regulator_info *info; + int i, ret = 0; + struct regulator_config config = { }; + + if (!np) + return -ENODEV; + + info = devm_kzalloc(&pdev->dev, sizeof(struct pf1550_regulator_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + config.dev = iodev->dev; + config.regmap = iodev->regmap; + info->dev = &pdev->dev; + info->pf1550 = iodev; + + memcpy(info->regulator_descs, pf1550_regulators, + sizeof(info->regulator_descs)); + + for (i = 0; i < ARRAY_SIZE(pf1550_regulators); i++) { + struct regulator_dev *rdev; + struct regulator_desc *desc; + unsigned int val; + + desc = &info->regulator_descs[i].desc; + + if (desc->id == PF1550_SW2) { + pf1550_read_otp(info->pf1550, 0x1f, &val); + /* OTP_SW2_DVS_ENB == 1? */ + if ((val & 0x8)) { + desc->volt_table = pf1550_sw12_volts; + desc->n_voltages = ARRAY_SIZE(pf1550_sw12_volts); + desc->ops = &pf1550_sw1_ops; + } + } + + rdev = devm_regulator_register(&pdev->dev, desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "Failed to initialize regulator-%d\n", i); + return PTR_ERR(rdev); + } + } + + platform_set_drvdata(pdev, info); + + for (i = 0; i < ARRAY_SIZE(pf1550_regulator_irqs); i++) { + struct pf1550_irq_info *regulator_irq = + &pf1550_regulator_irqs[i]; + unsigned int virq = 0; + + virq = regmap_irq_get_virq(iodev->irq_data_regulator, + regulator_irq->irq); + + if (!virq) + return -EINVAL; + regulator_irq->virq = virq; + + ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, + pf1550_regulator_irq_handler, + IRQF_NO_SUSPEND, + regulator_irq->name, info); + if (ret) { + dev_err(&pdev->dev, + "failed: irq request (IRQ: %d, error :%d)\n", + regulator_irq->irq, ret); + return ret; + } + } + + /* unmask all exception interrupts for regulators */ + regmap_write(info->pf1550->regmap, PF1550_PMIC_REG_SW_INT_MASK0, 0); + regmap_write(info->pf1550->regmap, PF1550_PMIC_REG_SW_INT_MASK1, 0); + regmap_write(info->pf1550->regmap, PF1550_PMIC_REG_LDO_INT_MASK0, 0); + regmap_write(info->pf1550->regmap, PF1550_PMIC_REG_TEMP_INT_MASK0, 0); + + return 0; +} + +static const struct platform_device_id pf1550_regulator_id[] = { + {"pf1550-regulator", PF1550}, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(platform, pf1550_regulator_id); + +static struct platform_driver pf1550_regulator_driver = { + .driver = { + .name = "pf1550-regulator", + }, + .probe = pf1550_regulator_probe, + .id_table = pf1550_regulator_id, +}; + +module_platform_driver(pf1550_regulator_driver); + +MODULE_DESCRIPTION("Freescale PF1550 regulator driver"); +MODULE_AUTHOR("Robin Gong "); +MODULE_LICENSE("GPL v2"); From patchwork Fri May 16 18:58:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Kayode X-Patchwork-Id: 890792 Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BB1C627C862; Fri, 16 May 2025 18:58:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=208.88.110.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421925; cv=none; b=reKrHLBwM3wG2IWhJiYmfNDAxsTBNatnH4kGLwO7MlNSvLXKFN8iRZBlM5iZVbYYUJ9W7BBV7MGDdDUgNhHtf1gHxMX4OSHyOEZgmh0i53Iru2cDp48p58xOUTDPXCe1tE3uiA3oKLg1pl9sIC9KHF4FcX2e7h33czp8LPwfo1c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421925; c=relaxed/simple; bh=ZqnjDkltiuxaJn8UPSQwpYUw5lN6C1vgqVTkncotVVU=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=Q0EyOa84TojoKdhPZx/KGUiwK7xz5sGG0R6C4O/HwqjuQS0aqSGVSiNRV+JF5Ee82MNEA2STFeDCcG9pwe0ChbRa1o3S3GYy8Qd5x+3+OTYbsICZCaVimXLIKwM+1IsrQ3kKgaTNE54MSfz9KnEBjP2flJU9pDI8kWvM0qaJSjw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com; spf=pass smtp.mailfrom=savoirfairelinux.com; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b=O+8vfCe+; arc=none smtp.client-ip=208.88.110.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b="O+8vfCe+" Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 0E9199C8F98; Fri, 16 May 2025 14:58:41 -0400 (EDT) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10032) with ESMTP id vcpDFetGGzSz; Fri, 16 May 2025 14:58:39 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id A42889C906E; Fri, 16 May 2025 14:58:39 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com A42889C906E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1747421919; bh=ELnZI3rb/dEWeIpOY64yxy8830zYxJ3C0P9+ns8lwek=; h=Date:From:To:Message-ID:MIME-Version; b=O+8vfCe+qHTS0foF/rh8Xe6p0qGyVHdVsS7LbFIY7oNUGz9xr2tLgcldEphSun1D3 CFAR196UDmrTtf8+aOOyXWBIkOMLeuwQyR2eoLyvRw/eO9K0LVOs2Z7oQtuJnL7f0o e4x0YpAJj/s1hyZNCQ7bm2vDFVUFVifw5JDzgzTCfJH7MCB47vm1t4zGDNhU+OXOfH gpKY9G6UdewTm7okPxKMoi48P//ufxVmSb0gz/ZqJH4+jxdjGmMOujYoBeb5K8SMIl VRx+7kqyb1YeoaukuWzR/8IvJjH24Q1wqnotHMG8UuUFj3ZFOTvZIoHPuXQ0WFmJSM EfhH0+RoL3rOA== X-Virus-Scanned: amavis at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10026) with ESMTP id lrTPNdN4K9bB; Fri, 16 May 2025 14:58:39 -0400 (EDT) Received: from fedora (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id 51A8B9C8F98; Fri, 16 May 2025 14:58:39 -0400 (EDT) Date: Fri, 16 May 2025 14:58:38 -0400 From: Samuel Kayode To: Lee Jones , Liam Girdwood , Mark Brown , Dmitry Torokhov , Sebastian Reichel , Robin Gong Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-imx@nxp.com, linux-input@vger.kernel.org, Abel Vesa , Abel Vesa , Robin Gong , Enric Balletbo Serra Subject: [PATCH v2 8/9] power: supply: pf1550: add battery charger support Message-ID: <4f51191483189a69072fe08c5609048fa14b39b1.1747409892.git.samuel.kayode@savoirfairelinux.com> References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add support for the battery charger for pf1550 PMIC. Signed-off-by: Samuel Kayode --- drivers/power/supply/Kconfig | 6 + drivers/power/supply/Makefile | 1 + drivers/power/supply/pf1550_charger.c | 656 ++++++++++++++++++++++++++ 3 files changed, 663 insertions(+) create mode 100644 drivers/power/supply/pf1550_charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 79ddb006e2da..5a2b694561b9 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -471,6 +471,12 @@ config CHARGER_88PM860X help Say Y here to enable charger for Marvell 88PM860x chip. +config CHARGER_PF1550 + tristate "Freescale PF1550 battery charger driver" + depends on MFD_PF1550 + help + Say Y to enable support for the Freescale PF1550 battery charger. + config BATTERY_RX51 tristate "Nokia RX-51 (N900) battery driver" depends on TWL4030_MADC diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 4f5f8e3507f8..e7dd9fb41d4e 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_CHARGER_RT9467) += rt9467-charger.o obj-$(CONFIG_CHARGER_RT9471) += rt9471.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o +obj-$(CONFIG_CHARGER_PF1550) += pf1550_charger.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o diff --git a/drivers/power/supply/pf1550_charger.c b/drivers/power/supply/pf1550_charger.c new file mode 100644 index 000000000000..48fd9a07bb47 --- /dev/null +++ b/drivers/power/supply/pf1550_charger.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pf1550_charger.c - regulator driver for the PF1550 + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Robin Gong + */ + +#include +#include +#include +#include +#include +#include + +#define PF1550_CHARGER_NAME "pf1550-charger" +#define PF1550_DEFAULT_CONSTANT_VOLT 4200000 +#define PF1550_DEFAULT_MIN_SYSTEM_VOLT 3500000 +#define PF1550_DEFAULT_THERMAL_TEMP 75 + +static const char *pf1550_charger_model = "PF1550"; +static const char *pf1550_charger_manufacturer = "Freescale"; + +struct pf1550_charger { + struct device *dev; + struct pf1550_dev *pf1550; + struct power_supply *charger; + struct power_supply_desc psy_desc; + int irq; + struct delayed_work irq_work; + struct mutex mutex; + + u32 constant_volt; + u32 min_system_volt; + u32 thermal_regulation_temp; +}; + +static struct pf1550_irq_info pf1550_charger_irqs[] = { + { PF1550_CHARG_IRQ_BAT2SOCI, "BAT2SOC" }, + { PF1550_CHARG_IRQ_BATI, "BAT" }, + { PF1550_CHARG_IRQ_CHGI, "CHG" }, + { PF1550_CHARG_IRQ_VBUSI, "VBUS" }, + { PF1550_CHARG_IRQ_THMI, "THM" }, +}; + +static int pf1550_get_charger_state(struct regmap *regmap, int *val) +{ + int ret; + unsigned int data; + + ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data); + if (ret < 0) + return ret; + + data &= PF1550_CHG_SNS_MASK; + + switch (data) { + case PF1550_CHG_PRECHARGE: + case PF1550_CHG_CONSTANT_CURRENT: + *val = POWER_SUPPLY_STATUS_CHARGING; + break; + case PF1550_CHG_CONSTANT_VOL: + *val = POWER_SUPPLY_STATUS_CHARGING; + break; + case PF1550_CHG_EOC: + *val = POWER_SUPPLY_STATUS_CHARGING; + break; + case PF1550_CHG_DONE: + *val = POWER_SUPPLY_STATUS_FULL; + break; + case PF1550_CHG_TIMER_FAULT: + case PF1550_CHG_SUSPEND: + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case PF1550_CHG_OFF_INV: + case PF1550_CHG_OFF_TEMP: + case PF1550_CHG_LINEAR_ONLY: + *val = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + *val = POWER_SUPPLY_STATUS_UNKNOWN; + } + + return 0; +} + +static int pf1550_get_charge_type(struct regmap *regmap, int *val) +{ + int ret; + unsigned int data; + + ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data); + if (ret < 0) + return ret; + + data &= PF1550_CHG_SNS_MASK; + + switch (data) { + case PF1550_CHG_SNS_MASK: + *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case PF1550_CHG_CONSTANT_CURRENT: + case PF1550_CHG_CONSTANT_VOL: + case PF1550_CHG_EOC: + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case PF1550_CHG_DONE: + case PF1550_CHG_TIMER_FAULT: + case PF1550_CHG_SUSPEND: + case PF1550_CHG_OFF_INV: + case PF1550_CHG_BAT_OVER: + case PF1550_CHG_OFF_TEMP: + case PF1550_CHG_LINEAR_ONLY: + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + + return 0; +} + +/* + * Supported health statuses: + * - POWER_SUPPLY_HEALTH_DEAD + * - POWER_SUPPLY_HEALTH_GOOD + * - POWER_SUPPLY_HEALTH_OVERVOLTAGE + * - POWER_SUPPLY_HEALTH_UNKNOWN + */ +static int pf1550_get_battery_health(struct regmap *regmap, int *val) +{ + int ret; + unsigned int data; + + ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data); + if (ret < 0) + return ret; + + data &= PF1550_BAT_SNS_MASK; + + switch (data) { + case PF1550_BAT_NO_DETECT: + *val = POWER_SUPPLY_HEALTH_DEAD; + break; + case PF1550_BAT_NO_VBUS: + case PF1550_BAT_LOW_THAN_PRECHARG: + case PF1550_BAT_CHARG_FAIL: + case PF1550_BAT_HIGH_THAN_PRECHARG: + *val = POWER_SUPPLY_HEALTH_GOOD; + break; + case PF1550_BAT_OVER_VOL: + *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + default: + *val = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + return 0; +} + +static int pf1550_get_present(struct regmap *regmap, int *val) +{ + unsigned int data; + int ret; + + ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data); + if (ret < 0) + return ret; + + data &= PF1550_BAT_SNS_MASK; + *val = (data == PF1550_BAT_NO_DETECT) ? 0 : 1; + + return 0; +} + +static int pf1550_get_online(struct regmap *regmap, int *val) +{ + unsigned int data; + int ret; + + ret = regmap_read(regmap, PF1550_CHARG_REG_VBUS_SNS, &data); + if (ret < 0) + return ret; + + *val = (data & PF1550_VBUS_VALID) ? 1 : 0; + + return 0; +} + +static void pf1550_chg_bat_isr(struct pf1550_charger *chg) +{ + unsigned int data; + + if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_BATT_SNS, &data)) { + dev_err(chg->dev, "Read BATT_SNS error.\n"); + return; + } + + switch (data & PF1550_BAT_SNS_MASK) { + case PF1550_BAT_NO_VBUS: + dev_dbg(chg->dev, "No valid VBUS input.\n"); + break; + case PF1550_BAT_LOW_THAN_PRECHARG: + dev_dbg(chg->dev, "VBAT < VPRECHG.LB.\n"); + break; + case PF1550_BAT_CHARG_FAIL: + dev_dbg(chg->dev, "Battery charging failed.\n"); + break; + case PF1550_BAT_HIGH_THAN_PRECHARG: + dev_dbg(chg->dev, "VBAT > VPRECHG.LB.\n"); + break; + case PF1550_BAT_OVER_VOL: + dev_dbg(chg->dev, "VBAT > VBATOV.\n"); + break; + case PF1550_BAT_NO_DETECT: + dev_dbg(chg->dev, "Battery not detected.\n"); + break; + default: + dev_err(chg->dev, "Unknown value read:%x\n", + data & PF1550_CHG_SNS_MASK); + } +} + +static void pf1550_chg_chg_isr(struct pf1550_charger *chg) +{ + unsigned int data; + + if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_SNS, &data)) { + dev_err(chg->dev, "Read CHG_SNS error.\n"); + return; + } + + switch (data & PF1550_CHG_SNS_MASK) { + case PF1550_CHG_PRECHARGE: + dev_dbg(chg->dev, "In pre-charger mode.\n"); + break; + case PF1550_CHG_CONSTANT_CURRENT: + dev_dbg(chg->dev, "In fast-charge constant current mode.\n"); + break; + case PF1550_CHG_CONSTANT_VOL: + dev_dbg(chg->dev, "In fast-charge constant voltage mode.\n"); + break; + case PF1550_CHG_EOC: + dev_dbg(chg->dev, "In EOC mode.\n"); + break; + case PF1550_CHG_DONE: + dev_dbg(chg->dev, "In DONE mode.\n"); + break; + case PF1550_CHG_TIMER_FAULT: + dev_info(chg->dev, "In timer fault mode.\n"); + break; + case PF1550_CHG_SUSPEND: + dev_info(chg->dev, "In thermistor suspend mode.\n"); + break; + case PF1550_CHG_OFF_INV: + dev_info(chg->dev, "Input invalid, charger off.\n"); + break; + case PF1550_CHG_BAT_OVER: + dev_info(chg->dev, "Battery over-voltage.\n"); + break; + case PF1550_CHG_OFF_TEMP: + dev_info(chg->dev, "Temp high, charger off.\n"); + break; + case PF1550_CHG_LINEAR_ONLY: + dev_dbg(chg->dev, "In Linear mode, not charging.\n"); + break; + default: + dev_err(chg->dev, "Unknown value read:%x\n", + data & PF1550_CHG_SNS_MASK); + } +} + +static void pf1550_chg_vbus_isr(struct pf1550_charger *chg) +{ + enum power_supply_type old_type; + unsigned int data; + + if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_VBUS_SNS, &data)) { + dev_err(chg->dev, "Read VBUS_SNS error.\n"); + return; + } + + old_type = chg->psy_desc.type; + + if (data & PF1550_VBUS_UVLO) { + chg->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; + dev_dbg(chg->dev, "VBUS deattached.\n"); + } + if (data & PF1550_VBUS_IN2SYS) + dev_dbg(chg->dev, "VBUS_IN2SYS_SNS.\n"); + if (data & PF1550_VBUS_OVLO) + dev_dbg(chg->dev, "VBUS_OVLO_SNS.\n"); + if (data & PF1550_VBUS_VALID) { + chg->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; + dev_dbg(chg->dev, "VBUS attached.\n"); + } + + if (old_type != chg->psy_desc.type) + power_supply_changed(chg->charger); +} + +static irqreturn_t pf1550_charger_irq_handler(int irq, void *data) +{ + struct pf1550_charger *chg = data; + + chg->irq = irq; + schedule_delayed_work(&chg->irq_work, msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +static void pf1550_charger_irq_work(struct work_struct *work) +{ + struct pf1550_charger *chg = container_of(to_delayed_work(work), + struct pf1550_charger, + irq_work); + int i, irq_type = -1; + unsigned int status; + + if (!chg->charger) + return; + + mutex_lock(&chg->mutex); + + for (i = 0; i < ARRAY_SIZE(pf1550_charger_irqs); i++) + if (chg->irq == pf1550_charger_irqs[i].virq) + irq_type = pf1550_charger_irqs[i].irq; + + switch (irq_type) { + case PF1550_CHARG_IRQ_BAT2SOCI: + dev_info(chg->dev, "BAT to SYS Overcurrent interrupt.\n"); + break; + case PF1550_CHARG_IRQ_BATI: + pf1550_chg_bat_isr(chg); + break; + case PF1550_CHARG_IRQ_CHGI: + pf1550_chg_chg_isr(chg); + break; + case PF1550_CHARG_IRQ_VBUSI: + pf1550_chg_vbus_isr(chg); + break; + case PF1550_CHARG_IRQ_THMI: + dev_info(chg->dev, "Thermal interrupt.\n"); + break; + default: + dev_err(chg->dev, "unknown interrupt occurred.\n"); + } + + if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT, &status)) + dev_err(chg->dev, "Read CHG_INT error.\n"); + if (regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT, status)) + dev_err(chg->dev, "clear CHG_INT error.\n"); + + mutex_unlock(&chg->mutex); +} + +static enum power_supply_property pf1550_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static int pf1550_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pf1550_charger *chg = power_supply_get_drvdata(psy); + struct regmap *regmap = chg->pf1550->regmap; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = pf1550_get_charger_state(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = pf1550_get_charge_type(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = pf1550_get_battery_health(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = pf1550_get_present(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = pf1550_get_online(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = pf1550_charger_model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = pf1550_charger_manufacturer; + break; + default: + return -EINVAL; + } + + return ret; +} + +static int pf1550_set_constant_volt(struct pf1550_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + if (uvolt >= 3500000 && uvolt <= 4440000) { + data = 8 + (uvolt - 3500000) / 20000; + } else { + dev_err(chg->dev, "Wrong value for constant voltage\n"); + return -EINVAL; + } + + dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt, + data); + + return regmap_update_bits(chg->pf1550->regmap, + PF1550_CHARG_REG_BATT_REG, + PF1550_CHARG_REG_BATT_REG_CHGCV_MASK, data); +} + +static int pf1550_set_min_system_volt(struct pf1550_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + switch (uvolt) { + case 3500000: + data = 0x0; + break; + case 3700000: + data = 0x1; + break; + case 4300000: + data = 0x2; + break; + default: + dev_err(chg->dev, "Wrong value for minimum system voltage\n"); + return -EINVAL; + } + + data <<= PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT; + + dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n", + uvolt, data); + + return regmap_update_bits(chg->pf1550->regmap, + PF1550_CHARG_REG_BATT_REG, + PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK, data); +} + +static int pf1550_set_thermal_regulation_temp(struct pf1550_charger *chg, + unsigned int cels) +{ + unsigned int data; + + switch (cels) { + case 60: + data = 0x0; + break; + case 75: + data = 0x1; + break; + case 90: + data = 0x2; + break; + case 105: + data = 0x3; + break; + default: + dev_err(chg->dev, "Wrong value for thermal temperature\n"); + return -EINVAL; + } + + data <<= PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT; + + dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n", + cels, data); + + return regmap_update_bits(chg->pf1550->regmap, + PF1550_CHARG_REG_THM_REG_CNFG, + PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK, data); +} + +/* + * Sets charger registers to proper and safe default values. + */ +static int pf1550_reg_init(struct pf1550_charger *chg) +{ + int ret; + unsigned int data; + + /* Unmask charger interrupt, mask DPMI and reserved bit */ + ret = regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT_MASK, + PF1550_CHG_INT_MASK); + if (ret) { + dev_err(chg->dev, "Error unmask charger interrupt: %d\n", ret); + return ret; + } + + ret = regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_VBUS_SNS, + &data); + if (ret) { + dev_err(chg->dev, "Read charg vbus_sns error: %d\n", ret); + return ret; + } + + if (data & PF1550_VBUS_VALID) + chg->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; + + ret = pf1550_set_constant_volt(chg, chg->constant_volt); + if (ret) + return ret; + + ret = pf1550_set_min_system_volt(chg, chg->min_system_volt); + if (ret) + return ret; + + ret = pf1550_set_thermal_regulation_temp(chg, + chg->thermal_regulation_temp); + if (ret) + return ret; + + /* Turn on charger */ + ret = regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_OPER, + PF1550_CHG_TURNON); + if (ret) { + dev_err(chg->dev, "Error turn on charger: %d\n", ret); + return ret; + } + + return 0; +} + +static int pf1550_dt_init(struct device *dev, struct pf1550_charger *chg) +{ + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "no charger OF node\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "fsl,constant-microvolt", + &chg->constant_volt)) + chg->constant_volt = PF1550_DEFAULT_CONSTANT_VOLT; + + if (of_property_read_u32(np, "fsl,min-system-microvolt", + &chg->min_system_volt)) + chg->min_system_volt = PF1550_DEFAULT_MIN_SYSTEM_VOLT; + + if (of_property_read_u32(np, "fsl,thermal-regulation", + &chg->thermal_regulation_temp)) + chg->thermal_regulation_temp = PF1550_DEFAULT_THERMAL_TEMP; + + return 0; +} + +static int pf1550_charger_probe(struct platform_device *pdev) +{ + struct pf1550_charger *chg; + struct power_supply_config psy_cfg = {}; + struct pf1550_dev *pf1550 = dev_get_drvdata(pdev->dev.parent); + int i, ret; + + chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + chg->dev = &pdev->dev; + chg->pf1550 = pf1550; + + platform_set_drvdata(pdev, chg); + + ret = pf1550_dt_init(&pdev->dev, chg); + if (ret) + return ret; + + mutex_init(&chg->mutex); + + INIT_DELAYED_WORK(&chg->irq_work, pf1550_charger_irq_work); + + for (i = 0; i < ARRAY_SIZE(pf1550_charger_irqs); i++) { + struct pf1550_irq_info *charger_irq = + &pf1550_charger_irqs[i]; + unsigned int virq = 0; + + virq = regmap_irq_get_virq(pf1550->irq_data_charger, + charger_irq->irq); + if (!virq) + return -EINVAL; + + charger_irq->virq = virq; + + ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, + pf1550_charger_irq_handler, + IRQF_NO_SUSPEND, + charger_irq->name, chg); + if (ret) { + dev_err(&pdev->dev, + "failed: irq request (IRQ: %d, error :%d)\n", + charger_irq->irq, ret); + return ret; + } + } + + psy_cfg.drv_data = chg; + + chg->psy_desc.name = PF1550_CHARGER_NAME; + chg->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; + chg->psy_desc.get_property = pf1550_charger_get_property; + chg->psy_desc.properties = pf1550_charger_props; + chg->psy_desc.num_properties = ARRAY_SIZE(pf1550_charger_props); + + chg->charger = devm_power_supply_register(&pdev->dev, &chg->psy_desc, + &psy_cfg); + if (IS_ERR(chg->charger)) { + dev_err(&pdev->dev, "failed: power supply register\n"); + ret = PTR_ERR(chg->charger); + return ret; + } + + ret = pf1550_reg_init(chg); + + return ret; +} + +static void pf1550_charger_remove(struct platform_device *pdev) +{ + struct pf1550_charger *chg = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&chg->irq_work); +} + +static const struct platform_device_id pf1550_charger_id[] = { + { "pf1550-charger", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, pf1550_charger_id); + +static struct platform_driver pf1550_charger_driver = { + .driver = { + .name = "pf1550-charger", + }, + .probe = pf1550_charger_probe, + .remove = pf1550_charger_remove, + .id_table = pf1550_charger_id, +}; +module_platform_driver(pf1550_charger_driver); + +MODULE_AUTHOR("Robin Gong "); +MODULE_DESCRIPTION("PF1550 charger driver"); +MODULE_LICENSE("GPL v2");