diff options
author | Tim Bird <tim.bird@sonymobile.com> | 2015-07-16 19:55:32 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-07-29 16:37:18 -0400 |
commit | 1f26d1c1053ac179b0ee1edea7809cef07fe728e (patch) | |
tree | 4681b536a979598a51d14dd9089091b10f53dead /drivers/misc/qcom-coincell.c | |
parent | ee2b7f60a5c5e391fdde907ac61f135d87e74622 (diff) |
ARM: qcom: Add coincell charger driver
This driver is used to configure the coincell charger found in
Qualcomm PMICs.
The driver allows configuring the current-limiting resistor for
the charger, as well as the voltage to apply to the coincell
(or capacitor) when charging.
Signed-off-by: Tim Bird <tim.bird@sonymobile.com>
Reviewed-by: Andy Gross <agross@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/qcom-coincell.c')
-rw-r--r-- | drivers/misc/qcom-coincell.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c new file mode 100644 index 000000000000..7b4a2da487a5 --- /dev/null +++ b/drivers/misc/qcom-coincell.c | |||
@@ -0,0 +1,152 @@ | |||
1 | /* Copyright (c) 2013, The Linux Foundation. All rights reserved. | ||
2 | * Copyright (c) 2015, Sony Mobile Communications Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/regmap.h> | ||
19 | #include <linux/of_device.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | |||
22 | struct qcom_coincell { | ||
23 | struct device *dev; | ||
24 | struct regmap *regmap; | ||
25 | u32 base_addr; | ||
26 | }; | ||
27 | |||
28 | #define QCOM_COINCELL_REG_RSET 0x44 | ||
29 | #define QCOM_COINCELL_REG_VSET 0x45 | ||
30 | #define QCOM_COINCELL_REG_ENABLE 0x46 | ||
31 | |||
32 | #define QCOM_COINCELL_ENABLE BIT(7) | ||
33 | |||
34 | static const int qcom_rset_map[] = { 2100, 1700, 1200, 800 }; | ||
35 | static const int qcom_vset_map[] = { 2500, 3200, 3100, 3000 }; | ||
36 | /* NOTE: for pm8921 and others, voltage of 2500 is 16 (10000b), not 0 */ | ||
37 | |||
38 | /* if enable==0, rset and vset are ignored */ | ||
39 | static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset, | ||
40 | int vset, bool enable) | ||
41 | { | ||
42 | int i, j, rc; | ||
43 | |||
44 | /* if disabling, just do that and skip other operations */ | ||
45 | if (!enable) | ||
46 | return regmap_write(chgr->regmap, | ||
47 | chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0); | ||
48 | |||
49 | /* find index for current-limiting resistor */ | ||
50 | for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++) | ||
51 | if (rset == qcom_rset_map[i]) | ||
52 | break; | ||
53 | |||
54 | if (i >= ARRAY_SIZE(qcom_rset_map)) { | ||
55 | dev_err(chgr->dev, "invalid rset-ohms value %d\n", rset); | ||
56 | return -EINVAL; | ||
57 | } | ||
58 | |||
59 | /* find index for charge voltage */ | ||
60 | for (j = 0; j < ARRAY_SIZE(qcom_vset_map); j++) | ||
61 | if (vset == qcom_vset_map[j]) | ||
62 | break; | ||
63 | |||
64 | if (j >= ARRAY_SIZE(qcom_vset_map)) { | ||
65 | dev_err(chgr->dev, "invalid vset-millivolts value %d\n", vset); | ||
66 | return -EINVAL; | ||
67 | } | ||
68 | |||
69 | rc = regmap_write(chgr->regmap, | ||
70 | chgr->base_addr + QCOM_COINCELL_REG_RSET, i); | ||
71 | if (rc) { | ||
72 | /* | ||
73 | * This is mainly to flag a bad base_addr (reg) from dts. | ||
74 | * Other failures writing to the registers should be | ||
75 | * extremely rare, or indicative of problems that | ||
76 | * should be reported elsewhere (eg. spmi failure). | ||
77 | */ | ||
78 | dev_err(chgr->dev, "could not write to RSET register\n"); | ||
79 | return rc; | ||
80 | } | ||
81 | |||
82 | rc = regmap_write(chgr->regmap, | ||
83 | chgr->base_addr + QCOM_COINCELL_REG_VSET, j); | ||
84 | if (rc) | ||
85 | return rc; | ||
86 | |||
87 | /* set 'enable' register */ | ||
88 | return regmap_write(chgr->regmap, | ||
89 | chgr->base_addr + QCOM_COINCELL_REG_ENABLE, | ||
90 | QCOM_COINCELL_ENABLE); | ||
91 | } | ||
92 | |||
93 | static int qcom_coincell_probe(struct platform_device *pdev) | ||
94 | { | ||
95 | struct device_node *node = pdev->dev.of_node; | ||
96 | struct qcom_coincell chgr; | ||
97 | u32 rset, vset; | ||
98 | bool enable; | ||
99 | int rc; | ||
100 | |||
101 | chgr.dev = &pdev->dev; | ||
102 | |||
103 | chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL); | ||
104 | if (!chgr.regmap) { | ||
105 | dev_err(chgr.dev, "Unable to get regmap\n"); | ||
106 | return -EINVAL; | ||
107 | } | ||
108 | |||
109 | rc = of_property_read_u32(node, "reg", &chgr.base_addr); | ||
110 | if (rc) | ||
111 | return rc; | ||
112 | |||
113 | enable = !of_property_read_bool(node, "qcom,charger-disable"); | ||
114 | |||
115 | if (enable) { | ||
116 | rc = of_property_read_u32(node, "qcom,rset-ohms", &rset); | ||
117 | if (rc) { | ||
118 | dev_err(chgr.dev, | ||
119 | "can't find 'qcom,rset-ohms' in DT block"); | ||
120 | return rc; | ||
121 | } | ||
122 | |||
123 | rc = of_property_read_u32(node, "qcom,vset-millivolts", &vset); | ||
124 | if (rc) { | ||
125 | dev_err(chgr.dev, | ||
126 | "can't find 'qcom,vset-millivolts' in DT block"); | ||
127 | return rc; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | return qcom_coincell_chgr_config(&chgr, rset, vset, enable); | ||
132 | } | ||
133 | |||
134 | static const struct of_device_id qcom_coincell_match_table[] = { | ||
135 | { .compatible = "qcom,pm8941-coincell", }, | ||
136 | {} | ||
137 | }; | ||
138 | |||
139 | MODULE_DEVICE_TABLE(of, qcom_coincell_match_table); | ||
140 | |||
141 | static struct platform_driver qcom_coincell_driver = { | ||
142 | .driver = { | ||
143 | .name = "qcom-spmi-coincell", | ||
144 | .of_match_table = qcom_coincell_match_table, | ||
145 | }, | ||
146 | .probe = qcom_coincell_probe, | ||
147 | }; | ||
148 | |||
149 | module_platform_driver(qcom_coincell_driver); | ||
150 | |||
151 | MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver"); | ||
152 | MODULE_LICENSE("GPL v2"); | ||