root/drivers/misc/qcom-coincell.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. qcom_coincell_chgr_config
  2. qcom_coincell_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
   3  * Copyright (c) 2015, Sony Mobile Communications Inc.
   4  */
   5 
   6 #include <linux/kernel.h>
   7 #include <linux/module.h>
   8 #include <linux/slab.h>
   9 #include <linux/of.h>
  10 #include <linux/regmap.h>
  11 #include <linux/of_device.h>
  12 #include <linux/platform_device.h>
  13 
  14 struct qcom_coincell {
  15         struct device   *dev;
  16         struct regmap   *regmap;
  17         u32             base_addr;
  18 };
  19 
  20 #define QCOM_COINCELL_REG_RSET          0x44
  21 #define QCOM_COINCELL_REG_VSET          0x45
  22 #define QCOM_COINCELL_REG_ENABLE        0x46
  23 
  24 #define QCOM_COINCELL_ENABLE            BIT(7)
  25 
  26 static const int qcom_rset_map[] = { 2100, 1700, 1200, 800 };
  27 static const int qcom_vset_map[] = { 2500, 3200, 3100, 3000 };
  28 /* NOTE: for pm8921 and others, voltage of 2500 is 16 (10000b), not 0 */
  29 
  30 /* if enable==0, rset and vset are ignored */
  31 static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset,
  32                                      int vset, bool enable)
  33 {
  34         int i, j, rc;
  35 
  36         /* if disabling, just do that and skip other operations */
  37         if (!enable)
  38                 return regmap_write(chgr->regmap,
  39                           chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0);
  40 
  41         /* find index for current-limiting resistor */
  42         for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++)
  43                 if (rset == qcom_rset_map[i])
  44                         break;
  45 
  46         if (i >= ARRAY_SIZE(qcom_rset_map)) {
  47                 dev_err(chgr->dev, "invalid rset-ohms value %d\n", rset);
  48                 return -EINVAL;
  49         }
  50 
  51         /* find index for charge voltage */
  52         for (j = 0; j < ARRAY_SIZE(qcom_vset_map); j++)
  53                 if (vset == qcom_vset_map[j])
  54                         break;
  55 
  56         if (j >= ARRAY_SIZE(qcom_vset_map)) {
  57                 dev_err(chgr->dev, "invalid vset-millivolts value %d\n", vset);
  58                 return -EINVAL;
  59         }
  60 
  61         rc = regmap_write(chgr->regmap,
  62                           chgr->base_addr + QCOM_COINCELL_REG_RSET, i);
  63         if (rc) {
  64                 /*
  65                  * This is mainly to flag a bad base_addr (reg) from dts.
  66                  * Other failures writing to the registers should be
  67                  * extremely rare, or indicative of problems that
  68                  * should be reported elsewhere (eg. spmi failure).
  69                  */
  70                 dev_err(chgr->dev, "could not write to RSET register\n");
  71                 return rc;
  72         }
  73 
  74         rc = regmap_write(chgr->regmap,
  75                 chgr->base_addr + QCOM_COINCELL_REG_VSET, j);
  76         if (rc)
  77                 return rc;
  78 
  79         /* set 'enable' register */
  80         return regmap_write(chgr->regmap,
  81                             chgr->base_addr + QCOM_COINCELL_REG_ENABLE,
  82                             QCOM_COINCELL_ENABLE);
  83 }
  84 
  85 static int qcom_coincell_probe(struct platform_device *pdev)
  86 {
  87         struct device_node *node = pdev->dev.of_node;
  88         struct qcom_coincell chgr;
  89         u32 rset = 0;
  90         u32 vset = 0;
  91         bool enable;
  92         int rc;
  93 
  94         chgr.dev = &pdev->dev;
  95 
  96         chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL);
  97         if (!chgr.regmap) {
  98                 dev_err(chgr.dev, "Unable to get regmap\n");
  99                 return -EINVAL;
 100         }
 101 
 102         rc = of_property_read_u32(node, "reg", &chgr.base_addr);
 103         if (rc)
 104                 return rc;
 105 
 106         enable = !of_property_read_bool(node, "qcom,charger-disable");
 107 
 108         if (enable) {
 109                 rc = of_property_read_u32(node, "qcom,rset-ohms", &rset);
 110                 if (rc) {
 111                         dev_err(chgr.dev,
 112                                 "can't find 'qcom,rset-ohms' in DT block");
 113                         return rc;
 114                 }
 115 
 116                 rc = of_property_read_u32(node, "qcom,vset-millivolts", &vset);
 117                 if (rc) {
 118                         dev_err(chgr.dev,
 119                             "can't find 'qcom,vset-millivolts' in DT block");
 120                         return rc;
 121                 }
 122         }
 123 
 124         return qcom_coincell_chgr_config(&chgr, rset, vset, enable);
 125 }
 126 
 127 static const struct of_device_id qcom_coincell_match_table[] = {
 128         { .compatible = "qcom,pm8941-coincell", },
 129         {}
 130 };
 131 
 132 MODULE_DEVICE_TABLE(of, qcom_coincell_match_table);
 133 
 134 static struct platform_driver qcom_coincell_driver = {
 135         .driver = {
 136                 .name           = "qcom-spmi-coincell",
 137                 .of_match_table = qcom_coincell_match_table,
 138         },
 139         .probe          = qcom_coincell_probe,
 140 };
 141 
 142 module_platform_driver(qcom_coincell_driver);
 143 
 144 MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver");
 145 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */