aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@freescale.com>2013-11-14 00:52:14 -0500
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 09:47:13 -0400
commitb0984c8d7971a1a9ca3b468e1cc7ae2613fd350a (patch)
tree94ade60fe7af18319a96f9ec67b4dbfb8a55acee /drivers/power
parent8d38d55ca79892f987167bc9911611230bf12a45 (diff)
ENGR00287992-1 power: imx6: add imx6 USB charger detection
Add imx6 USB charger detection, the vbus supplier will create and remove struct usb_charger, and notify vbus connect and disconnect event. The detail USB charger detection flow is at: "i.MX6 RM, Chapter Universal Serial Bus 2.0 Integrated PHY (USB-PHY), Charger detection, Charger detection software flow". Since imx6 only has charger detection function, and no charging current function is existed. It the user wants the detection abilities from SoC, it can use this detection method (add imx6-usb-charger-detection at dts). If the charger IC already has USB charger detection function, and the user wants to use the detection method from charger IC, please do not add imx6-usb-charger-detection property at dts. Signed-off-by: Peter Chen <peter.chen@freescale.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/Kconfig6
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/imx6_usb_charger.c294
3 files changed, 301 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 13be9dd95388..7c6d4d9fc8a3 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -370,6 +370,12 @@ config BATTERY_GOLDFISH
370 Say Y to enable support for the battery and AC power in the 370 Say Y to enable support for the battery and AC power in the
371 Goldfish emulator. 371 Goldfish emulator.
372 372
373config IMX6_USB_CHARGER
374 tristate "Freescale imx6 USB Charger"
375 depends on USB_CHIPIDEA && USB_GADGET
376 help
377 Say Y to enable Freescale imx6 USB Charger Detect.
378
373source "drivers/power/reset/Kconfig" 379source "drivers/power/reset/Kconfig"
374 380
375endif # POWER_SUPPLY 381endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index a993aaff1715..16746a50fe54 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_POWER_AVS) += avs/
55obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o 55obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
56obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o 56obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
57obj-$(CONFIG_POWER_RESET) += reset/ 57obj-$(CONFIG_POWER_RESET) += reset/
58obj-$(CONFIG_IMX6_USB_CHARGER) += imx6_usb_charger.o
diff --git a/drivers/power/imx6_usb_charger.c b/drivers/power/imx6_usb_charger.c
new file mode 100644
index 000000000000..d94a30226664
--- /dev/null
+++ b/drivers/power/imx6_usb_charger.c
@@ -0,0 +1,294 @@
1/*
2 * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved.
3 *
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
7 *
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
10 */
11
12#include <linux/delay.h>
13#include <linux/device.h>
14#include <linux/power/imx6_usb_charger.h>
15#include <linux/regmap.h>
16
17#define HW_ANADIG_REG_3P0_SET (0x00000124)
18#define HW_ANADIG_REG_3P0_CLR (0x00000128)
19#define BM_ANADIG_REG_3P0_ENABLE_ILIMIT 0x00000004
20#define BM_ANADIG_REG_3P0_ENABLE_LINREG 0x00000001
21
22#define HW_ANADIG_USB1_CHRG_DETECT_SET (0x000001b4)
23#define HW_ANADIG_USB1_CHRG_DETECT_CLR (0x000001b8)
24
25#define BM_ANADIG_USB1_CHRG_DETECT_EN_B 0x00100000
26#define BM_ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B 0x00080000
27#define BM_ANADIG_USB1_CHRG_DETECT_CHK_CONTACT 0x00040000
28
29#define HW_ANADIG_USB1_VBUS_DET_STAT (0x000001c0)
30
31#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID 0x00000008
32
33#define HW_ANADIG_USB1_CHRG_DET_STAT (0x000001d0)
34
35#define BM_ANADIG_USB1_CHRG_DET_STAT_DM_STATE 0x00000004
36#define BM_ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED 0x00000002
37#define BM_ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT 0x00000001
38
39static char *imx6_usb_charger_supplied_to[] = {
40 "imx6_usb_charger",
41};
42
43static enum power_supply_property imx6_usb_charger_power_props[] = {
44 POWER_SUPPLY_PROP_PRESENT, /* Charger detected */
45 POWER_SUPPLY_PROP_ONLINE, /* VBUS online */
46 POWER_SUPPLY_PROP_CURRENT_MAX, /* Maximum current in mA */
47};
48
49static int imx6_usb_charger_get_property(struct power_supply *psy,
50 enum power_supply_property psp,
51 union power_supply_propval *val)
52{
53 struct usb_charger *charger =
54 container_of(psy, struct usb_charger, psy);
55
56 switch (psp) {
57 case POWER_SUPPLY_PROP_PRESENT:
58 val->intval = charger->present;
59 break;
60 case POWER_SUPPLY_PROP_ONLINE:
61 val->intval = charger->online;
62 break;
63 case POWER_SUPPLY_PROP_CURRENT_MAX:
64 val->intval = charger->max_current;
65 break;
66 default:
67 return -EINVAL;
68 }
69 return 0;
70}
71
72static void disable_charger_detector(struct regmap *regmap)
73{
74 regmap_write(regmap, HW_ANADIG_USB1_CHRG_DETECT_SET,
75 BM_ANADIG_USB1_CHRG_DETECT_EN_B |
76 BM_ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
77}
78
79static void disable_current_limiter(struct regmap *regmap)
80{
81 /* Disable the vdd3p0 current limiter */
82 regmap_write(regmap, HW_ANADIG_REG_3P0_CLR,
83 BM_ANADIG_REG_3P0_ENABLE_ILIMIT);
84}
85
86/* Return value if the charger is present */
87static int imx6_usb_charger_detect(struct usb_charger *charger)
88{
89 struct regmap *regmap = charger->anatop;
90 u32 val;
91 int i, data_pin_contact_count = 0;
92
93 /* Enable the vdd3p0 curret limiter */
94 regmap_write(regmap, HW_ANADIG_REG_3P0_SET,
95 BM_ANADIG_REG_3P0_ENABLE_LINREG |
96 BM_ANADIG_REG_3P0_ENABLE_ILIMIT);
97
98 /* check if vbus is valid */
99 regmap_read(regmap, HW_ANADIG_USB1_VBUS_DET_STAT, &val);
100 if (!(val & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)) {
101 dev_err(charger->dev, "vbus is error\n");
102 disable_current_limiter(regmap);
103 return -EINVAL;
104 }
105
106 /* Enable charger detector */
107 regmap_write(regmap, HW_ANADIG_USB1_CHRG_DETECT_CLR,
108 BM_ANADIG_USB1_CHRG_DETECT_EN_B);
109 /*
110 * - Do not check whether a charger is connected to the USB port
111 * - Check whether the USB plug has been in contact with each other
112 */
113 regmap_write(regmap, HW_ANADIG_USB1_CHRG_DETECT_SET,
114 BM_ANADIG_USB1_CHRG_DETECT_CHK_CONTACT |
115 BM_ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
116
117 /* Check if plug is connected */
118 for (i = 0; i < 100; i = i + 1) {
119 regmap_read(regmap, HW_ANADIG_USB1_CHRG_DET_STAT, &val);
120 if (val & BM_ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT) {
121 if (data_pin_contact_count++ > 5)
122 /* Data pin makes contact */
123 break;
124 } else {
125 msleep(20);
126 }
127 }
128
129 if (i == 100) {
130 dev_err(charger->dev,
131 "VBUS is coming from a dedicated power supply.\n");
132 disable_current_limiter(regmap);
133 disable_charger_detector(regmap);
134 return -ENXIO;
135 }
136
137 /*
138 * - Do check whether a charger is connected to the USB port
139 * - Do not Check whether the USB plug has been in contact with
140 * each other
141 */
142 regmap_write(regmap, HW_ANADIG_USB1_CHRG_DETECT_CLR,
143 BM_ANADIG_USB1_CHRG_DETECT_CHK_CONTACT |
144 BM_ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
145 msleep(45);
146
147 /* Check if it is a charger */
148 regmap_read(regmap, HW_ANADIG_USB1_CHRG_DET_STAT, &val);
149 if (!(val & BM_ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) {
150 dev_dbg(charger->dev, "It is a stardard downstream port\n");
151 charger->psy.type = POWER_SUPPLY_TYPE_USB;
152 charger->max_current = 500;
153 disable_charger_detector(regmap);
154 } else {
155 /* It is a charger */
156 disable_charger_detector(regmap);
157 msleep(45);
158 }
159
160 disable_current_limiter(regmap);
161
162 return 0;
163}
164
165/*
166 * imx6_usb_vbus_connect - inform about VBUS connection
167 * @charger: the usb charger
168 *
169 * Inform the charger VBUS is connected, vbus detect supplier should call it.
170 * Besides, the USB device controller is expected to keep the dataline
171 * pullups disabled.
172 */
173int imx6_usb_vbus_connect(struct usb_charger *charger)
174{
175 int ret;
176
177 charger->online = 1;
178
179 mutex_lock(&charger->lock);
180
181 /* Start the 1st period charger detection. */
182 ret = imx6_usb_charger_detect(charger);
183 if (ret)
184 dev_err(charger->dev,
185 "Error occurs during detection: %d\n",
186 ret);
187 else
188 charger->present = 1;
189
190 mutex_unlock(&charger->lock);
191
192 return ret;
193}
194EXPORT_SYMBOL(imx6_usb_vbus_connect);
195
196/*
197 * It must be called after dp is pulled up (from USB controller driver),
198 * That is used to differentiate DCP and CDP
199 */
200int imx6_usb_charger_detect_post(struct usb_charger *charger)
201{
202 struct regmap *regmap = charger->anatop;
203 int val;
204
205 mutex_lock(&charger->lock);
206
207 msleep(40);
208
209 regmap_read(regmap, HW_ANADIG_USB1_CHRG_DET_STAT, &val);
210 if (val & BM_ANADIG_USB1_CHRG_DET_STAT_DM_STATE) {
211 dev_dbg(charger->dev, "It is a dedicate charging port\n");
212 charger->psy.type = POWER_SUPPLY_TYPE_USB_DCP;
213 charger->max_current = 1500;
214 } else {
215 dev_dbg(charger->dev, "It is a charging downstream port\n");
216 charger->psy.type = POWER_SUPPLY_TYPE_USB_CDP;
217 charger->max_current = 900;
218 }
219
220 power_supply_changed(&charger->psy);
221
222 mutex_unlock(&charger->lock);
223
224 return 0;
225}
226EXPORT_SYMBOL(imx6_usb_charger_detect_post);
227
228/*
229 * imx6_usb_vbus_disconnect - inform about VBUS disconnection
230 * @charger: the usb charger
231 *
232 * Inform the charger that VBUS is disconnected. The charging will be
233 * stopped and the charger properties cleared.
234 */
235int imx6_usb_vbus_disconnect(struct usb_charger *charger)
236{
237 charger->online = 0;
238 charger->present = 0;
239 charger->max_current = 0;
240 charger->psy.type = POWER_SUPPLY_TYPE_MAINS;
241
242 power_supply_changed(&charger->psy);
243
244 return 0;
245}
246EXPORT_SYMBOL(imx6_usb_vbus_disconnect);
247
248/*
249 * imx6_usb_create_charger - create a USB charger
250 * @charger: the charger to be initialized
251 * @name: name for the power supply
252
253 * Registers a power supply for the charger. The USB Controller
254 * driver will call this after filling struct usb_charger.
255 */
256int imx6_usb_create_charger(struct usb_charger *charger,
257 const char *name)
258{
259 struct power_supply *psy = &charger->psy;
260
261 if (!charger->dev)
262 return -EINVAL;
263
264 if (name)
265 psy->name = name;
266 else
267 psy->name = "imx6_usb_charger";
268
269 charger->bc = BATTERY_CHARGING_SPEC_1_2;
270 mutex_init(&charger->lock);
271
272 psy->type = POWER_SUPPLY_TYPE_MAINS;
273 psy->properties = imx6_usb_charger_power_props;
274 psy->num_properties = ARRAY_SIZE(imx6_usb_charger_power_props);
275 psy->get_property = imx6_usb_charger_get_property;
276 psy->supplied_to = imx6_usb_charger_supplied_to;
277 psy->num_supplicants = sizeof(imx6_usb_charger_supplied_to)
278 / sizeof(char *);
279
280 return power_supply_register(charger->dev, psy);
281}
282EXPORT_SYMBOL(imx6_usb_create_charger);
283
284/*
285 * imx6_usb_remove_charger - remove a USB charger
286 * @charger: the charger to be removed
287 *
288 * Unregister the chargers power supply.
289 */
290void imx6_usb_remove_charger(struct usb_charger *charger)
291{
292 power_supply_unregister(&charger->psy);
293}
294EXPORT_SYMBOL(imx6_usb_remove_charger);