xref: /openbmc/linux/drivers/misc/qcom-coincell.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1*97fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21f26d1c1STim Bird /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
31f26d1c1STim Bird  * Copyright (c) 2015, Sony Mobile Communications Inc.
41f26d1c1STim Bird  */
51f26d1c1STim Bird 
61f26d1c1STim Bird #include <linux/kernel.h>
71f26d1c1STim Bird #include <linux/module.h>
81f26d1c1STim Bird #include <linux/slab.h>
91f26d1c1STim Bird #include <linux/of.h>
101f26d1c1STim Bird #include <linux/regmap.h>
111f26d1c1STim Bird #include <linux/platform_device.h>
121f26d1c1STim Bird 
131f26d1c1STim Bird struct qcom_coincell {
141f26d1c1STim Bird 	struct device	*dev;
151f26d1c1STim Bird 	struct regmap	*regmap;
161f26d1c1STim Bird 	u32		base_addr;
171f26d1c1STim Bird };
181f26d1c1STim Bird 
191f26d1c1STim Bird #define QCOM_COINCELL_REG_RSET		0x44
201f26d1c1STim Bird #define QCOM_COINCELL_REG_VSET		0x45
211f26d1c1STim Bird #define QCOM_COINCELL_REG_ENABLE	0x46
221f26d1c1STim Bird 
231f26d1c1STim Bird #define QCOM_COINCELL_ENABLE		BIT(7)
241f26d1c1STim Bird 
251f26d1c1STim Bird static const int qcom_rset_map[] = { 2100, 1700, 1200, 800 };
261f26d1c1STim Bird static const int qcom_vset_map[] = { 2500, 3200, 3100, 3000 };
271f26d1c1STim Bird /* NOTE: for pm8921 and others, voltage of 2500 is 16 (10000b), not 0 */
281f26d1c1STim Bird 
291f26d1c1STim Bird /* if enable==0, rset and vset are ignored */
qcom_coincell_chgr_config(struct qcom_coincell * chgr,int rset,int vset,bool enable)301f26d1c1STim Bird static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset,
311f26d1c1STim Bird 				     int vset, bool enable)
321f26d1c1STim Bird {
331f26d1c1STim Bird 	int i, j, rc;
341f26d1c1STim Bird 
351f26d1c1STim Bird 	/* if disabling, just do that and skip other operations */
361f26d1c1STim Bird 	if (!enable)
371f26d1c1STim Bird 		return regmap_write(chgr->regmap,
381f26d1c1STim Bird 			  chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0);
391f26d1c1STim Bird 
401f26d1c1STim Bird 	/* find index for current-limiting resistor */
411f26d1c1STim Bird 	for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++)
421f26d1c1STim Bird 		if (rset == qcom_rset_map[i])
431f26d1c1STim Bird 			break;
441f26d1c1STim Bird 
451f26d1c1STim Bird 	if (i >= ARRAY_SIZE(qcom_rset_map)) {
461f26d1c1STim Bird 		dev_err(chgr->dev, "invalid rset-ohms value %d\n", rset);
471f26d1c1STim Bird 		return -EINVAL;
481f26d1c1STim Bird 	}
491f26d1c1STim Bird 
501f26d1c1STim Bird 	/* find index for charge voltage */
511f26d1c1STim Bird 	for (j = 0; j < ARRAY_SIZE(qcom_vset_map); j++)
521f26d1c1STim Bird 		if (vset == qcom_vset_map[j])
531f26d1c1STim Bird 			break;
541f26d1c1STim Bird 
551f26d1c1STim Bird 	if (j >= ARRAY_SIZE(qcom_vset_map)) {
561f26d1c1STim Bird 		dev_err(chgr->dev, "invalid vset-millivolts value %d\n", vset);
571f26d1c1STim Bird 		return -EINVAL;
581f26d1c1STim Bird 	}
591f26d1c1STim Bird 
601f26d1c1STim Bird 	rc = regmap_write(chgr->regmap,
611f26d1c1STim Bird 			  chgr->base_addr + QCOM_COINCELL_REG_RSET, i);
621f26d1c1STim Bird 	if (rc) {
631f26d1c1STim Bird 		/*
641f26d1c1STim Bird 		 * This is mainly to flag a bad base_addr (reg) from dts.
651f26d1c1STim Bird 		 * Other failures writing to the registers should be
661f26d1c1STim Bird 		 * extremely rare, or indicative of problems that
671f26d1c1STim Bird 		 * should be reported elsewhere (eg. spmi failure).
681f26d1c1STim Bird 		 */
691f26d1c1STim Bird 		dev_err(chgr->dev, "could not write to RSET register\n");
701f26d1c1STim Bird 		return rc;
711f26d1c1STim Bird 	}
721f26d1c1STim Bird 
731f26d1c1STim Bird 	rc = regmap_write(chgr->regmap,
741f26d1c1STim Bird 		chgr->base_addr + QCOM_COINCELL_REG_VSET, j);
751f26d1c1STim Bird 	if (rc)
761f26d1c1STim Bird 		return rc;
771f26d1c1STim Bird 
781f26d1c1STim Bird 	/* set 'enable' register */
791f26d1c1STim Bird 	return regmap_write(chgr->regmap,
801f26d1c1STim Bird 			    chgr->base_addr + QCOM_COINCELL_REG_ENABLE,
811f26d1c1STim Bird 			    QCOM_COINCELL_ENABLE);
821f26d1c1STim Bird }
831f26d1c1STim Bird 
qcom_coincell_probe(struct platform_device * pdev)841f26d1c1STim Bird static int qcom_coincell_probe(struct platform_device *pdev)
851f26d1c1STim Bird {
861f26d1c1STim Bird 	struct device_node *node = pdev->dev.of_node;
871f26d1c1STim Bird 	struct qcom_coincell chgr;
8866b58edfSDan Carpenter 	u32 rset = 0;
8966b58edfSDan Carpenter 	u32 vset = 0;
901f26d1c1STim Bird 	bool enable;
911f26d1c1STim Bird 	int rc;
921f26d1c1STim Bird 
931f26d1c1STim Bird 	chgr.dev = &pdev->dev;
941f26d1c1STim Bird 
951f26d1c1STim Bird 	chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL);
961f26d1c1STim Bird 	if (!chgr.regmap) {
971f26d1c1STim Bird 		dev_err(chgr.dev, "Unable to get regmap\n");
981f26d1c1STim Bird 		return -EINVAL;
991f26d1c1STim Bird 	}
1001f26d1c1STim Bird 
1011f26d1c1STim Bird 	rc = of_property_read_u32(node, "reg", &chgr.base_addr);
1021f26d1c1STim Bird 	if (rc)
1031f26d1c1STim Bird 		return rc;
1041f26d1c1STim Bird 
1051f26d1c1STim Bird 	enable = !of_property_read_bool(node, "qcom,charger-disable");
1061f26d1c1STim Bird 
1071f26d1c1STim Bird 	if (enable) {
1081f26d1c1STim Bird 		rc = of_property_read_u32(node, "qcom,rset-ohms", &rset);
1091f26d1c1STim Bird 		if (rc) {
1101f26d1c1STim Bird 			dev_err(chgr.dev,
1111f26d1c1STim Bird 				"can't find 'qcom,rset-ohms' in DT block");
1121f26d1c1STim Bird 			return rc;
1131f26d1c1STim Bird 		}
1141f26d1c1STim Bird 
1151f26d1c1STim Bird 		rc = of_property_read_u32(node, "qcom,vset-millivolts", &vset);
1161f26d1c1STim Bird 		if (rc) {
1171f26d1c1STim Bird 			dev_err(chgr.dev,
1181f26d1c1STim Bird 			    "can't find 'qcom,vset-millivolts' in DT block");
1191f26d1c1STim Bird 			return rc;
1201f26d1c1STim Bird 		}
1211f26d1c1STim Bird 	}
1221f26d1c1STim Bird 
1231f26d1c1STim Bird 	return qcom_coincell_chgr_config(&chgr, rset, vset, enable);
1241f26d1c1STim Bird }
1251f26d1c1STim Bird 
1261f26d1c1STim Bird static const struct of_device_id qcom_coincell_match_table[] = {
1271f26d1c1STim Bird 	{ .compatible = "qcom,pm8941-coincell", },
1281f26d1c1STim Bird 	{}
1291f26d1c1STim Bird };
1301f26d1c1STim Bird 
1311f26d1c1STim Bird MODULE_DEVICE_TABLE(of, qcom_coincell_match_table);
1321f26d1c1STim Bird 
1331f26d1c1STim Bird static struct platform_driver qcom_coincell_driver = {
1341f26d1c1STim Bird 	.driver	= {
1351f26d1c1STim Bird 		.name		= "qcom-spmi-coincell",
1361f26d1c1STim Bird 		.of_match_table	= qcom_coincell_match_table,
1371f26d1c1STim Bird 	},
1381f26d1c1STim Bird 	.probe		= qcom_coincell_probe,
1391f26d1c1STim Bird };
1401f26d1c1STim Bird 
1411f26d1c1STim Bird module_platform_driver(qcom_coincell_driver);
1421f26d1c1STim Bird 
1431f26d1c1STim Bird MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver");
1441f26d1c1STim Bird MODULE_LICENSE("GPL v2");
145