ptrace: cleanup arch_ptrace() and friends on Blackfin
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / power / isp1704_charger.c
blob72512185f3e281febf049f23dde76464782ed92c
1 /*
2 * ISP1704 USB Charger Detection driver
4 * Copyright (C) 2010 Nokia Corporation
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/err.h>
24 #include <linux/init.h>
25 #include <linux/types.h>
26 #include <linux/device.h>
27 #include <linux/sysfs.h>
28 #include <linux/platform_device.h>
29 #include <linux/power_supply.h>
30 #include <linux/delay.h>
32 #include <linux/usb/otg.h>
33 #include <linux/usb/ulpi.h>
34 #include <linux/usb/ch9.h>
35 #include <linux/usb/gadget.h>
37 /* Vendor specific Power Control register */
38 #define ISP1704_PWR_CTRL 0x3d
39 #define ISP1704_PWR_CTRL_SWCTRL (1 << 0)
40 #define ISP1704_PWR_CTRL_DET_COMP (1 << 1)
41 #define ISP1704_PWR_CTRL_BVALID_RISE (1 << 2)
42 #define ISP1704_PWR_CTRL_BVALID_FALL (1 << 3)
43 #define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4)
44 #define ISP1704_PWR_CTRL_VDAT_DET (1 << 5)
45 #define ISP1704_PWR_CTRL_DPVSRC_EN (1 << 6)
46 #define ISP1704_PWR_CTRL_HWDETECT (1 << 7)
48 #define NXP_VENDOR_ID 0x04cc
50 static u16 isp170x_id[] = {
51 0x1704,
52 0x1707,
55 struct isp1704_charger {
56 struct device *dev;
57 struct power_supply psy;
58 struct otg_transceiver *otg;
59 struct notifier_block nb;
60 struct work_struct work;
62 char model[7];
63 unsigned present:1;
67 * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
68 * is actually a dedicated charger, the following steps need to be taken.
70 static inline int isp1704_charger_verify(struct isp1704_charger *isp)
72 int ret = 0;
73 u8 r;
75 /* Reset the transceiver */
76 r = otg_io_read(isp->otg, ULPI_FUNC_CTRL);
77 r |= ULPI_FUNC_CTRL_RESET;
78 otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
79 usleep_range(1000, 2000);
81 /* Set normal mode */
82 r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
83 otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
85 /* Clear the DP and DM pull-down bits */
86 r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
87 otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), r);
89 /* Enable strong pull-up on DP (1.5K) and reset */
90 r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
91 otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), r);
92 usleep_range(1000, 2000);
94 /* Read the line state */
95 if (!otg_io_read(isp->otg, ULPI_DEBUG)) {
96 /* Disable strong pull-up on DP (1.5K) */
97 otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
98 ULPI_FUNC_CTRL_TERMSELECT);
99 return 1;
102 /* Is it a charger or PS/2 connection */
104 /* Enable weak pull-up resistor on DP */
105 otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL),
106 ISP1704_PWR_CTRL_DP_WKPU_EN);
108 /* Disable strong pull-up on DP (1.5K) */
109 otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
110 ULPI_FUNC_CTRL_TERMSELECT);
112 /* Enable weak pull-down resistor on DM */
113 otg_io_write(isp->otg, ULPI_SET(ULPI_OTG_CTRL),
114 ULPI_OTG_CTRL_DM_PULLDOWN);
116 /* It's a charger if the line states are clear */
117 if (!(otg_io_read(isp->otg, ULPI_DEBUG)))
118 ret = 1;
120 /* Disable weak pull-up resistor on DP */
121 otg_io_write(isp->otg, ULPI_CLR(ISP1704_PWR_CTRL),
122 ISP1704_PWR_CTRL_DP_WKPU_EN);
124 return ret;
127 static inline int isp1704_charger_detect(struct isp1704_charger *isp)
129 unsigned long timeout;
130 u8 r;
131 int ret = 0;
133 /* set SW control bit in PWR_CTRL register */
134 otg_io_write(isp->otg, ISP1704_PWR_CTRL,
135 ISP1704_PWR_CTRL_SWCTRL);
137 /* enable manual charger detection */
138 r = (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN);
139 otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), r);
140 usleep_range(1000, 2000);
142 timeout = jiffies + msecs_to_jiffies(300);
143 do {
144 /* Check if there is a charger */
145 if (otg_io_read(isp->otg, ISP1704_PWR_CTRL)
146 & ISP1704_PWR_CTRL_VDAT_DET) {
147 ret = isp1704_charger_verify(isp);
148 break;
150 } while (!time_after(jiffies, timeout));
152 return ret;
155 static void isp1704_charger_work(struct work_struct *data)
157 int detect;
158 struct isp1704_charger *isp =
159 container_of(data, struct isp1704_charger, work);
162 * FIXME Only supporting dedicated chargers even though isp1704 can
163 * detect HUB and HOST chargers. If the device has already been
164 * enumerated, the detection will break the connection.
166 if (isp->otg->state != OTG_STATE_B_IDLE)
167 return;
169 /* disable data pullups */
170 if (isp->otg->gadget)
171 usb_gadget_disconnect(isp->otg->gadget);
173 /* detect charger */
174 detect = isp1704_charger_detect(isp);
175 if (detect) {
176 isp->present = detect;
177 power_supply_changed(&isp->psy);
180 /* enable data pullups */
181 if (isp->otg->gadget)
182 usb_gadget_connect(isp->otg->gadget);
185 static int isp1704_notifier_call(struct notifier_block *nb,
186 unsigned long event, void *unused)
188 struct isp1704_charger *isp =
189 container_of(nb, struct isp1704_charger, nb);
191 switch (event) {
192 case USB_EVENT_VBUS:
193 schedule_work(&isp->work);
194 break;
195 case USB_EVENT_NONE:
196 if (isp->present) {
197 isp->present = 0;
198 power_supply_changed(&isp->psy);
200 break;
201 default:
202 return NOTIFY_DONE;
205 return NOTIFY_OK;
208 static int isp1704_charger_get_property(struct power_supply *psy,
209 enum power_supply_property psp,
210 union power_supply_propval *val)
212 struct isp1704_charger *isp =
213 container_of(psy, struct isp1704_charger, psy);
215 switch (psp) {
216 case POWER_SUPPLY_PROP_PRESENT:
217 val->intval = isp->present;
218 break;
219 case POWER_SUPPLY_PROP_MODEL_NAME:
220 val->strval = isp->model;
221 break;
222 case POWER_SUPPLY_PROP_MANUFACTURER:
223 val->strval = "NXP";
224 break;
225 default:
226 return -EINVAL;
228 return 0;
231 static enum power_supply_property power_props[] = {
232 POWER_SUPPLY_PROP_PRESENT,
233 POWER_SUPPLY_PROP_MODEL_NAME,
234 POWER_SUPPLY_PROP_MANUFACTURER,
237 static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
239 int vendor;
240 int product;
241 int i;
242 int ret = -ENODEV;
244 /* Test ULPI interface */
245 ret = otg_io_write(isp->otg, ULPI_SCRATCH, 0xaa);
246 if (ret < 0)
247 return ret;
249 ret = otg_io_read(isp->otg, ULPI_SCRATCH);
250 if (ret < 0)
251 return ret;
253 if (ret != 0xaa)
254 return -ENODEV;
256 /* Verify the product and vendor id matches */
257 vendor = otg_io_read(isp->otg, ULPI_VENDOR_ID_LOW);
258 vendor |= otg_io_read(isp->otg, ULPI_VENDOR_ID_HIGH) << 8;
259 if (vendor != NXP_VENDOR_ID)
260 return -ENODEV;
262 product = otg_io_read(isp->otg, ULPI_PRODUCT_ID_LOW);
263 product |= otg_io_read(isp->otg, ULPI_PRODUCT_ID_HIGH) << 8;
265 for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
266 if (product == isp170x_id[i]) {
267 sprintf(isp->model, "isp%x", product);
268 return product;
272 dev_err(isp->dev, "product id %x not matching known ids", product);
274 return -ENODEV;
277 static int __devinit isp1704_charger_probe(struct platform_device *pdev)
279 struct isp1704_charger *isp;
280 int ret = -ENODEV;
282 isp = kzalloc(sizeof *isp, GFP_KERNEL);
283 if (!isp)
284 return -ENOMEM;
286 isp->otg = otg_get_transceiver();
287 if (!isp->otg)
288 goto fail0;
290 ret = isp1704_test_ulpi(isp);
291 if (ret < 0)
292 goto fail1;
294 isp->dev = &pdev->dev;
295 platform_set_drvdata(pdev, isp);
297 isp->psy.name = "isp1704";
298 isp->psy.type = POWER_SUPPLY_TYPE_USB;
299 isp->psy.properties = power_props;
300 isp->psy.num_properties = ARRAY_SIZE(power_props);
301 isp->psy.get_property = isp1704_charger_get_property;
303 ret = power_supply_register(isp->dev, &isp->psy);
304 if (ret)
305 goto fail1;
308 * REVISIT: using work in order to allow the otg notifications to be
309 * made atomically in the future.
311 INIT_WORK(&isp->work, isp1704_charger_work);
313 isp->nb.notifier_call = isp1704_notifier_call;
315 ret = otg_register_notifier(isp->otg, &isp->nb);
316 if (ret)
317 goto fail2;
319 dev_info(isp->dev, "registered with product id %s\n", isp->model);
321 return 0;
322 fail2:
323 power_supply_unregister(&isp->psy);
324 fail1:
325 otg_put_transceiver(isp->otg);
326 fail0:
327 kfree(isp);
329 dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
331 return ret;
334 static int __devexit isp1704_charger_remove(struct platform_device *pdev)
336 struct isp1704_charger *isp = platform_get_drvdata(pdev);
338 otg_unregister_notifier(isp->otg, &isp->nb);
339 power_supply_unregister(&isp->psy);
340 otg_put_transceiver(isp->otg);
341 kfree(isp);
343 return 0;
346 static struct platform_driver isp1704_charger_driver = {
347 .driver = {
348 .name = "isp1704_charger",
350 .probe = isp1704_charger_probe,
351 .remove = __devexit_p(isp1704_charger_remove),
354 static int __init isp1704_charger_init(void)
356 return platform_driver_register(&isp1704_charger_driver);
358 module_init(isp1704_charger_init);
360 static void __exit isp1704_charger_exit(void)
362 platform_driver_unregister(&isp1704_charger_driver);
364 module_exit(isp1704_charger_exit);
366 MODULE_ALIAS("platform:isp1704_charger");
367 MODULE_AUTHOR("Nokia Corporation");
368 MODULE_DESCRIPTION("ISP170x USB Charger driver");
369 MODULE_LICENSE("GPL");