diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-08 23:14:19 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-08 23:14:19 -0500 |
commit | 52cb6a205c9750353cbb58dbf7eea1e046c89819 (patch) | |
tree | 0fdac6d6e9b4e805d8010adf90828d0475836825 | |
parent | 19c055738b571c920c6e313fa31ac8c5ef4ce5a1 (diff) | |
parent | 6544dfa5795060b01042fd62fd1a92e18c2fc485 (diff) |
Merge tag 'extcon-next-for-3.14' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon into char-misc-next
Chanwoo writes:
Update extcon for v3.14
This patchset add new driver of extcon-max14577.c which detect various external
connector and fix minor issue of extcon provider driver(extcon-arizona/palams/
gpio.c). Also, update documentation of previous 'switch' porting guide and
extcon git repository url.
Detailed description for patchset:
- New driver of extcon-max14577.c
: Add extcon-max14577.c drvier to support Maxim MUIC(Micro USB Interface
Controller) which detect USB/TA/JIG/AUDIO-DOCK and additional accessory
according to each resistance when connected external connector.
- extcon-arizoan.c driver
: Code clean to use define macro instead of hex value
: Fix minor issue to reset back to our staring state
: Fix race with microphone detection and removal
- extcon-palmas.c driver
: Fix minor issue and renaming compatible string of Devicetree
- extcon-gpio.c driver
: Fix bug about ordering initialization of gpio pin on probe()
: Send uevent after wakeup from suspend state because some SoC
haven't wakeup interrupt on suspend state.
- Documentation (Documentation/extcon/porting-android-switch-class)
: Fix switch class porting guide
- Update extcon git repository url
-rw-r--r-- | Documentation/devicetree/bindings/extcon/extcon-palmas.txt | 6 | ||||
-rw-r--r-- | Documentation/extcon/porting-android-switch-class | 9 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/extcon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/extcon/Makefile | 1 | ||||
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 73 | ||||
-rw-r--r-- | drivers/extcon/extcon-gpio.c | 32 | ||||
-rw-r--r-- | drivers/extcon/extcon-max14577.c | 752 | ||||
-rw-r--r-- | drivers/extcon/extcon-palmas.c | 17 | ||||
-rw-r--r-- | include/linux/extcon/extcon-gpio.h | 1 | ||||
-rw-r--r-- | include/linux/mfd/arizona/registers.h | 9 |
11 files changed, 876 insertions, 35 deletions
diff --git a/Documentation/devicetree/bindings/extcon/extcon-palmas.txt b/Documentation/devicetree/bindings/extcon/extcon-palmas.txt index 7dab6a8f4a0e..45414bbcd945 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-palmas.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-palmas.txt | |||
@@ -2,7 +2,11 @@ EXTCON FOR PALMAS/TWL CHIPS | |||
2 | 2 | ||
3 | PALMAS USB COMPARATOR | 3 | PALMAS USB COMPARATOR |
4 | Required Properties: | 4 | Required Properties: |
5 | - compatible : Should be "ti,palmas-usb" or "ti,twl6035-usb" | 5 | - compatible: should contain one of: |
6 | * "ti,palmas-usb-vid". | ||
7 | * "ti,twl6035-usb-vid". | ||
8 | * "ti,palmas-usb" (DEPRECATED - use "ti,palmas-usb-vid"). | ||
9 | * "ti,twl6035-usb" (DEPRECATED - use "ti,twl6035-usb-vid"). | ||
6 | 10 | ||
7 | Optional Properties: | 11 | Optional Properties: |
8 | - ti,wakeup : To enable the wakeup comparator in probe | 12 | - ti,wakeup : To enable the wakeup comparator in probe |
diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class index 5377f6317961..49c81caef84d 100644 --- a/Documentation/extcon/porting-android-switch-class +++ b/Documentation/extcon/porting-android-switch-class | |||
@@ -50,7 +50,7 @@ so that they are still compatible with legacy userspace processes. | |||
50 | Extcon's extended features for switch device drivers with | 50 | Extcon's extended features for switch device drivers with |
51 | complex features usually required magic numbers in state | 51 | complex features usually required magic numbers in state |
52 | value of switch_dev. With extcon, such magic numbers that | 52 | value of switch_dev. With extcon, such magic numbers that |
53 | support multiple cables ( | 53 | support multiple cables are no more required or supported. |
54 | 54 | ||
55 | 1. Define cable names at edev->supported_cable. | 55 | 1. Define cable names at edev->supported_cable. |
56 | 2. (Recommended) remove print_state callback. | 56 | 2. (Recommended) remove print_state callback. |
@@ -114,11 +114,8 @@ exclusive, the two cables cannot be in ATTACHED state simulteneously. | |||
114 | 114 | ||
115 | ****** ABI Location | 115 | ****** ABI Location |
116 | 116 | ||
117 | If "CONFIG_ANDROID" is enabled and "CONFIG_ANDROID_SWITCH" is | 117 | If "CONFIG_ANDROID" is enabled, /sys/class/switch/* are created |
118 | disabled, /sys/class/switch/* are created as symbolic links to | 118 | as symbolic links to /sys/class/extcon/*. |
119 | /sys/class/extcon/*. Because CONFIG_ANDROID_SWITCH creates | ||
120 | /sys/class/switch directory, we disable symboling linking if | ||
121 | CONFIG_ANDROID_SWITCH is enabled. | ||
122 | 119 | ||
123 | The two files of switch class, name and state, are provided with | 120 | The two files of switch class, name and state, are provided with |
124 | extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is | 121 | extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is |
diff --git a/MAINTAINERS b/MAINTAINERS index 0a1475b6b358..cba11d2acd3a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -3330,6 +3330,7 @@ EXTERNAL CONNECTOR SUBSYSTEM (EXTCON) | |||
3330 | M: MyungJoo Ham <myungjoo.ham@samsung.com> | 3330 | M: MyungJoo Ham <myungjoo.ham@samsung.com> |
3331 | M: Chanwoo Choi <cw00.choi@samsung.com> | 3331 | M: Chanwoo Choi <cw00.choi@samsung.com> |
3332 | L: linux-kernel@vger.kernel.org | 3332 | L: linux-kernel@vger.kernel.org |
3333 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git | ||
3333 | S: Maintained | 3334 | S: Maintained |
3334 | F: drivers/extcon/ | 3335 | F: drivers/extcon/ |
3335 | F: Documentation/extcon/ | 3336 | F: Documentation/extcon/ |
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index f1d54a3985bd..bdb5a00f1dfa 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig | |||
@@ -31,6 +31,16 @@ config EXTCON_ADC_JACK | |||
31 | help | 31 | help |
32 | Say Y here to enable extcon device driver based on ADC values. | 32 | Say Y here to enable extcon device driver based on ADC values. |
33 | 33 | ||
34 | config EXTCON_MAX14577 | ||
35 | tristate "MAX14577 EXTCON Support" | ||
36 | depends on MFD_MAX14577 | ||
37 | select IRQ_DOMAIN | ||
38 | select REGMAP_I2C | ||
39 | help | ||
40 | If you say yes here you get support for the MUIC device of | ||
41 | Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory | ||
42 | detector and switch. | ||
43 | |||
34 | config EXTCON_MAX77693 | 44 | config EXTCON_MAX77693 |
35 | tristate "MAX77693 EXTCON Support" | 45 | tristate "MAX77693 EXTCON Support" |
36 | depends on MFD_MAX77693 && INPUT | 46 | depends on MFD_MAX77693 && INPUT |
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 759fdae46f95..43eccc0e3448 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_OF_EXTCON) += of_extcon.o | |||
7 | obj-$(CONFIG_EXTCON) += extcon-class.o | 7 | obj-$(CONFIG_EXTCON) += extcon-class.o |
8 | obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o | 8 | obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o |
9 | obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o | 9 | obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o |
10 | obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o | ||
10 | obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o | 11 | obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o |
11 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o | 12 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o |
12 | obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o | 13 | obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o |
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index a287cece0593..c20602f601ee 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c | |||
@@ -44,6 +44,15 @@ | |||
44 | #define HPDET_DEBOUNCE 500 | 44 | #define HPDET_DEBOUNCE 500 |
45 | #define DEFAULT_MICD_TIMEOUT 2000 | 45 | #define DEFAULT_MICD_TIMEOUT 2000 |
46 | 46 | ||
47 | #define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \ | ||
48 | ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \ | ||
49 | ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \ | ||
50 | ARIZONA_MICD_LVL_7) | ||
51 | |||
52 | #define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7) | ||
53 | |||
54 | #define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8) | ||
55 | |||
47 | struct arizona_extcon_info { | 56 | struct arizona_extcon_info { |
48 | struct device *dev; | 57 | struct device *dev; |
49 | struct arizona *arizona; | 58 | struct arizona *arizona; |
@@ -426,26 +435,15 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) | |||
426 | } | 435 | } |
427 | 436 | ||
428 | val &= ARIZONA_HP_LVL_B_MASK; | 437 | val &= ARIZONA_HP_LVL_B_MASK; |
438 | /* Convert to ohms, the value is in 0.5 ohm increments */ | ||
439 | val /= 2; | ||
429 | 440 | ||
430 | regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, | 441 | regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, |
431 | &range); | 442 | &range); |
432 | range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) | 443 | range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) |
433 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; | 444 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; |
434 | 445 | ||
435 | /* Skip up or down a range? */ | 446 | /* Skip up a range, or report? */ |
436 | if (range && (val < arizona_hpdet_c_ranges[range].min)) { | ||
437 | range--; | ||
438 | dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", | ||
439 | arizona_hpdet_c_ranges[range].min, | ||
440 | arizona_hpdet_c_ranges[range].max); | ||
441 | regmap_update_bits(arizona->regmap, | ||
442 | ARIZONA_HEADPHONE_DETECT_1, | ||
443 | ARIZONA_HP_IMPEDANCE_RANGE_MASK, | ||
444 | range << | ||
445 | ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); | ||
446 | return -EAGAIN; | ||
447 | } | ||
448 | |||
449 | if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 && | 447 | if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 && |
450 | (val >= arizona_hpdet_c_ranges[range].max)) { | 448 | (val >= arizona_hpdet_c_ranges[range].max)) { |
451 | range++; | 449 | range++; |
@@ -459,6 +457,12 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) | |||
459 | ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); | 457 | ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); |
460 | return -EAGAIN; | 458 | return -EAGAIN; |
461 | } | 459 | } |
460 | |||
461 | if (range && (val < arizona_hpdet_c_ranges[range].min)) { | ||
462 | dev_dbg(arizona->dev, "Reporting range boundary %d\n", | ||
463 | arizona_hpdet_c_ranges[range].min); | ||
464 | val = arizona_hpdet_c_ranges[range].min; | ||
465 | } | ||
462 | } | 466 | } |
463 | 467 | ||
464 | dev_dbg(arizona->dev, "HP impedance %d ohms\n", val); | 468 | dev_dbg(arizona->dev, "HP impedance %d ohms\n", val); |
@@ -594,9 +598,15 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) | |||
594 | dev_err(arizona->dev, "Failed to report HP/line: %d\n", | 598 | dev_err(arizona->dev, "Failed to report HP/line: %d\n", |
595 | ret); | 599 | ret); |
596 | 600 | ||
601 | done: | ||
602 | /* Reset back to starting range */ | ||
603 | regmap_update_bits(arizona->regmap, | ||
604 | ARIZONA_HEADPHONE_DETECT_1, | ||
605 | ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, | ||
606 | 0); | ||
607 | |||
597 | arizona_extcon_do_magic(info, 0); | 608 | arizona_extcon_do_magic(info, 0); |
598 | 609 | ||
599 | done: | ||
600 | if (id_gpio) | 610 | if (id_gpio) |
601 | gpio_set_value_cansleep(id_gpio, 0); | 611 | gpio_set_value_cansleep(id_gpio, 0); |
602 | 612 | ||
@@ -765,7 +775,20 @@ static void arizona_micd_detect(struct work_struct *work) | |||
765 | 775 | ||
766 | mutex_lock(&info->lock); | 776 | mutex_lock(&info->lock); |
767 | 777 | ||
768 | for (i = 0; i < 10 && !(val & 0x7fc); i++) { | 778 | /* If the cable was removed while measuring ignore the result */ |
779 | ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); | ||
780 | if (ret < 0) { | ||
781 | dev_err(arizona->dev, "Failed to check cable state: %d\n", | ||
782 | ret); | ||
783 | mutex_unlock(&info->lock); | ||
784 | return; | ||
785 | } else if (!ret) { | ||
786 | dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); | ||
787 | mutex_unlock(&info->lock); | ||
788 | return; | ||
789 | } | ||
790 | |||
791 | for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) { | ||
769 | ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); | 792 | ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); |
770 | if (ret != 0) { | 793 | if (ret != 0) { |
771 | dev_err(arizona->dev, | 794 | dev_err(arizona->dev, |
@@ -784,7 +807,7 @@ static void arizona_micd_detect(struct work_struct *work) | |||
784 | } | 807 | } |
785 | } | 808 | } |
786 | 809 | ||
787 | if (i == 10 && !(val & 0x7fc)) { | 810 | if (i == 10 && !(val & MICD_LVL_0_TO_8)) { |
788 | dev_err(arizona->dev, "Failed to get valid MICDET value\n"); | 811 | dev_err(arizona->dev, "Failed to get valid MICDET value\n"); |
789 | mutex_unlock(&info->lock); | 812 | mutex_unlock(&info->lock); |
790 | return; | 813 | return; |
@@ -798,7 +821,7 @@ static void arizona_micd_detect(struct work_struct *work) | |||
798 | } | 821 | } |
799 | 822 | ||
800 | /* If we got a high impedence we should have a headset, report it. */ | 823 | /* If we got a high impedence we should have a headset, report it. */ |
801 | if (info->detecting && (val & 0x400)) { | 824 | if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { |
802 | arizona_identify_headphone(info); | 825 | arizona_identify_headphone(info); |
803 | 826 | ||
804 | ret = extcon_update_state(&info->edev, | 827 | ret = extcon_update_state(&info->edev, |
@@ -827,7 +850,7 @@ static void arizona_micd_detect(struct work_struct *work) | |||
827 | * plain headphones. If both polarities report a low | 850 | * plain headphones. If both polarities report a low |
828 | * impedence then give up and report headphones. | 851 | * impedence then give up and report headphones. |
829 | */ | 852 | */ |
830 | if (info->detecting && (val & 0x3f8)) { | 853 | if (info->detecting && (val & MICD_LVL_1_TO_7)) { |
831 | if (info->jack_flips >= info->micd_num_modes * 10) { | 854 | if (info->jack_flips >= info->micd_num_modes * 10) { |
832 | dev_dbg(arizona->dev, "Detected HP/line\n"); | 855 | dev_dbg(arizona->dev, "Detected HP/line\n"); |
833 | arizona_identify_headphone(info); | 856 | arizona_identify_headphone(info); |
@@ -851,7 +874,7 @@ static void arizona_micd_detect(struct work_struct *work) | |||
851 | * If we're still detecting and we detect a short then we've | 874 | * If we're still detecting and we detect a short then we've |
852 | * got a headphone. Otherwise it's a button press. | 875 | * got a headphone. Otherwise it's a button press. |
853 | */ | 876 | */ |
854 | if (val & 0x3fc) { | 877 | if (val & MICD_LVL_0_TO_7) { |
855 | if (info->mic) { | 878 | if (info->mic) { |
856 | dev_dbg(arizona->dev, "Mic button detected\n"); | 879 | dev_dbg(arizona->dev, "Mic button detected\n"); |
857 | 880 | ||
@@ -1126,6 +1149,16 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
1126 | break; | 1149 | break; |
1127 | } | 1150 | } |
1128 | break; | 1151 | break; |
1152 | case WM5110: | ||
1153 | switch (arizona->rev) { | ||
1154 | case 0 ... 2: | ||
1155 | break; | ||
1156 | default: | ||
1157 | info->micd_clamp = true; | ||
1158 | info->hpdet_ip = 2; | ||
1159 | break; | ||
1160 | } | ||
1161 | break; | ||
1129 | default: | 1162 | default: |
1130 | break; | 1163 | break; |
1131 | } | 1164 | } |
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 7e0dff58e494..a63a6b21c9ad 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c | |||
@@ -40,6 +40,7 @@ struct gpio_extcon_data { | |||
40 | int irq; | 40 | int irq; |
41 | struct delayed_work work; | 41 | struct delayed_work work; |
42 | unsigned long debounce_jiffies; | 42 | unsigned long debounce_jiffies; |
43 | bool check_on_resume; | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | static void gpio_extcon_work(struct work_struct *work) | 46 | static void gpio_extcon_work(struct work_struct *work) |
@@ -103,8 +104,15 @@ static int gpio_extcon_probe(struct platform_device *pdev) | |||
103 | extcon_data->gpio_active_low = pdata->gpio_active_low; | 104 | extcon_data->gpio_active_low = pdata->gpio_active_low; |
104 | extcon_data->state_on = pdata->state_on; | 105 | extcon_data->state_on = pdata->state_on; |
105 | extcon_data->state_off = pdata->state_off; | 106 | extcon_data->state_off = pdata->state_off; |
107 | extcon_data->check_on_resume = pdata->check_on_resume; | ||
106 | if (pdata->state_on && pdata->state_off) | 108 | if (pdata->state_on && pdata->state_off) |
107 | extcon_data->edev.print_state = extcon_gpio_print_state; | 109 | extcon_data->edev.print_state = extcon_gpio_print_state; |
110 | |||
111 | ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, | ||
112 | pdev->name); | ||
113 | if (ret < 0) | ||
114 | return ret; | ||
115 | |||
108 | if (pdata->debounce) { | 116 | if (pdata->debounce) { |
109 | ret = gpio_set_debounce(extcon_data->gpio, | 117 | ret = gpio_set_debounce(extcon_data->gpio, |
110 | pdata->debounce * 1000); | 118 | pdata->debounce * 1000); |
@@ -117,11 +125,6 @@ static int gpio_extcon_probe(struct platform_device *pdev) | |||
117 | if (ret < 0) | 125 | if (ret < 0) |
118 | return ret; | 126 | return ret; |
119 | 127 | ||
120 | ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, | ||
121 | pdev->name); | ||
122 | if (ret < 0) | ||
123 | goto err; | ||
124 | |||
125 | INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work); | 128 | INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work); |
126 | 129 | ||
127 | extcon_data->irq = gpio_to_irq(extcon_data->gpio); | 130 | extcon_data->irq = gpio_to_irq(extcon_data->gpio); |
@@ -159,12 +162,31 @@ static int gpio_extcon_remove(struct platform_device *pdev) | |||
159 | return 0; | 162 | return 0; |
160 | } | 163 | } |
161 | 164 | ||
165 | #ifdef CONFIG_PM_SLEEP | ||
166 | static int gpio_extcon_resume(struct device *dev) | ||
167 | { | ||
168 | struct gpio_extcon_data *extcon_data; | ||
169 | |||
170 | extcon_data = dev_get_drvdata(dev); | ||
171 | if (extcon_data->check_on_resume) | ||
172 | queue_delayed_work(system_power_efficient_wq, | ||
173 | &extcon_data->work, extcon_data->debounce_jiffies); | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | #endif | ||
178 | |||
179 | static const struct dev_pm_ops gpio_extcon_pm_ops = { | ||
180 | SET_SYSTEM_SLEEP_PM_OPS(NULL, gpio_extcon_resume) | ||
181 | }; | ||
182 | |||
162 | static struct platform_driver gpio_extcon_driver = { | 183 | static struct platform_driver gpio_extcon_driver = { |
163 | .probe = gpio_extcon_probe, | 184 | .probe = gpio_extcon_probe, |
164 | .remove = gpio_extcon_remove, | 185 | .remove = gpio_extcon_remove, |
165 | .driver = { | 186 | .driver = { |
166 | .name = "extcon-gpio", | 187 | .name = "extcon-gpio", |
167 | .owner = THIS_MODULE, | 188 | .owner = THIS_MODULE, |
189 | .pm = &gpio_extcon_pm_ops, | ||
168 | }, | 190 | }, |
169 | }; | 191 | }; |
170 | 192 | ||
diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c new file mode 100644 index 000000000000..3846941801b8 --- /dev/null +++ b/drivers/extcon/extcon-max14577.c | |||
@@ -0,0 +1,752 @@ | |||
1 | /* | ||
2 | * extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electrnoics | ||
5 | * Chanwoo Choi <cw00.choi@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/interrupt.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/mfd/max14577.h> | ||
24 | #include <linux/mfd/max14577-private.h> | ||
25 | #include <linux/extcon.h> | ||
26 | |||
27 | #define DEV_NAME "max14577-muic" | ||
28 | #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ | ||
29 | |||
30 | enum max14577_muic_adc_debounce_time { | ||
31 | ADC_DEBOUNCE_TIME_5MS = 0, | ||
32 | ADC_DEBOUNCE_TIME_10MS, | ||
33 | ADC_DEBOUNCE_TIME_25MS, | ||
34 | ADC_DEBOUNCE_TIME_38_62MS, | ||
35 | }; | ||
36 | |||
37 | enum max14577_muic_status { | ||
38 | MAX14577_MUIC_STATUS1 = 0, | ||
39 | MAX14577_MUIC_STATUS2 = 1, | ||
40 | MAX14577_MUIC_STATUS_END, | ||
41 | }; | ||
42 | |||
43 | struct max14577_muic_info { | ||
44 | struct device *dev; | ||
45 | struct max14577 *max14577; | ||
46 | struct extcon_dev *edev; | ||
47 | int prev_cable_type; | ||
48 | int prev_chg_type; | ||
49 | u8 status[MAX14577_MUIC_STATUS_END]; | ||
50 | |||
51 | bool irq_adc; | ||
52 | bool irq_chg; | ||
53 | struct work_struct irq_work; | ||
54 | struct mutex mutex; | ||
55 | |||
56 | /* | ||
57 | * Use delayed workqueue to detect cable state and then | ||
58 | * notify cable state to notifiee/platform through uevent. | ||
59 | * After completing the booting of platform, the extcon provider | ||
60 | * driver should notify cable state to upper layer. | ||
61 | */ | ||
62 | struct delayed_work wq_detcable; | ||
63 | |||
64 | /* | ||
65 | * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB | ||
66 | * h/w path of COMP2/COMN1 on CONTROL1 register. | ||
67 | */ | ||
68 | int path_usb; | ||
69 | int path_uart; | ||
70 | }; | ||
71 | |||
72 | enum max14577_muic_cable_group { | ||
73 | MAX14577_CABLE_GROUP_ADC = 0, | ||
74 | MAX14577_CABLE_GROUP_CHG, | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * struct max14577_muic_irq | ||
79 | * @irq: the index of irq list of MUIC device. | ||
80 | * @name: the name of irq. | ||
81 | * @virq: the virtual irq to use irq domain | ||
82 | */ | ||
83 | struct max14577_muic_irq { | ||
84 | unsigned int irq; | ||
85 | const char *name; | ||
86 | unsigned int virq; | ||
87 | }; | ||
88 | |||
89 | static struct max14577_muic_irq muic_irqs[] = { | ||
90 | { MAX14577_IRQ_INT1_ADC, "muic-ADC" }, | ||
91 | { MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" }, | ||
92 | { MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" }, | ||
93 | { MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" }, | ||
94 | { MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" }, | ||
95 | { MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" }, | ||
96 | { MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" }, | ||
97 | { MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" }, | ||
98 | }; | ||
99 | |||
100 | /* Define supported accessory type */ | ||
101 | enum max14577_muic_acc_type { | ||
102 | MAX14577_MUIC_ADC_GROUND = 0x0, | ||
103 | MAX14577_MUIC_ADC_SEND_END_BUTTON, | ||
104 | MAX14577_MUIC_ADC_REMOTE_S1_BUTTON, | ||
105 | MAX14577_MUIC_ADC_REMOTE_S2_BUTTON, | ||
106 | MAX14577_MUIC_ADC_REMOTE_S3_BUTTON, | ||
107 | MAX14577_MUIC_ADC_REMOTE_S4_BUTTON, | ||
108 | MAX14577_MUIC_ADC_REMOTE_S5_BUTTON, | ||
109 | MAX14577_MUIC_ADC_REMOTE_S6_BUTTON, | ||
110 | MAX14577_MUIC_ADC_REMOTE_S7_BUTTON, | ||
111 | MAX14577_MUIC_ADC_REMOTE_S8_BUTTON, | ||
112 | MAX14577_MUIC_ADC_REMOTE_S9_BUTTON, | ||
113 | MAX14577_MUIC_ADC_REMOTE_S10_BUTTON, | ||
114 | MAX14577_MUIC_ADC_REMOTE_S11_BUTTON, | ||
115 | MAX14577_MUIC_ADC_REMOTE_S12_BUTTON, | ||
116 | MAX14577_MUIC_ADC_RESERVED_ACC_1, | ||
117 | MAX14577_MUIC_ADC_RESERVED_ACC_2, | ||
118 | MAX14577_MUIC_ADC_RESERVED_ACC_3, | ||
119 | MAX14577_MUIC_ADC_RESERVED_ACC_4, | ||
120 | MAX14577_MUIC_ADC_RESERVED_ACC_5, | ||
121 | MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2, | ||
122 | MAX14577_MUIC_ADC_PHONE_POWERED_DEV, | ||
123 | MAX14577_MUIC_ADC_TTY_CONVERTER, | ||
124 | MAX14577_MUIC_ADC_UART_CABLE, | ||
125 | MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG, | ||
126 | MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF, | ||
127 | MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON, | ||
128 | MAX14577_MUIC_ADC_AV_CABLE_NOLOAD, | ||
129 | MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG, | ||
130 | MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF, | ||
131 | MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON, | ||
132 | MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1, /* with Remote and Simple Ctrl */ | ||
133 | MAX14577_MUIC_ADC_OPEN, | ||
134 | }; | ||
135 | |||
136 | /* max14577 MUIC device support below list of accessories(external connector) */ | ||
137 | enum { | ||
138 | EXTCON_CABLE_USB = 0, | ||
139 | EXTCON_CABLE_TA, | ||
140 | EXTCON_CABLE_FAST_CHARGER, | ||
141 | EXTCON_CABLE_SLOW_CHARGER, | ||
142 | EXTCON_CABLE_CHARGE_DOWNSTREAM, | ||
143 | EXTCON_CABLE_JIG_USB_ON, | ||
144 | EXTCON_CABLE_JIG_USB_OFF, | ||
145 | EXTCON_CABLE_JIG_UART_OFF, | ||
146 | EXTCON_CABLE_JIG_UART_ON, | ||
147 | |||
148 | _EXTCON_CABLE_NUM, | ||
149 | }; | ||
150 | |||
151 | static const char *max14577_extcon_cable[] = { | ||
152 | [EXTCON_CABLE_USB] = "USB", | ||
153 | [EXTCON_CABLE_TA] = "TA", | ||
154 | [EXTCON_CABLE_FAST_CHARGER] = "Fast-charger", | ||
155 | [EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger", | ||
156 | [EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream", | ||
157 | [EXTCON_CABLE_JIG_USB_ON] = "JIG-USB-ON", | ||
158 | [EXTCON_CABLE_JIG_USB_OFF] = "JIG-USB-OFF", | ||
159 | [EXTCON_CABLE_JIG_UART_OFF] = "JIG-UART-OFF", | ||
160 | [EXTCON_CABLE_JIG_UART_ON] = "JIG-UART-ON", | ||
161 | |||
162 | NULL, | ||
163 | }; | ||
164 | |||
165 | /* | ||
166 | * max14577_muic_set_debounce_time - Set the debounce time of ADC | ||
167 | * @info: the instance including private data of max14577 MUIC | ||
168 | * @time: the debounce time of ADC | ||
169 | */ | ||
170 | static int max14577_muic_set_debounce_time(struct max14577_muic_info *info, | ||
171 | enum max14577_muic_adc_debounce_time time) | ||
172 | { | ||
173 | u8 ret; | ||
174 | |||
175 | switch (time) { | ||
176 | case ADC_DEBOUNCE_TIME_5MS: | ||
177 | case ADC_DEBOUNCE_TIME_10MS: | ||
178 | case ADC_DEBOUNCE_TIME_25MS: | ||
179 | case ADC_DEBOUNCE_TIME_38_62MS: | ||
180 | ret = max14577_update_reg(info->max14577->regmap, | ||
181 | MAX14577_MUIC_REG_CONTROL3, | ||
182 | CTRL3_ADCDBSET_MASK, | ||
183 | time << CTRL3_ADCDBSET_SHIFT); | ||
184 | if (ret) { | ||
185 | dev_err(info->dev, "failed to set ADC debounce time\n"); | ||
186 | return ret; | ||
187 | } | ||
188 | break; | ||
189 | default: | ||
190 | dev_err(info->dev, "invalid ADC debounce time\n"); | ||
191 | return -EINVAL; | ||
192 | } | ||
193 | |||
194 | return 0; | ||
195 | }; | ||
196 | |||
197 | /* | ||
198 | * max14577_muic_set_path - Set hardware line according to attached cable | ||
199 | * @info: the instance including private data of max14577 MUIC | ||
200 | * @value: the path according to attached cable | ||
201 | * @attached: the state of cable (true:attached, false:detached) | ||
202 | * | ||
203 | * The max14577 MUIC device share outside H/W line among a varity of cables | ||
204 | * so, this function set internal path of H/W line according to the type of | ||
205 | * attached cable. | ||
206 | */ | ||
207 | static int max14577_muic_set_path(struct max14577_muic_info *info, | ||
208 | u8 val, bool attached) | ||
209 | { | ||
210 | int ret = 0; | ||
211 | u8 ctrl1, ctrl2 = 0; | ||
212 | |||
213 | /* Set open state to path before changing hw path */ | ||
214 | ret = max14577_update_reg(info->max14577->regmap, | ||
215 | MAX14577_MUIC_REG_CONTROL1, | ||
216 | CLEAR_IDBEN_MICEN_MASK, CTRL1_SW_OPEN); | ||
217 | if (ret < 0) { | ||
218 | dev_err(info->dev, "failed to update MUIC register\n"); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | if (attached) | ||
223 | ctrl1 = val; | ||
224 | else | ||
225 | ctrl1 = CTRL1_SW_OPEN; | ||
226 | |||
227 | ret = max14577_update_reg(info->max14577->regmap, | ||
228 | MAX14577_MUIC_REG_CONTROL1, | ||
229 | CLEAR_IDBEN_MICEN_MASK, ctrl1); | ||
230 | if (ret < 0) { | ||
231 | dev_err(info->dev, "failed to update MUIC register\n"); | ||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | if (attached) | ||
236 | ctrl2 |= CTRL2_CPEN_MASK; /* LowPwr=0, CPEn=1 */ | ||
237 | else | ||
238 | ctrl2 |= CTRL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ | ||
239 | |||
240 | ret = max14577_update_reg(info->max14577->regmap, | ||
241 | MAX14577_REG_CONTROL2, | ||
242 | CTRL2_LOWPWR_MASK | CTRL2_CPEN_MASK, ctrl2); | ||
243 | if (ret < 0) { | ||
244 | dev_err(info->dev, "failed to update MUIC register\n"); | ||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | dev_dbg(info->dev, | ||
249 | "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", | ||
250 | ctrl1, ctrl2, attached ? "attached" : "detached"); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * max14577_muic_get_cable_type - Return cable type and check cable state | ||
257 | * @info: the instance including private data of max14577 MUIC | ||
258 | * @group: the path according to attached cable | ||
259 | * @attached: store cable state and return | ||
260 | * | ||
261 | * This function check the cable state either attached or detached, | ||
262 | * and then divide precise type of cable according to cable group. | ||
263 | * - max14577_CABLE_GROUP_ADC | ||
264 | * - max14577_CABLE_GROUP_CHG | ||
265 | */ | ||
266 | static int max14577_muic_get_cable_type(struct max14577_muic_info *info, | ||
267 | enum max14577_muic_cable_group group, bool *attached) | ||
268 | { | ||
269 | int cable_type = 0; | ||
270 | int adc; | ||
271 | int chg_type; | ||
272 | |||
273 | switch (group) { | ||
274 | case MAX14577_CABLE_GROUP_ADC: | ||
275 | /* | ||
276 | * Read ADC value to check cable type and decide cable state | ||
277 | * according to cable type | ||
278 | */ | ||
279 | adc = info->status[MAX14577_MUIC_STATUS1] & STATUS1_ADC_MASK; | ||
280 | adc >>= STATUS1_ADC_SHIFT; | ||
281 | |||
282 | /* | ||
283 | * Check current cable state/cable type and store cable type | ||
284 | * (info->prev_cable_type) for handling cable when cable is | ||
285 | * detached. | ||
286 | */ | ||
287 | if (adc == MAX14577_MUIC_ADC_OPEN) { | ||
288 | *attached = false; | ||
289 | |||
290 | cable_type = info->prev_cable_type; | ||
291 | info->prev_cable_type = MAX14577_MUIC_ADC_OPEN; | ||
292 | } else { | ||
293 | *attached = true; | ||
294 | |||
295 | cable_type = info->prev_cable_type = adc; | ||
296 | } | ||
297 | break; | ||
298 | case MAX14577_CABLE_GROUP_CHG: | ||
299 | /* | ||
300 | * Read charger type to check cable type and decide cable state | ||
301 | * according to type of charger cable. | ||
302 | */ | ||
303 | chg_type = info->status[MAX14577_MUIC_STATUS2] & | ||
304 | STATUS2_CHGTYP_MASK; | ||
305 | chg_type >>= STATUS2_CHGTYP_SHIFT; | ||
306 | |||
307 | if (chg_type == MAX14577_CHARGER_TYPE_NONE) { | ||
308 | *attached = false; | ||
309 | |||
310 | cable_type = info->prev_chg_type; | ||
311 | info->prev_chg_type = MAX14577_CHARGER_TYPE_NONE; | ||
312 | } else { | ||
313 | *attached = true; | ||
314 | |||
315 | /* | ||
316 | * Check current cable state/cable type and store cable | ||
317 | * type(info->prev_chg_type) for handling cable when | ||
318 | * charger cable is detached. | ||
319 | */ | ||
320 | cable_type = info->prev_chg_type = chg_type; | ||
321 | } | ||
322 | |||
323 | break; | ||
324 | default: | ||
325 | dev_err(info->dev, "Unknown cable group (%d)\n", group); | ||
326 | cable_type = -EINVAL; | ||
327 | break; | ||
328 | } | ||
329 | |||
330 | return cable_type; | ||
331 | } | ||
332 | |||
333 | static int max14577_muic_jig_handler(struct max14577_muic_info *info, | ||
334 | int cable_type, bool attached) | ||
335 | { | ||
336 | char cable_name[32]; | ||
337 | int ret = 0; | ||
338 | u8 path = CTRL1_SW_OPEN; | ||
339 | |||
340 | dev_dbg(info->dev, | ||
341 | "external connector is %s (adc:0x%02x)\n", | ||
342 | attached ? "attached" : "detached", cable_type); | ||
343 | |||
344 | switch (cable_type) { | ||
345 | case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: /* ADC_JIG_USB_OFF */ | ||
346 | /* PATH:AP_USB */ | ||
347 | strcpy(cable_name, "JIG-USB-OFF"); | ||
348 | path = CTRL1_SW_USB; | ||
349 | break; | ||
350 | case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: /* ADC_JIG_USB_ON */ | ||
351 | /* PATH:AP_USB */ | ||
352 | strcpy(cable_name, "JIG-USB-ON"); | ||
353 | path = CTRL1_SW_USB; | ||
354 | break; | ||
355 | case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: /* ADC_JIG_UART_OFF */ | ||
356 | /* PATH:AP_UART */ | ||
357 | strcpy(cable_name, "JIG-UART-OFF"); | ||
358 | path = CTRL1_SW_UART; | ||
359 | break; | ||
360 | default: | ||
361 | dev_err(info->dev, "failed to detect %s jig cable\n", | ||
362 | attached ? "attached" : "detached"); | ||
363 | return -EINVAL; | ||
364 | } | ||
365 | |||
366 | ret = max14577_muic_set_path(info, path, attached); | ||
367 | if (ret < 0) | ||
368 | return ret; | ||
369 | |||
370 | extcon_set_cable_state(info->edev, cable_name, attached); | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int max14577_muic_adc_handler(struct max14577_muic_info *info) | ||
376 | { | ||
377 | int cable_type; | ||
378 | bool attached; | ||
379 | int ret = 0; | ||
380 | |||
381 | /* Check accessory state which is either detached or attached */ | ||
382 | cable_type = max14577_muic_get_cable_type(info, | ||
383 | MAX14577_CABLE_GROUP_ADC, &attached); | ||
384 | |||
385 | dev_dbg(info->dev, | ||
386 | "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n", | ||
387 | attached ? "attached" : "detached", cable_type, | ||
388 | info->prev_cable_type); | ||
389 | |||
390 | switch (cable_type) { | ||
391 | case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: | ||
392 | case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: | ||
393 | case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: | ||
394 | /* JIG */ | ||
395 | ret = max14577_muic_jig_handler(info, cable_type, attached); | ||
396 | if (ret < 0) | ||
397 | return ret; | ||
398 | break; | ||
399 | case MAX14577_MUIC_ADC_GROUND: | ||
400 | case MAX14577_MUIC_ADC_SEND_END_BUTTON: | ||
401 | case MAX14577_MUIC_ADC_REMOTE_S1_BUTTON: | ||
402 | case MAX14577_MUIC_ADC_REMOTE_S2_BUTTON: | ||
403 | case MAX14577_MUIC_ADC_REMOTE_S3_BUTTON: | ||
404 | case MAX14577_MUIC_ADC_REMOTE_S4_BUTTON: | ||
405 | case MAX14577_MUIC_ADC_REMOTE_S5_BUTTON: | ||
406 | case MAX14577_MUIC_ADC_REMOTE_S6_BUTTON: | ||
407 | case MAX14577_MUIC_ADC_REMOTE_S7_BUTTON: | ||
408 | case MAX14577_MUIC_ADC_REMOTE_S8_BUTTON: | ||
409 | case MAX14577_MUIC_ADC_REMOTE_S9_BUTTON: | ||
410 | case MAX14577_MUIC_ADC_REMOTE_S10_BUTTON: | ||
411 | case MAX14577_MUIC_ADC_REMOTE_S11_BUTTON: | ||
412 | case MAX14577_MUIC_ADC_REMOTE_S12_BUTTON: | ||
413 | case MAX14577_MUIC_ADC_RESERVED_ACC_1: | ||
414 | case MAX14577_MUIC_ADC_RESERVED_ACC_2: | ||
415 | case MAX14577_MUIC_ADC_RESERVED_ACC_3: | ||
416 | case MAX14577_MUIC_ADC_RESERVED_ACC_4: | ||
417 | case MAX14577_MUIC_ADC_RESERVED_ACC_5: | ||
418 | case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2: | ||
419 | case MAX14577_MUIC_ADC_PHONE_POWERED_DEV: | ||
420 | case MAX14577_MUIC_ADC_TTY_CONVERTER: | ||
421 | case MAX14577_MUIC_ADC_UART_CABLE: | ||
422 | case MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG: | ||
423 | case MAX14577_MUIC_ADC_AV_CABLE_NOLOAD: | ||
424 | case MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG: | ||
425 | case MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON: | ||
426 | case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1: | ||
427 | /* | ||
428 | * This accessory isn't used in general case if it is specially | ||
429 | * needed to detect additional accessory, should implement | ||
430 | * proper operation when this accessory is attached/detached. | ||
431 | */ | ||
432 | dev_info(info->dev, | ||
433 | "accessory is %s but it isn't used (adc:0x%x)\n", | ||
434 | attached ? "attached" : "detached", cable_type); | ||
435 | return -EAGAIN; | ||
436 | default: | ||
437 | dev_err(info->dev, | ||
438 | "failed to detect %s accessory (adc:0x%x)\n", | ||
439 | attached ? "attached" : "detached", cable_type); | ||
440 | return -EINVAL; | ||
441 | } | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static int max14577_muic_chg_handler(struct max14577_muic_info *info) | ||
447 | { | ||
448 | int chg_type; | ||
449 | bool attached; | ||
450 | int ret = 0; | ||
451 | |||
452 | chg_type = max14577_muic_get_cable_type(info, | ||
453 | MAX14577_CABLE_GROUP_CHG, &attached); | ||
454 | |||
455 | dev_dbg(info->dev, | ||
456 | "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n", | ||
457 | attached ? "attached" : "detached", | ||
458 | chg_type, info->prev_chg_type); | ||
459 | |||
460 | switch (chg_type) { | ||
461 | case MAX14577_CHARGER_TYPE_USB: | ||
462 | /* PATH:AP_USB */ | ||
463 | ret = max14577_muic_set_path(info, info->path_usb, attached); | ||
464 | if (ret < 0) | ||
465 | return ret; | ||
466 | |||
467 | extcon_set_cable_state(info->edev, "USB", attached); | ||
468 | break; | ||
469 | case MAX14577_CHARGER_TYPE_DEDICATED_CHG: | ||
470 | extcon_set_cable_state(info->edev, "TA", attached); | ||
471 | break; | ||
472 | case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: | ||
473 | extcon_set_cable_state(info->edev, | ||
474 | "Charge-downstream", attached); | ||
475 | break; | ||
476 | case MAX14577_CHARGER_TYPE_SPECIAL_500MA: | ||
477 | extcon_set_cable_state(info->edev, "Slow-charger", attached); | ||
478 | break; | ||
479 | case MAX14577_CHARGER_TYPE_SPECIAL_1A: | ||
480 | extcon_set_cable_state(info->edev, "Fast-charger", attached); | ||
481 | break; | ||
482 | case MAX14577_CHARGER_TYPE_NONE: | ||
483 | case MAX14577_CHARGER_TYPE_DEAD_BATTERY: | ||
484 | break; | ||
485 | default: | ||
486 | dev_err(info->dev, | ||
487 | "failed to detect %s accessory (chg_type:0x%x)\n", | ||
488 | attached ? "attached" : "detached", chg_type); | ||
489 | return -EINVAL; | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static void max14577_muic_irq_work(struct work_struct *work) | ||
496 | { | ||
497 | struct max14577_muic_info *info = container_of(work, | ||
498 | struct max14577_muic_info, irq_work); | ||
499 | int ret = 0; | ||
500 | |||
501 | if (!info->edev) | ||
502 | return; | ||
503 | |||
504 | mutex_lock(&info->mutex); | ||
505 | |||
506 | ret = max14577_bulk_read(info->max14577->regmap, | ||
507 | MAX14577_MUIC_REG_STATUS1, info->status, 2); | ||
508 | if (ret) { | ||
509 | dev_err(info->dev, "failed to read MUIC register\n"); | ||
510 | mutex_unlock(&info->mutex); | ||
511 | return; | ||
512 | } | ||
513 | |||
514 | if (info->irq_adc) { | ||
515 | ret = max14577_muic_adc_handler(info); | ||
516 | info->irq_adc = false; | ||
517 | } | ||
518 | if (info->irq_chg) { | ||
519 | ret = max14577_muic_chg_handler(info); | ||
520 | info->irq_chg = false; | ||
521 | } | ||
522 | |||
523 | if (ret < 0) | ||
524 | dev_err(info->dev, "failed to handle MUIC interrupt\n"); | ||
525 | |||
526 | mutex_unlock(&info->mutex); | ||
527 | |||
528 | return; | ||
529 | } | ||
530 | |||
531 | static irqreturn_t max14577_muic_irq_handler(int irq, void *data) | ||
532 | { | ||
533 | struct max14577_muic_info *info = data; | ||
534 | int i, irq_type = -1; | ||
535 | |||
536 | /* | ||
537 | * We may be called multiple times for different nested IRQ-s. | ||
538 | * Including changes in INT1_ADC and INT2_CGHTYP at once. | ||
539 | * However we only need to know whether it was ADC, charger | ||
540 | * or both interrupts so decode IRQ and turn on proper flags. | ||
541 | */ | ||
542 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) | ||
543 | if (irq == muic_irqs[i].virq) | ||
544 | irq_type = muic_irqs[i].irq; | ||
545 | |||
546 | switch (irq_type) { | ||
547 | case MAX14577_IRQ_INT1_ADC: | ||
548 | case MAX14577_IRQ_INT1_ADCLOW: | ||
549 | case MAX14577_IRQ_INT1_ADCERR: | ||
550 | /* Handle all of accessory except for | ||
551 | type of charger accessory */ | ||
552 | info->irq_adc = true; | ||
553 | break; | ||
554 | case MAX14577_IRQ_INT2_CHGTYP: | ||
555 | case MAX14577_IRQ_INT2_CHGDETRUN: | ||
556 | case MAX14577_IRQ_INT2_DCDTMR: | ||
557 | case MAX14577_IRQ_INT2_DBCHG: | ||
558 | case MAX14577_IRQ_INT2_VBVOLT: | ||
559 | /* Handle charger accessory */ | ||
560 | info->irq_chg = true; | ||
561 | break; | ||
562 | default: | ||
563 | dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n", | ||
564 | irq_type); | ||
565 | return IRQ_HANDLED; | ||
566 | } | ||
567 | schedule_work(&info->irq_work); | ||
568 | |||
569 | return IRQ_HANDLED; | ||
570 | } | ||
571 | |||
572 | static int max14577_muic_detect_accessory(struct max14577_muic_info *info) | ||
573 | { | ||
574 | int ret = 0; | ||
575 | int adc; | ||
576 | int chg_type; | ||
577 | bool attached; | ||
578 | |||
579 | mutex_lock(&info->mutex); | ||
580 | |||
581 | /* Read STATUSx register to detect accessory */ | ||
582 | ret = max14577_bulk_read(info->max14577->regmap, | ||
583 | MAX14577_MUIC_REG_STATUS1, info->status, 2); | ||
584 | if (ret) { | ||
585 | dev_err(info->dev, "failed to read MUIC register\n"); | ||
586 | mutex_unlock(&info->mutex); | ||
587 | return ret; | ||
588 | } | ||
589 | |||
590 | adc = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_ADC, | ||
591 | &attached); | ||
592 | if (attached && adc != MAX14577_MUIC_ADC_OPEN) { | ||
593 | ret = max14577_muic_adc_handler(info); | ||
594 | if (ret < 0) { | ||
595 | dev_err(info->dev, "Cannot detect accessory\n"); | ||
596 | mutex_unlock(&info->mutex); | ||
597 | return ret; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | chg_type = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_CHG, | ||
602 | &attached); | ||
603 | if (attached && chg_type != MAX14577_CHARGER_TYPE_NONE) { | ||
604 | ret = max14577_muic_chg_handler(info); | ||
605 | if (ret < 0) { | ||
606 | dev_err(info->dev, "Cannot detect charger accessory\n"); | ||
607 | mutex_unlock(&info->mutex); | ||
608 | return ret; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | mutex_unlock(&info->mutex); | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | static void max14577_muic_detect_cable_wq(struct work_struct *work) | ||
618 | { | ||
619 | struct max14577_muic_info *info = container_of(to_delayed_work(work), | ||
620 | struct max14577_muic_info, wq_detcable); | ||
621 | |||
622 | max14577_muic_detect_accessory(info); | ||
623 | } | ||
624 | |||
625 | static int max14577_muic_probe(struct platform_device *pdev) | ||
626 | { | ||
627 | struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); | ||
628 | struct max14577_muic_info *info; | ||
629 | int delay_jiffies; | ||
630 | int ret; | ||
631 | int i; | ||
632 | u8 id; | ||
633 | |||
634 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||
635 | if (!info) { | ||
636 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
637 | return -ENOMEM; | ||
638 | } | ||
639 | info->dev = &pdev->dev; | ||
640 | info->max14577 = max14577; | ||
641 | |||
642 | platform_set_drvdata(pdev, info); | ||
643 | mutex_init(&info->mutex); | ||
644 | |||
645 | INIT_WORK(&info->irq_work, max14577_muic_irq_work); | ||
646 | |||
647 | /* Support irq domain for max14577 MUIC device */ | ||
648 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { | ||
649 | struct max14577_muic_irq *muic_irq = &muic_irqs[i]; | ||
650 | unsigned int virq = 0; | ||
651 | |||
652 | virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq); | ||
653 | if (!virq) | ||
654 | return -EINVAL; | ||
655 | muic_irq->virq = virq; | ||
656 | |||
657 | ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, | ||
658 | max14577_muic_irq_handler, | ||
659 | IRQF_NO_SUSPEND, | ||
660 | muic_irq->name, info); | ||
661 | if (ret) { | ||
662 | dev_err(&pdev->dev, | ||
663 | "failed: irq request (IRQ: %d," | ||
664 | " error :%d)\n", | ||
665 | muic_irq->irq, ret); | ||
666 | return ret; | ||
667 | } | ||
668 | } | ||
669 | |||
670 | /* Initialize extcon device */ | ||
671 | info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL); | ||
672 | if (!info->edev) { | ||
673 | dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); | ||
674 | return -ENOMEM; | ||
675 | } | ||
676 | info->edev->name = DEV_NAME; | ||
677 | info->edev->supported_cable = max14577_extcon_cable; | ||
678 | ret = extcon_dev_register(info->edev); | ||
679 | if (ret) { | ||
680 | dev_err(&pdev->dev, "failed to register extcon device\n"); | ||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | /* Default h/w line path */ | ||
685 | info->path_usb = CTRL1_SW_USB; | ||
686 | info->path_uart = CTRL1_SW_UART; | ||
687 | delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); | ||
688 | |||
689 | /* Set initial path for UART */ | ||
690 | max14577_muic_set_path(info, info->path_uart, true); | ||
691 | |||
692 | /* Check revision number of MUIC device*/ | ||
693 | ret = max14577_read_reg(info->max14577->regmap, | ||
694 | MAX14577_REG_DEVICEID, &id); | ||
695 | if (ret < 0) { | ||
696 | dev_err(&pdev->dev, "failed to read revision number\n"); | ||
697 | goto err_extcon; | ||
698 | } | ||
699 | dev_info(info->dev, "device ID : 0x%x\n", id); | ||
700 | |||
701 | /* Set ADC debounce time */ | ||
702 | max14577_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); | ||
703 | |||
704 | /* | ||
705 | * Detect accessory after completing the initialization of platform | ||
706 | * | ||
707 | * - Use delayed workqueue to detect cable state and then | ||
708 | * notify cable state to notifiee/platform through uevent. | ||
709 | * After completing the booting of platform, the extcon provider | ||
710 | * driver should notify cable state to upper layer. | ||
711 | */ | ||
712 | INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq); | ||
713 | ret = queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, | ||
714 | delay_jiffies); | ||
715 | if (ret < 0) { | ||
716 | dev_err(&pdev->dev, | ||
717 | "failed to schedule delayed work for cable detect\n"); | ||
718 | goto err_extcon; | ||
719 | } | ||
720 | |||
721 | return ret; | ||
722 | |||
723 | err_extcon: | ||
724 | extcon_dev_unregister(info->edev); | ||
725 | return ret; | ||
726 | } | ||
727 | |||
728 | static int max14577_muic_remove(struct platform_device *pdev) | ||
729 | { | ||
730 | struct max14577_muic_info *info = platform_get_drvdata(pdev); | ||
731 | |||
732 | cancel_work_sync(&info->irq_work); | ||
733 | extcon_dev_unregister(info->edev); | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | static struct platform_driver max14577_muic_driver = { | ||
739 | .driver = { | ||
740 | .name = DEV_NAME, | ||
741 | .owner = THIS_MODULE, | ||
742 | }, | ||
743 | .probe = max14577_muic_probe, | ||
744 | .remove = max14577_muic_remove, | ||
745 | }; | ||
746 | |||
747 | module_platform_driver(max14577_muic_driver); | ||
748 | |||
749 | MODULE_DESCRIPTION("MAXIM 14577 Extcon driver"); | ||
750 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | ||
751 | MODULE_LICENSE("GPL"); | ||
752 | MODULE_ALIAS("platform:extcon-max14577"); | ||
diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 6c91976dd823..2aea4bcdd7f3 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c | |||
@@ -78,20 +78,24 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) | |||
78 | 78 | ||
79 | static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) | 79 | static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) |
80 | { | 80 | { |
81 | unsigned int set; | 81 | unsigned int set, id_src; |
82 | struct palmas_usb *palmas_usb = _palmas_usb; | 82 | struct palmas_usb *palmas_usb = _palmas_usb; |
83 | 83 | ||
84 | palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, | 84 | palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, |
85 | PALMAS_USB_ID_INT_LATCH_SET, &set); | 85 | PALMAS_USB_ID_INT_LATCH_SET, &set); |
86 | palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, | ||
87 | PALMAS_USB_ID_INT_SRC, &id_src); | ||
86 | 88 | ||
87 | if (set & PALMAS_USB_ID_INT_SRC_ID_GND) { | 89 | if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) && |
90 | (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { | ||
88 | palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, | 91 | palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, |
89 | PALMAS_USB_ID_INT_LATCH_CLR, | 92 | PALMAS_USB_ID_INT_LATCH_CLR, |
90 | PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); | 93 | PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); |
91 | palmas_usb->linkstat = PALMAS_USB_STATE_ID; | 94 | palmas_usb->linkstat = PALMAS_USB_STATE_ID; |
92 | extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); | 95 | extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); |
93 | dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); | 96 | dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); |
94 | } else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) { | 97 | } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && |
98 | (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { | ||
95 | palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, | 99 | palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, |
96 | PALMAS_USB_ID_INT_LATCH_CLR, | 100 | PALMAS_USB_ID_INT_LATCH_CLR, |
97 | PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); | 101 | PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); |
@@ -103,6 +107,11 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) | |||
103 | palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; | 107 | palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; |
104 | extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); | 108 | extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); |
105 | dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); | 109 | dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); |
110 | } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && | ||
111 | (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { | ||
112 | palmas_usb->linkstat = PALMAS_USB_STATE_ID; | ||
113 | extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); | ||
114 | dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); | ||
106 | } | 115 | } |
107 | 116 | ||
108 | return IRQ_HANDLED; | 117 | return IRQ_HANDLED; |
@@ -269,7 +278,9 @@ static const struct dev_pm_ops palmas_pm_ops = { | |||
269 | 278 | ||
270 | static struct of_device_id of_palmas_match_tbl[] = { | 279 | static struct of_device_id of_palmas_match_tbl[] = { |
271 | { .compatible = "ti,palmas-usb", }, | 280 | { .compatible = "ti,palmas-usb", }, |
281 | { .compatible = "ti,palmas-usb-vid", }, | ||
272 | { .compatible = "ti,twl6035-usb", }, | 282 | { .compatible = "ti,twl6035-usb", }, |
283 | { .compatible = "ti,twl6035-usb-vid", }, | ||
273 | { /* end */ } | 284 | { /* end */ } |
274 | }; | 285 | }; |
275 | 286 | ||
diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h index 4195810f87fe..8900fdf511c6 100644 --- a/include/linux/extcon/extcon-gpio.h +++ b/include/linux/extcon/extcon-gpio.h | |||
@@ -51,6 +51,7 @@ struct gpio_extcon_platform_data { | |||
51 | /* if NULL, "0" or "1" will be printed */ | 51 | /* if NULL, "0" or "1" will be printed */ |
52 | const char *state_on; | 52 | const char *state_on; |
53 | const char *state_off; | 53 | const char *state_off; |
54 | bool check_on_resume; | ||
54 | }; | 55 | }; |
55 | 56 | ||
56 | #endif /* __EXTCON_GPIO_H__ */ | 57 | #endif /* __EXTCON_GPIO_H__ */ |
diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index cb49417f8ba9..b31976595eba 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h | |||
@@ -2196,6 +2196,15 @@ | |||
2196 | /* | 2196 | /* |
2197 | * R677 (0x2A5) - Mic Detect 3 | 2197 | * R677 (0x2A5) - Mic Detect 3 |
2198 | */ | 2198 | */ |
2199 | #define ARIZONA_MICD_LVL_0 0x0004 /* MICD_LVL - [2] */ | ||
2200 | #define ARIZONA_MICD_LVL_1 0x0008 /* MICD_LVL - [3] */ | ||
2201 | #define ARIZONA_MICD_LVL_2 0x0010 /* MICD_LVL - [4] */ | ||
2202 | #define ARIZONA_MICD_LVL_3 0x0020 /* MICD_LVL - [5] */ | ||
2203 | #define ARIZONA_MICD_LVL_4 0x0040 /* MICD_LVL - [6] */ | ||
2204 | #define ARIZONA_MICD_LVL_5 0x0080 /* MICD_LVL - [7] */ | ||
2205 | #define ARIZONA_MICD_LVL_6 0x0100 /* MICD_LVL - [8] */ | ||
2206 | #define ARIZONA_MICD_LVL_7 0x0200 /* MICD_LVL - [9] */ | ||
2207 | #define ARIZONA_MICD_LVL_8 0x0400 /* MICD_LVL - [10] */ | ||
2199 | #define ARIZONA_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */ | 2208 | #define ARIZONA_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */ |
2200 | #define ARIZONA_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */ | 2209 | #define ARIZONA_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */ |
2201 | #define ARIZONA_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */ | 2210 | #define ARIZONA_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */ |