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