0001
0002
0003
0004
0005
0006 #include <linux/kernel.h>
0007 #include <linux/module.h>
0008 #include <linux/slab.h>
0009 #include <linux/of.h>
0010 #include <linux/regmap.h>
0011 #include <linux/of_device.h>
0012 #include <linux/platform_device.h>
0013
0014 struct qcom_coincell {
0015 struct device *dev;
0016 struct regmap *regmap;
0017 u32 base_addr;
0018 };
0019
0020 #define QCOM_COINCELL_REG_RSET 0x44
0021 #define QCOM_COINCELL_REG_VSET 0x45
0022 #define QCOM_COINCELL_REG_ENABLE 0x46
0023
0024 #define QCOM_COINCELL_ENABLE BIT(7)
0025
0026 static const int qcom_rset_map[] = { 2100, 1700, 1200, 800 };
0027 static const int qcom_vset_map[] = { 2500, 3200, 3100, 3000 };
0028
0029
0030
0031 static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset,
0032 int vset, bool enable)
0033 {
0034 int i, j, rc;
0035
0036
0037 if (!enable)
0038 return regmap_write(chgr->regmap,
0039 chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0);
0040
0041
0042 for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++)
0043 if (rset == qcom_rset_map[i])
0044 break;
0045
0046 if (i >= ARRAY_SIZE(qcom_rset_map)) {
0047 dev_err(chgr->dev, "invalid rset-ohms value %d\n", rset);
0048 return -EINVAL;
0049 }
0050
0051
0052 for (j = 0; j < ARRAY_SIZE(qcom_vset_map); j++)
0053 if (vset == qcom_vset_map[j])
0054 break;
0055
0056 if (j >= ARRAY_SIZE(qcom_vset_map)) {
0057 dev_err(chgr->dev, "invalid vset-millivolts value %d\n", vset);
0058 return -EINVAL;
0059 }
0060
0061 rc = regmap_write(chgr->regmap,
0062 chgr->base_addr + QCOM_COINCELL_REG_RSET, i);
0063 if (rc) {
0064
0065
0066
0067
0068
0069
0070 dev_err(chgr->dev, "could not write to RSET register\n");
0071 return rc;
0072 }
0073
0074 rc = regmap_write(chgr->regmap,
0075 chgr->base_addr + QCOM_COINCELL_REG_VSET, j);
0076 if (rc)
0077 return rc;
0078
0079
0080 return regmap_write(chgr->regmap,
0081 chgr->base_addr + QCOM_COINCELL_REG_ENABLE,
0082 QCOM_COINCELL_ENABLE);
0083 }
0084
0085 static int qcom_coincell_probe(struct platform_device *pdev)
0086 {
0087 struct device_node *node = pdev->dev.of_node;
0088 struct qcom_coincell chgr;
0089 u32 rset = 0;
0090 u32 vset = 0;
0091 bool enable;
0092 int rc;
0093
0094 chgr.dev = &pdev->dev;
0095
0096 chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL);
0097 if (!chgr.regmap) {
0098 dev_err(chgr.dev, "Unable to get regmap\n");
0099 return -EINVAL;
0100 }
0101
0102 rc = of_property_read_u32(node, "reg", &chgr.base_addr);
0103 if (rc)
0104 return rc;
0105
0106 enable = !of_property_read_bool(node, "qcom,charger-disable");
0107
0108 if (enable) {
0109 rc = of_property_read_u32(node, "qcom,rset-ohms", &rset);
0110 if (rc) {
0111 dev_err(chgr.dev,
0112 "can't find 'qcom,rset-ohms' in DT block");
0113 return rc;
0114 }
0115
0116 rc = of_property_read_u32(node, "qcom,vset-millivolts", &vset);
0117 if (rc) {
0118 dev_err(chgr.dev,
0119 "can't find 'qcom,vset-millivolts' in DT block");
0120 return rc;
0121 }
0122 }
0123
0124 return qcom_coincell_chgr_config(&chgr, rset, vset, enable);
0125 }
0126
0127 static const struct of_device_id qcom_coincell_match_table[] = {
0128 { .compatible = "qcom,pm8941-coincell", },
0129 {}
0130 };
0131
0132 MODULE_DEVICE_TABLE(of, qcom_coincell_match_table);
0133
0134 static struct platform_driver qcom_coincell_driver = {
0135 .driver = {
0136 .name = "qcom-spmi-coincell",
0137 .of_match_table = qcom_coincell_match_table,
0138 },
0139 .probe = qcom_coincell_probe,
0140 };
0141
0142 module_platform_driver(qcom_coincell_driver);
0143
0144 MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver");
0145 MODULE_LICENSE("GPL v2");