Root/drivers/extcon/extcon-max8997.c

1/*
2 * extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC
3 *
4 * Copyright (C) 2012 Samsung Electrnoics
5 * Donggeun Kim <dg77.kim@samsung.com>
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>
27#include <linux/mfd/max8997-private.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
78struct max8997_muic_irq {
79    unsigned int irq;
80    const char *name;
81    unsigned int virq;
82};
83
84static 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
96struct max8997_muic_info {
97    struct device *dev;
98    struct i2c_client *muic;
99    struct max8997_muic_platform_data *muic_pdata;
100
101    int irq;
102    struct work_struct irq_work;
103
104    enum max8997_muic_charger_type pre_charger_type;
105    int pre_adc;
106
107    struct mutex mutex;
108
109    struct extcon_dev *edev;
110};
111
112const 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
127static 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 */
134        ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1,
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
155out:
156    return ret;
157}
158
159static 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 */
165    ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1,
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) {
174    case MAX8997_ADC_DESKDOCK:
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    }
184out:
185    return ret;
186}
187
188static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
189            bool attached)
190{
191    int ret = 0;
192
193    /* switch to UART */
194    ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1,
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);
203out:
204    return ret;
205}
206
207static 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;
218    case MAX8997_ADC_JIG_USB_1:
219    case MAX8997_ADC_JIG_USB_2:
220        ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, false);
221        break;
222    case MAX8997_ADC_DESKDOCK:
223    case MAX8997_ADC_CARDOCK:
224        ret = max8997_muic_handle_dock(info, info->pre_adc, false);
225        break;
226    case MAX8997_ADC_JIG_UART:
227        ret = max8997_muic_handle_jig_uart(info, false);
228        break;
229    default:
230        break;
231    }
232
233    return ret;
234}
235
236static 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;
247    case MAX8997_ADC_JIG_USB_1:
248    case MAX8997_ADC_JIG_USB_2:
249        ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, true);
250        break;
251    case MAX8997_ADC_DESKDOCK:
252    case MAX8997_ADC_CARDOCK:
253        ret = max8997_muic_handle_dock(info, adc, true);
254        break;
255    case MAX8997_ADC_JIG_UART:
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;
267out:
268    return ret;
269}
270
271static int max8997_muic_handle_charger_type_detach(
272                struct max8997_muic_info *info)
273{
274    int ret = 0;
275
276    switch (info->pre_charger_type) {
277    case MAX8997_CHARGER_TYPE_USB:
278        extcon_set_cable_state(info->edev, "USB", false);
279        break;
280    case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
281        extcon_set_cable_state(info->edev, "Charge-downstream", false);
282        break;
283    case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
284        extcon_set_cable_state(info->edev, "TA", false);
285        break;
286    case MAX8997_CHARGER_TYPE_500MA:
287        extcon_set_cable_state(info->edev, "Slow-charger", false);
288        break;
289    case MAX8997_CHARGER_TYPE_1A:
290        extcon_set_cable_state(info->edev, "Fast-charger", false);
291        break;
292    default:
293        ret = -EINVAL;
294        break;
295    }
296
297    return ret;
298}
299
300static int max8997_muic_handle_charger_type(struct max8997_muic_info *info,
301                enum max8997_muic_charger_type charger_type)
302{
303    u8 adc;
304    int ret;
305
306    ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc);
307    if (ret) {
308        dev_err(info->dev, "failed to read muic register\n");
309        goto out;
310    }
311
312    switch (charger_type) {
313    case MAX8997_CHARGER_TYPE_NONE:
314        ret = max8997_muic_handle_charger_type_detach(info);
315        break;
316    case MAX8997_CHARGER_TYPE_USB:
317        if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) {
318            max8997_muic_handle_usb(info,
319                    MAX8997_USB_DEVICE, true);
320        }
321        break;
322    case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
323        extcon_set_cable_state(info->edev, "Charge-downstream", true);
324        break;
325    case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
326        extcon_set_cable_state(info->edev, "TA", true);
327        break;
328    case MAX8997_CHARGER_TYPE_500MA:
329        extcon_set_cable_state(info->edev, "Slow-charger", true);
330        break;
331    case MAX8997_CHARGER_TYPE_1A:
332        extcon_set_cable_state(info->edev, "Fast-charger", true);
333        break;
334    default:
335        ret = -EINVAL;
336        goto out;
337    }
338
339    info->pre_charger_type = charger_type;
340out:
341    return ret;
342}
343
344static void max8997_muic_irq_work(struct work_struct *work)
345{
346    struct max8997_muic_info *info = container_of(work,
347            struct max8997_muic_info, irq_work);
348    u8 status[2];
349    u8 adc, chg_type;
350    int irq_type = 0;
351    int i, ret;
352
353    mutex_lock(&info->mutex);
354
355    ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
356                2, status);
357    if (ret) {
358        dev_err(info->dev, "failed to read muic register\n");
359        mutex_unlock(&info->mutex);
360        return;
361    }
362
363    dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
364            status[0], status[1]);
365
366    for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
367        if (info->irq == muic_irqs[i].virq)
368            irq_type = muic_irqs[i].irq;
369
370    switch (irq_type) {
371    case MAX8997_MUICIRQ_ADC:
372        adc = status[0] & STATUS1_ADC_MASK;
373        adc >>= STATUS1_ADC_SHIFT;
374
375        max8997_muic_handle_adc(info, adc);
376        break;
377    case MAX8997_MUICIRQ_ChgTyp:
378        chg_type = status[1] & STATUS2_CHGTYP_MASK;
379        chg_type >>= STATUS2_CHGTYP_SHIFT;
380
381        max8997_muic_handle_charger_type(info, chg_type);
382        break;
383    default:
384        dev_info(info->dev, "misc interrupt: irq %d occurred\n",
385                irq_type);
386        break;
387    }
388
389    mutex_unlock(&info->mutex);
390
391    return;
392}
393
394static irqreturn_t max8997_muic_irq_handler(int irq, void *data)
395{
396    struct max8997_muic_info *info = data;
397
398    dev_dbg(info->dev, "irq:%d\n", irq);
399    info->irq = irq;
400
401    schedule_work(&info->irq_work);
402
403    return IRQ_HANDLED;
404}
405
406static void max8997_muic_detect_dev(struct max8997_muic_info *info)
407{
408    int ret;
409    u8 status[2], adc, chg_type;
410
411    ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
412                2, status);
413    if (ret) {
414        dev_err(info->dev, "failed to read muic register\n");
415        return;
416    }
417
418    dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n",
419            status[0], status[1]);
420
421    adc = status[0] & STATUS1_ADC_MASK;
422    adc >>= STATUS1_ADC_SHIFT;
423
424    chg_type = status[1] & STATUS2_CHGTYP_MASK;
425    chg_type >>= STATUS2_CHGTYP_SHIFT;
426
427    max8997_muic_handle_adc(info, adc);
428    max8997_muic_handle_charger_type(info, chg_type);
429}
430
431static int __devinit max8997_muic_probe(struct platform_device *pdev)
432{
433    struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
434    struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev);
435    struct max8997_muic_info *info;
436    int ret, i;
437
438    info = kzalloc(sizeof(struct max8997_muic_info), GFP_KERNEL);
439    if (!info) {
440        dev_err(&pdev->dev, "failed to allocate memory\n");
441        ret = -ENOMEM;
442        goto err_kfree;
443    }
444
445    info->dev = &pdev->dev;
446    info->muic = max8997->muic;
447
448    platform_set_drvdata(pdev, info);
449    mutex_init(&info->mutex);
450
451    INIT_WORK(&info->irq_work, max8997_muic_irq_work);
452
453    for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
454        struct max8997_muic_irq *muic_irq = &muic_irqs[i];
455        int virq = 0;
456
457        virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
458        if (!virq)
459            goto err_irq;
460        muic_irq->virq = virq;
461
462        ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler,
463                0, muic_irq->name, info);
464        if (ret) {
465            dev_err(&pdev->dev,
466                "failed: irq request (IRQ: %d,"
467                " error :%d)\n",
468                muic_irq->irq, ret);
469            goto err_irq;
470        }
471    }
472
473    /* External connector */
474    info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL);
475    if (!info->edev) {
476        dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
477        ret = -ENOMEM;
478        goto err_irq;
479    }
480    info->edev->name = DEV_NAME;
481    info->edev->supported_cable = max8997_extcon_cable;
482    ret = extcon_dev_register(info->edev, NULL);
483    if (ret) {
484        dev_err(&pdev->dev, "failed to register extcon device\n");
485        goto err_extcon;
486    }
487
488    /* Initialize registers according to platform data */
489    if (pdata->muic_pdata) {
490        struct max8997_muic_platform_data *mdata = info->muic_pdata;
491
492        for (i = 0; i < mdata->num_init_data; i++) {
493            max8997_write_reg(info->muic, mdata->init_data[i].addr,
494                    mdata->init_data[i].data);
495        }
496    }
497
498    /* Initial device detection */
499    max8997_muic_detect_dev(info);
500
501    return ret;
502
503err_extcon:
504    kfree(info->edev);
505err_irq:
506    while (--i >= 0)
507        free_irq(muic_irqs[i].virq, info);
508    kfree(info);
509err_kfree:
510    return ret;
511}
512
513static int __devexit max8997_muic_remove(struct platform_device *pdev)
514{
515    struct max8997_muic_info *info = platform_get_drvdata(pdev);
516    int i;
517
518    for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
519        free_irq(muic_irqs[i].virq, info);
520    cancel_work_sync(&info->irq_work);
521
522    extcon_dev_unregister(info->edev);
523
524    kfree(info->edev);
525    kfree(info);
526
527    return 0;
528}
529
530static struct platform_driver max8997_muic_driver = {
531    .driver = {
532        .name = DEV_NAME,
533        .owner = THIS_MODULE,
534    },
535    .probe = max8997_muic_probe,
536    .remove = __devexit_p(max8997_muic_remove),
537};
538
539module_platform_driver(max8997_muic_driver);
540
541MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver");
542MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
543MODULE_LICENSE("GPL");
544

Archive Download this file



interactive