From patchwork Fri May 16 18:47:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Kayode X-Patchwork-Id: 890738 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 09E30221FC9; Fri, 16 May 2025 18:47:45 +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=1747421268; cv=none; b=HaiAWWwuFE9hK0scqqwR/3YnJg56mg1/yCzooVD/OXKPM6ck/b8XR4gHfjS6uAkoRKUwVvTjHu2yUR6pqNqof5V1XKMd1XGOunZCkNGJlWj67vt8MB5KMmG0MQTh9J5ZF09uzO2r7jXxbA/CjQrTtYwQYe1AlhODNLvvYGUv8CM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421268; c=relaxed/simple; bh=AxPJvm0FQ0jefqPO0WREeRAuJuPS67Jv84UZGd6Rk5w=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=NUCCXE/tZdSLuXDb93SZIKpK62IYKcMjE6ZbHk4X/f411oIB/HMqlH/35gtWpQiIC1hmI7/s1eem3a619eXpidc6NyGtHe1rnF9o3ETO6x7kawkt9V26dzvk4mXWGM2/Nx/EI/Xu/TL3QTg4E4pcgBzncv9LagnLCTiv826HdEU= 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=rJ0hJckl; 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="rJ0hJckl" Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 954ED9C8DB5; Fri, 16 May 2025 14:47:44 -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 bRob_rRLX19x; Fri, 16 May 2025 14:47:44 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 131A49C8DBE; Fri, 16 May 2025 14:47:44 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com 131A49C8DBE DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1747421264; bh=u+SI7YO3huugnHnGaiAAHUUQSVwwcqVrH+3aKJw6igA=; h=Date:From:To:Message-ID:MIME-Version; b=rJ0hJcklXEaHKw/3g7ftTLPZgkxSzlAXJ874cgYdznVzB7UwRKgZgfCvw0JfFVTF2 14mFRfPwbD1Xyq4tdxDRw7H2M99Hs/FiOHWsVfwSqruTQt8efdtlEs6Hip4UsVT3j2 nTafqYS8/TW9SA+3S90zKf598aY/5DBQ+MI7h1BykT4Kv1VlloFujBvQbBmE2A0sqm Vk3yplJFbTxuzfwS10zd/837mY+QKruY0AVBySOWASLubtBXRZnfiNM0NrhhZz6BHB 0NY9T+yAMwjJB4w+62OP8vQ2bpqDBZSFO5qHGQ+xkL1kMXjuoS7I5L/dPmwMc0yLZe bh9n6nZuczaHg== 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 ZxFcXrFd6owZ; Fri, 16 May 2025 14:47:43 -0400 (EDT) Received: from fedora (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id CED529C8DB5; Fri, 16 May 2025 14:47:43 -0400 (EDT) Date: Fri, 16 May 2025 14:47:42 -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 1/9] dt-bindings: power: supply: add pf1550 Message-ID: <21b1bc2ba4d266b0d69e447e60927d5985c8ae74.1747409892.git.samuel.kayode@savoirfairelinux.com> References: Precedence: bulk X-Mailing-List: linux-input@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 the battery charger module of pf1550. Signed-off-by: Samuel Kayode --- .../bindings/power/supply/pf1550_charger.yaml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/pf1550_charger.yaml diff --git a/Documentation/devicetree/bindings/power/supply/pf1550_charger.yaml b/Documentation/devicetree/bindings/power/supply/pf1550_charger.yaml new file mode 100644 index 000000000000..10fc0b35917c --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/pf1550_charger.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/pf1550_charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Battery charger driver 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 charger is represented as a sub-node of the PMIC node on the device tree. + +properties: + compatible: + const: fsl,pf1550-charger + + fsl,constant-microvolt: + description: + Constant charge voltage (in microvolts). + minimum: 3500000 + maximum: 4400000 + + fsl,min-system-microvolt: + description: + Minimum charge voltage (in microvolts). + enum: [ 3500000, 3700000, 4300000 ] + + fsl,thermal-regulation: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Temperature (in degrees Celsius) threshold past which the charging + current is reduced. + enum: [ 60, 75, 90, 105 ] + +required: + - compatible +additionalProperties: false + +... From patchwork Fri May 16 18:52:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Kayode X-Patchwork-Id: 890737 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 5AB1D2E628; Fri, 16 May 2025 18:52:04 +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=1747421526; cv=none; b=trCZyu+hW3BfbfjLgKl33KNJYmxhfA7Lwx/jv1665tTEJ0fomTt62tYmcZLyRy9yBQJqGWtYpMi99NgV1ioWnfNe3Km5L/+ksOG2HHozDNY6qHWH8HKzES0c7vqO8jraMRIR1BZ/AQGPIdL7QdmnICnTRW1kxGX8ZqOr0VY0VFs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747421526; c=relaxed/simple; bh=3E09u6U7E2io67Ymr2RG+oHARDJaNwa8aO5y50uDuqE=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=c7QuGW7gy3rrrLCrFP6hHId5G60XZvs98LVXEGiBqJs2tHGtjQvydfXKJlf4cZTa6VU0DQR4f6bmY18Mk+V+z9CTIjKEydDis5VLsbp4pVKeQD12x8c00YG3RCj2xxPUpUecG+ev+kOrzaIBuPyhob3M9E5SrTyhNgFS4J7hykQ= 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=BLyzrMt1; 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="BLyzrMt1" Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 73B0D9C9060; Fri, 16 May 2025 14:52:03 -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 F1PiPYXGVcmr; Fri, 16 May 2025 14:52:03 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id EB6AB9C905E; Fri, 16 May 2025 14:52:02 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com EB6AB9C905E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1747421523; bh=EDrtCERjeggy1F5Mvt3jo+Ghmjeat8adSrQGsX/ghDo=; h=Date:From:To:Message-ID:MIME-Version; b=BLyzrMt14szf/1NYzft8El3sE1JTMfudKedONm+J5C6lMfwzrrqGzgC5E035sg2Sh vZ25JRhp1dh4EWq3gY6JvPHlFGMyY63hmkoeyqssr1alz9BlVHGWtACuBBQrHqvmLL dUEk7RW2c820n4bkxydf9LmDW53bIRQcS7aC6T/8lQvW/j9G6OqfQTnY9NKDlHrVxW lW7Mb/WA36V0s8IT86KXiFAUkv52k4+XlYbDNW1ObKU5y8b7EbtHb8XYqQgMfGhmG8 v3e/KatWt5YJ2VBcS6eBaXOdizar472rcksN37vOUDa0Cr9yaDl+ECuqpg+5oS39G/ koM7rwBBJpPRw== 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 dbK4OqA5uaHS; Fri, 16 May 2025 14:52:02 -0400 (EDT) Received: from fedora (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id B69149C905D; Fri, 16 May 2025 14:52:02 -0400 (EDT) Date: Fri, 16 May 2025 14:52:01 -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 3/9] dt-bindings: input: add pf1550 Message-ID: References: Precedence: bulk X-Mailing-List: linux-input@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 the onkey module of pf1550. Signed-off-by: Samuel Kayode --- .../bindings/input/pf1550_onkey.yaml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/pf1550_onkey.yaml diff --git a/Documentation/devicetree/bindings/input/pf1550_onkey.yaml b/Documentation/devicetree/bindings/input/pf1550_onkey.yaml new file mode 100644 index 000000000000..7079f5284b73 --- /dev/null +++ b/Documentation/devicetree/bindings/input/pf1550_onkey.yaml @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/pf1550_onkey.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Onkey 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. + +allOf: + - $ref: input.yaml# + +properties: + compatible: + const: fsl,pf1550-onkey + + linux,keycodes: + description: Keycode to emit + default: 116 # KEY_POWER + +required: + - compatible +additionalProperties: false + +... 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: 890736 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-input@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: 890735 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-input@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");