Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
extcon-max8997.c
Go to the documentation of this file.
1 /*
2  * extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC
3  *
4  * Copyright (C) 2012 Samsung Electrnoics
5  * Donggeun Kim <[email protected]>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  */
17 
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/i2c.h>
21 #include <linux/slab.h>
22 #include <linux/interrupt.h>
23 #include <linux/err.h>
24 #include <linux/platform_device.h>
25 #include <linux/kobject.h>
26 #include <linux/mfd/max8997.h>
28 #include <linux/extcon.h>
29 #include <linux/irqdomain.h>
30 
31 #define DEV_NAME "max8997-muic"
32 
33 /* MAX8997-MUIC STATUS1 register */
34 #define STATUS1_ADC_SHIFT 0
35 #define STATUS1_ADCLOW_SHIFT 5
36 #define STATUS1_ADCERR_SHIFT 6
37 #define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT)
38 #define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT)
39 #define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT)
40 
41 /* MAX8997-MUIC STATUS2 register */
42 #define STATUS2_CHGTYP_SHIFT 0
43 #define STATUS2_CHGDETRUN_SHIFT 3
44 #define STATUS2_DCDTMR_SHIFT 4
45 #define STATUS2_DBCHG_SHIFT 5
46 #define STATUS2_VBVOLT_SHIFT 6
47 #define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
48 #define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT)
49 #define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT)
50 #define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT)
51 #define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT)
52 
53 /* MAX8997-MUIC STATUS3 register */
54 #define STATUS3_OVP_SHIFT 2
55 #define STATUS3_OVP_MASK (0x1 << STATUS3_OVP_SHIFT)
56 
57 /* MAX8997-MUIC CONTROL1 register */
58 #define COMN1SW_SHIFT 0
59 #define COMP2SW_SHIFT 3
60 #define COMN1SW_MASK (0x7 << COMN1SW_SHIFT)
61 #define COMP2SW_MASK (0x7 << COMP2SW_SHIFT)
62 #define SW_MASK (COMP2SW_MASK | COMN1SW_MASK)
63 
64 #define MAX8997_SW_USB ((1 << COMP2SW_SHIFT) | (1 << COMN1SW_SHIFT))
65 #define MAX8997_SW_AUDIO ((2 << COMP2SW_SHIFT) | (2 << COMN1SW_SHIFT))
66 #define MAX8997_SW_UART ((3 << COMP2SW_SHIFT) | (3 << COMN1SW_SHIFT))
67 #define MAX8997_SW_OPEN ((0 << COMP2SW_SHIFT) | (0 << COMN1SW_SHIFT))
68 
69 #define MAX8997_ADC_GROUND 0x00
70 #define MAX8997_ADC_MHL 0x01
71 #define MAX8997_ADC_JIG_USB_1 0x18
72 #define MAX8997_ADC_JIG_USB_2 0x19
73 #define MAX8997_ADC_DESKDOCK 0x1a
74 #define MAX8997_ADC_JIG_UART 0x1c
75 #define MAX8997_ADC_CARDOCK 0x1d
76 #define MAX8997_ADC_OPEN 0x1f
77 
79  unsigned int irq;
80  const char *name;
81  unsigned int virq;
82 };
83 
84 static struct max8997_muic_irq muic_irqs[] = {
85  { MAX8997_MUICIRQ_ADCError, "muic-ADC_error" },
86  { MAX8997_MUICIRQ_ADCLow, "muic-ADC_low" },
87  { MAX8997_MUICIRQ_ADC, "muic-ADC" },
88  { MAX8997_MUICIRQ_VBVolt, "muic-VB_voltage" },
89  { MAX8997_MUICIRQ_DBChg, "muic-DB_charger" },
90  { MAX8997_MUICIRQ_DCDTmr, "muic-DCD_timer" },
91  { MAX8997_MUICIRQ_ChgDetRun, "muic-CDR_status" },
92  { MAX8997_MUICIRQ_ChgTyp, "muic-charger_type" },
93  { MAX8997_MUICIRQ_OVP, "muic-over_voltage" },
94 };
95 
97  struct device *dev;
98  struct i2c_client *muic;
100 
101  int irq;
103 
105  int pre_adc;
106 
107  struct mutex mutex;
108 
109  struct extcon_dev *edev;
110 };
111 
112 const char *max8997_extcon_cable[] = {
113  [0] = "USB",
114  [1] = "USB-Host",
115  [2] = "TA",
116  [3] = "Fast-charger",
117  [4] = "Slow-charger",
118  [5] = "Charge-downstream",
119  [6] = "MHL",
120  [7] = "Dock-desk",
121  [8] = "Dock-card",
122  [9] = "JIG",
123 
124  NULL,
125 };
126 
127 static int max8997_muic_handle_usb(struct max8997_muic_info *info,
128  enum max8997_muic_usb_type usb_type, bool attached)
129 {
130  int ret = 0;
131 
132  if (usb_type == MAX8997_USB_HOST) {
133  /* switch to USB */
135  attached ? MAX8997_SW_USB : MAX8997_SW_OPEN,
136  SW_MASK);
137  if (ret) {
138  dev_err(info->dev, "failed to update muic register\n");
139  goto out;
140  }
141  }
142 
143  switch (usb_type) {
144  case MAX8997_USB_HOST:
145  extcon_set_cable_state(info->edev, "USB-Host", attached);
146  break;
147  case MAX8997_USB_DEVICE:
148  extcon_set_cable_state(info->edev, "USB", attached);
149  break;
150  default:
151  ret = -EINVAL;
152  break;
153  }
154 
155 out:
156  return ret;
157 }
158 
159 static int max8997_muic_handle_dock(struct max8997_muic_info *info,
160  int adc, bool attached)
161 {
162  int ret = 0;
163 
164  /* switch to AUDIO */
166  attached ? MAX8997_SW_AUDIO : MAX8997_SW_OPEN,
167  SW_MASK);
168  if (ret) {
169  dev_err(info->dev, "failed to update muic register\n");
170  goto out;
171  }
172 
173  switch (adc) {
175  extcon_set_cable_state(info->edev, "Dock-desk", attached);
176  break;
177  case MAX8997_ADC_CARDOCK:
178  extcon_set_cable_state(info->edev, "Dock-card", attached);
179  break;
180  default:
181  ret = -EINVAL;
182  break;
183  }
184 out:
185  return ret;
186 }
187 
188 static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
189  bool attached)
190 {
191  int ret = 0;
192 
193  /* switch to UART */
195  attached ? MAX8997_SW_UART : MAX8997_SW_OPEN,
196  SW_MASK);
197  if (ret) {
198  dev_err(info->dev, "failed to update muic register\n");
199  goto out;
200  }
201 
202  extcon_set_cable_state(info->edev, "JIG", attached);
203 out:
204  return ret;
205 }
206 
207 static int max8997_muic_handle_adc_detach(struct max8997_muic_info *info)
208 {
209  int ret = 0;
210 
211  switch (info->pre_adc) {
212  case MAX8997_ADC_GROUND:
213  ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, false);
214  break;
215  case MAX8997_ADC_MHL:
216  extcon_set_cable_state(info->edev, "MHL", false);
217  break;
220  ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, false);
221  break;
223  case MAX8997_ADC_CARDOCK:
224  ret = max8997_muic_handle_dock(info, info->pre_adc, false);
225  break;
227  ret = max8997_muic_handle_jig_uart(info, false);
228  break;
229  default:
230  break;
231  }
232 
233  return ret;
234 }
235 
236 static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc)
237 {
238  int ret = 0;
239 
240  switch (adc) {
241  case MAX8997_ADC_GROUND:
242  ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, true);
243  break;
244  case MAX8997_ADC_MHL:
245  extcon_set_cable_state(info->edev, "MHL", true);
246  break;
249  ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, true);
250  break;
252  case MAX8997_ADC_CARDOCK:
253  ret = max8997_muic_handle_dock(info, adc, true);
254  break;
256  ret = max8997_muic_handle_jig_uart(info, true);
257  break;
258  case MAX8997_ADC_OPEN:
259  ret = max8997_muic_handle_adc_detach(info);
260  break;
261  default:
262  ret = -EINVAL;
263  goto out;
264  }
265 
266  info->pre_adc = adc;
267 out:
268  return ret;
269 }
270 
271 static int max8997_muic_handle_charger_type_detach(
272  struct max8997_muic_info *info)
273 {
274  switch (info->pre_charger_type) {
276  extcon_set_cable_state(info->edev, "USB", false);
277  break;
279  extcon_set_cable_state(info->edev, "Charge-downstream", false);
280  break;
282  extcon_set_cable_state(info->edev, "TA", false);
283  break;
285  extcon_set_cable_state(info->edev, "Slow-charger", false);
286  break;
288  extcon_set_cable_state(info->edev, "Fast-charger", false);
289  break;
290  default:
291  return -EINVAL;
292  break;
293  }
294 
295  return 0;
296 }
297 
298 static int max8997_muic_handle_charger_type(struct max8997_muic_info *info,
299  enum max8997_muic_charger_type charger_type)
300 {
301  u8 adc;
302  int ret;
303 
304  ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc);
305  if (ret) {
306  dev_err(info->dev, "failed to read muic register\n");
307  goto out;
308  }
309 
310  switch (charger_type) {
312  ret = max8997_muic_handle_charger_type_detach(info);
313  break;
315  if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) {
316  max8997_muic_handle_usb(info,
317  MAX8997_USB_DEVICE, true);
318  }
319  break;
321  extcon_set_cable_state(info->edev, "Charge-downstream", true);
322  break;
324  extcon_set_cable_state(info->edev, "TA", true);
325  break;
327  extcon_set_cable_state(info->edev, "Slow-charger", true);
328  break;
330  extcon_set_cable_state(info->edev, "Fast-charger", true);
331  break;
332  default:
333  ret = -EINVAL;
334  goto out;
335  }
336 
337  info->pre_charger_type = charger_type;
338 out:
339  return ret;
340 }
341 
342 static void max8997_muic_irq_work(struct work_struct *work)
343 {
344  struct max8997_muic_info *info = container_of(work,
345  struct max8997_muic_info, irq_work);
346  u8 status[2];
347  u8 adc, chg_type;
348  int irq_type = 0;
349  int i, ret;
350 
351  mutex_lock(&info->mutex);
352 
354  2, status);
355  if (ret) {
356  dev_err(info->dev, "failed to read muic register\n");
357  mutex_unlock(&info->mutex);
358  return;
359  }
360 
361  dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
362  status[0], status[1]);
363 
364  for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
365  if (info->irq == muic_irqs[i].virq)
366  irq_type = muic_irqs[i].irq;
367 
368  switch (irq_type) {
369  case MAX8997_MUICIRQ_ADC:
370  adc = status[0] & STATUS1_ADC_MASK;
371  adc >>= STATUS1_ADC_SHIFT;
372 
373  max8997_muic_handle_adc(info, adc);
374  break;
376  chg_type = status[1] & STATUS2_CHGTYP_MASK;
377  chg_type >>= STATUS2_CHGTYP_SHIFT;
378 
379  max8997_muic_handle_charger_type(info, chg_type);
380  break;
381  default:
382  dev_info(info->dev, "misc interrupt: irq %d occurred\n",
383  irq_type);
384  break;
385  }
386 
387  mutex_unlock(&info->mutex);
388 
389  return;
390 }
391 
392 static irqreturn_t max8997_muic_irq_handler(int irq, void *data)
393 {
394  struct max8997_muic_info *info = data;
395 
396  dev_dbg(info->dev, "irq:%d\n", irq);
397  info->irq = irq;
398 
399  schedule_work(&info->irq_work);
400 
401  return IRQ_HANDLED;
402 }
403 
404 static void max8997_muic_detect_dev(struct max8997_muic_info *info)
405 {
406  int ret;
407  u8 status[2], adc, chg_type;
408 
410  2, status);
411  if (ret) {
412  dev_err(info->dev, "failed to read muic register\n");
413  return;
414  }
415 
416  dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n",
417  status[0], status[1]);
418 
419  adc = status[0] & STATUS1_ADC_MASK;
420  adc >>= STATUS1_ADC_SHIFT;
421 
422  chg_type = status[1] & STATUS2_CHGTYP_MASK;
423  chg_type >>= STATUS2_CHGTYP_SHIFT;
424 
425  max8997_muic_handle_adc(info, adc);
426  max8997_muic_handle_charger_type(info, chg_type);
427 }
428 
429 static int __devinit max8997_muic_probe(struct platform_device *pdev)
430 {
431  struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
432  struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev);
433  struct max8997_muic_info *info;
434  int ret, i;
435 
436  info = kzalloc(sizeof(struct max8997_muic_info), GFP_KERNEL);
437  if (!info) {
438  dev_err(&pdev->dev, "failed to allocate memory\n");
439  ret = -ENOMEM;
440  goto err_kfree;
441  }
442 
443  info->dev = &pdev->dev;
444  info->muic = max8997->muic;
445 
446  platform_set_drvdata(pdev, info);
447  mutex_init(&info->mutex);
448 
449  INIT_WORK(&info->irq_work, max8997_muic_irq_work);
450 
451  for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
452  struct max8997_muic_irq *muic_irq = &muic_irqs[i];
453  int virq = 0;
454 
455  virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
456  if (!virq)
457  goto err_irq;
458  muic_irq->virq = virq;
459 
460  ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler,
461  0, muic_irq->name, info);
462  if (ret) {
463  dev_err(&pdev->dev,
464  "failed: irq request (IRQ: %d,"
465  " error :%d)\n",
466  muic_irq->irq, ret);
467  goto err_irq;
468  }
469  }
470 
471  /* External connector */
472  info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL);
473  if (!info->edev) {
474  dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
475  ret = -ENOMEM;
476  goto err_irq;
477  }
478  info->edev->name = DEV_NAME;
479  info->edev->supported_cable = max8997_extcon_cable;
480  ret = extcon_dev_register(info->edev, NULL);
481  if (ret) {
482  dev_err(&pdev->dev, "failed to register extcon device\n");
483  goto err_extcon;
484  }
485 
486  /* Initialize registers according to platform data */
487  if (pdata->muic_pdata) {
488  struct max8997_muic_platform_data *mdata = info->muic_pdata;
489 
490  for (i = 0; i < mdata->num_init_data; i++) {
491  max8997_write_reg(info->muic, mdata->init_data[i].addr,
492  mdata->init_data[i].data);
493  }
494  }
495 
496  /* Initial device detection */
497  max8997_muic_detect_dev(info);
498 
499  return ret;
500 
501 err_extcon:
502  kfree(info->edev);
503 err_irq:
504  while (--i >= 0)
505  free_irq(muic_irqs[i].virq, info);
506  kfree(info);
507 err_kfree:
508  return ret;
509 }
510 
511 static int __devexit max8997_muic_remove(struct platform_device *pdev)
512 {
513  struct max8997_muic_info *info = platform_get_drvdata(pdev);
514  int i;
515 
516  for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
517  free_irq(muic_irqs[i].virq, info);
518  cancel_work_sync(&info->irq_work);
519 
521 
522  kfree(info->edev);
523  kfree(info);
524 
525  return 0;
526 }
527 
528 static struct platform_driver max8997_muic_driver = {
529  .driver = {
530  .name = DEV_NAME,
531  .owner = THIS_MODULE,
532  },
533  .probe = max8997_muic_probe,
534  .remove = __devexit_p(max8997_muic_remove),
535 };
536 
537 module_platform_driver(max8997_muic_driver);
538 
539 MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver");
540 MODULE_AUTHOR("Donggeun Kim <[email protected]>");
541 MODULE_LICENSE("GPL");