Update extcon for v4.3

This patchset include the function update of extcon drivers without critical
 update and fix minor issue of extcon drivers.
 
 Detailed description for patchset:
 1. Update the extcon drivers:
 - Update the logic of microphone detection for extcon-arizona driver
 - Support GPIO based USB ID detection of extcon-palmas driver
 
 2. Fix minor issues:
 - Clean code and remove the opitonal print_state() function pointer from extcon
   core driver
 - Clear interrupt bit state before requesting irq on extcon-max778433 driver
 - Fix signedness bugs of extcon core driver
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJVzIaXAAoJEJzN3yze689T9AYP+wSjsZuqVetmuwchzCrhKBJf
 wLisDyBKyuZ1OdBS0114pFxjrT11ULGQv9kk1kLQgSCVbnKZvMrRdZkkg9lPA2ut
 y9PH/1atvB1lSDZaU73I6Hof+7GEt8U38Hk5Ter69xqvhwn2Txg449+HZvwva+RB
 yrAEDSxeOUPpCGwI/qd913q7918LvUgNvM1IIHHBuJE3NAfAqpec2Pxo1iJLBlac
 dHOSrxLfVo/xRKUghv3VcP3tzxpznN/xYqYf7t8Q4n0p4pV4FIscqOCMk0jjYISu
 jP/kdEurzAxqIi1qDIRIcbHGSLMyzlD2qk/uvB+i6Vjf/6N5aFXjTasC5SvbLeVq
 jZvuoOmtCG412FnwAZNusMf64R8JLWgEQoc9txtOqB4LORD3uObUB0+IKDQmWWW0
 lySUFO4CZFhhxlSIQSwjESnxAuhxXSTWJIFn8ZJebmorvZgnnyO4mLo2FNuxT9yO
 nURvLZK2FbOo60EGYiwHvv4f7eOV7GuxYpUtunYBwjWmRE8yf5ofRFaHpmi9ynA8
 y1IJKjyF7znPvwABl9wsthkK5Pt/pJTaVyfPDldJAveK5JjBFOHMp3QLmx74FSlw
 Z9TmJ/VIoFg6tZINT997lMpKerGBFU2TlNpJFCb0fRUurd9xNfMCjhIqhDyRvVhJ
 6BOMAHTxLjH7eXYarEp8
 =Cd4Z
 -----END PGP SIGNATURE-----

Merge tag 'extcon-next-for-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon into char-misc-testing

Chanwoo writes:

Update extcon for v4.3

This patchset include the function update of extcon drivers without critical
update and fix minor issue of extcon drivers.

Detailed description for patchset:
1. Update the extcon drivers:
- Update the logic of microphone detection for extcon-arizona driver
- Support GPIO based USB ID detection of extcon-palmas driver

2. Fix minor issues:
- Clean code and remove the opitonal print_state() function pointer from extcon
  core driver
- Clear interrupt bit state before requesting irq on extcon-max778433 driver
- Fix signedness bugs of extcon core driver
This commit is contained in:
Greg Kroah-Hartman 2015-08-17 09:12:15 -07:00
commit 672cfeeb93
11 changed files with 255 additions and 77 deletions

View File

@ -10,8 +10,11 @@ Required Properties:
Optional Properties: Optional Properties:
- ti,wakeup : To enable the wakeup comparator in probe - ti,wakeup : To enable the wakeup comparator in probe
- ti,enable-id-detection: Perform ID detection. - ti,enable-id-detection: Perform ID detection. If id-gpio is specified
it performs id-detection using GPIO else using OTG core.
- ti,enable-vbus-detection: Perform VBUS detection. - ti,enable-vbus-detection: Perform VBUS detection.
- id-gpio: gpio for GPIO ID detection. See gpio binding.
- debounce-delay-ms: debounce delay for GPIO ID pin in milliseconds.
palmas-usb { palmas-usb {
compatible = "ti,twl6035-usb", "ti,palmas-usb"; compatible = "ti,twl6035-usb", "ti,palmas-usb";

View File

@ -20,10 +20,12 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/extcon.h> #include <linux/extcon.h>
@ -46,6 +48,9 @@
#define HPDET_DEBOUNCE 500 #define HPDET_DEBOUNCE 500
#define DEFAULT_MICD_TIMEOUT 2000 #define DEFAULT_MICD_TIMEOUT 2000
#define MICD_DBTIME_TWO_READINGS 2
#define MICD_DBTIME_FOUR_READINGS 4
#define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \ #define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \
ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \ ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \
ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \ ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \
@ -94,6 +99,8 @@ struct arizona_extcon_info {
int hpdet_ip_version; int hpdet_ip_version;
struct extcon_dev *edev; struct extcon_dev *edev;
struct gpio_desc *micd_pol_gpio;
}; };
static const struct arizona_micd_config micd_default_modes[] = { static const struct arizona_micd_config micd_default_modes[] = {
@ -204,6 +211,10 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
if (arizona->pdata.micd_pol_gpio > 0) if (arizona->pdata.micd_pol_gpio > 0)
gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio, gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
info->micd_modes[mode].gpio); info->micd_modes[mode].gpio);
else
gpiod_set_value_cansleep(info->micd_pol_gpio,
info->micd_modes[mode].gpio);
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_BIAS_SRC_MASK, ARIZONA_MICD_BIAS_SRC_MASK,
info->micd_modes[mode].bias << info->micd_modes[mode].bias <<
@ -757,10 +768,11 @@ static void arizona_micd_timeout_work(struct work_struct *work)
mutex_lock(&info->lock); mutex_lock(&info->lock);
dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
arizona_identify_headphone(info);
info->detecting = false; info->detecting = false;
arizona_identify_headphone(info);
arizona_stop_mic(info); arizona_stop_mic(info);
mutex_unlock(&info->lock); mutex_unlock(&info->lock);
@ -820,12 +832,18 @@ static void arizona_micd_detect(struct work_struct *work)
/* Due to jack detect this should never happen */ /* Due to jack detect this should never happen */
if (!(val & ARIZONA_MICD_STS)) { if (!(val & ARIZONA_MICD_STS)) {
dev_warn(arizona->dev, "Detected open circuit\n"); dev_warn(arizona->dev, "Detected open circuit\n");
info->mic = false;
arizona_stop_mic(info);
info->detecting = false; info->detecting = false;
arizona_identify_headphone(info);
goto handled; goto handled;
} }
/* If we got a high impedence we should have a headset, report it. */ /* If we got a high impedence we should have a headset, report it. */
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
info->mic = true;
info->detecting = false;
arizona_identify_headphone(info); arizona_identify_headphone(info);
ret = extcon_set_cable_state_(info->edev, ret = extcon_set_cable_state_(info->edev,
@ -841,8 +859,6 @@ static void arizona_micd_detect(struct work_struct *work)
ret); ret);
} }
info->mic = true;
info->detecting = false;
goto handled; goto handled;
} }
@ -855,10 +871,11 @@ static void arizona_micd_detect(struct work_struct *work)
if (info->detecting && (val & MICD_LVL_1_TO_7)) { if (info->detecting && (val & MICD_LVL_1_TO_7)) {
if (info->jack_flips >= info->micd_num_modes * 10) { if (info->jack_flips >= info->micd_num_modes * 10) {
dev_dbg(arizona->dev, "Detected HP/line\n"); dev_dbg(arizona->dev, "Detected HP/line\n");
arizona_identify_headphone(info);
info->detecting = false; info->detecting = false;
arizona_identify_headphone(info);
arizona_stop_mic(info); arizona_stop_mic(info);
} else { } else {
info->micd_mode++; info->micd_mode++;
@ -1110,12 +1127,12 @@ static void arizona_micd_set_level(struct arizona *arizona, int index,
regmap_update_bits(arizona->regmap, reg, mask, level); regmap_update_bits(arizona->regmap, reg, mask, level);
} }
static int arizona_extcon_of_get_pdata(struct arizona *arizona) static int arizona_extcon_device_get_pdata(struct arizona *arizona)
{ {
struct arizona_pdata *pdata = &arizona->pdata; struct arizona_pdata *pdata = &arizona->pdata;
unsigned int val = ARIZONA_ACCDET_MODE_HPL; unsigned int val = ARIZONA_ACCDET_MODE_HPL;
of_property_read_u32(arizona->dev->of_node, "wlf,hpdet-channel", &val); device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val);
switch (val) { switch (val) {
case ARIZONA_ACCDET_MODE_HPL: case ARIZONA_ACCDET_MODE_HPL:
case ARIZONA_ACCDET_MODE_HPR: case ARIZONA_ACCDET_MODE_HPR:
@ -1127,6 +1144,24 @@ static int arizona_extcon_of_get_pdata(struct arizona *arizona)
pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL; pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
} }
device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce",
&pdata->micd_detect_debounce);
device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time",
&pdata->micd_bias_start_time);
device_property_read_u32(arizona->dev, "wlf,micd-rate",
&pdata->micd_rate);
device_property_read_u32(arizona->dev, "wlf,micd-dbtime",
&pdata->micd_dbtime);
device_property_read_u32(arizona->dev, "wlf,micd-timeout",
&pdata->micd_timeout);
pdata->micd_force_micbias = device_property_read_bool(arizona->dev,
"wlf,micd-force-micbias");
return 0; return 0;
} }
@ -1147,10 +1182,8 @@ static int arizona_extcon_probe(struct platform_device *pdev)
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev))
if (!dev_get_platdata(arizona->dev)) arizona_extcon_device_get_pdata(arizona);
arizona_extcon_of_get_pdata(arizona);
}
info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD"); info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
if (IS_ERR(info->micvdd)) { if (IS_ERR(info->micvdd)) {
@ -1241,6 +1274,27 @@ static int arizona_extcon_probe(struct platform_device *pdev)
arizona->pdata.micd_pol_gpio, ret); arizona->pdata.micd_pol_gpio, ret);
goto err_register; goto err_register;
} }
} else {
if (info->micd_modes[0].gpio)
mode = GPIOD_OUT_HIGH;
else
mode = GPIOD_OUT_LOW;
/* We can't use devm here because we need to do the get
* against the MFD device, as that is where the of_node
* will reside, but if we devm against that the GPIO
* will not be freed if the extcon driver is unloaded.
*/
info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
"wlf,micd-pol",
GPIOD_OUT_LOW);
if (IS_ERR(info->micd_pol_gpio)) {
ret = PTR_ERR(info->micd_pol_gpio);
dev_err(arizona->dev,
"Failed to get microphone polarity GPIO: %d\n",
ret);
goto err_register;
}
} }
if (arizona->pdata.hpdet_id_gpio > 0) { if (arizona->pdata.hpdet_id_gpio > 0) {
@ -1251,7 +1305,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
if (ret != 0) { if (ret != 0) {
dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
arizona->pdata.hpdet_id_gpio, ret); arizona->pdata.hpdet_id_gpio, ret);
goto err_register; goto err_gpio;
} }
} }
@ -1267,11 +1321,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
arizona->pdata.micd_rate arizona->pdata.micd_rate
<< ARIZONA_MICD_RATE_SHIFT); << ARIZONA_MICD_RATE_SHIFT);
if (arizona->pdata.micd_dbtime) switch (arizona->pdata.micd_dbtime) {
case MICD_DBTIME_FOUR_READINGS:
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_DBTIME_MASK, ARIZONA_MICD_DBTIME_MASK,
arizona->pdata.micd_dbtime ARIZONA_MICD_DBTIME);
<< ARIZONA_MICD_DBTIME_SHIFT); break;
case MICD_DBTIME_TWO_READINGS:
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_DBTIME_MASK, 0);
break;
default:
break;
}
BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40); BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40);
@ -1295,7 +1357,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
dev_err(arizona->dev, dev_err(arizona->dev,
"MICD ranges must be sorted\n"); "MICD ranges must be sorted\n");
ret = -EINVAL; ret = -EINVAL;
goto err_input; goto err_gpio;
} }
} }
} }
@ -1314,7 +1376,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
dev_err(arizona->dev, "Unsupported MICD level %d\n", dev_err(arizona->dev, "Unsupported MICD level %d\n",
info->micd_ranges[i].max); info->micd_ranges[i].max);
ret = -EINVAL; ret = -EINVAL;
goto err_input; goto err_gpio;
} }
dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n", dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
@ -1387,7 +1449,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n", dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
ret); ret);
goto err_input; goto err_gpio;
} }
ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1); ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1);
@ -1458,7 +1520,8 @@ static int arizona_extcon_probe(struct platform_device *pdev)
arizona_set_irq_wake(arizona, jack_irq_rise, 0); arizona_set_irq_wake(arizona, jack_irq_rise, 0);
err_rise: err_rise:
arizona_free_irq(arizona, jack_irq_rise, info); arizona_free_irq(arizona, jack_irq_rise, info);
err_input: err_gpio:
gpiod_put(info->micd_pol_gpio);
err_register: err_register:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return ret; return ret;
@ -1470,6 +1533,8 @@ static int arizona_extcon_remove(struct platform_device *pdev)
struct arizona *arizona = info->arizona; struct arizona *arizona = info->arizona;
int jack_irq_rise, jack_irq_fall; int jack_irq_rise, jack_irq_fall;
gpiod_put(info->micd_pol_gpio);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
regmap_update_bits(arizona->regmap, regmap_update_bits(arizona->regmap,

View File

@ -65,22 +65,6 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
{
struct device *dev = edev->dev.parent;
struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
const char *state;
if (extcon_get_state(edev))
state = extcon_data->state_on;
else
state = extcon_data->state_off;
if (state)
return sprintf(buf, "%s\n", state);
return -EINVAL;
}
static int gpio_extcon_probe(struct platform_device *pdev) static int gpio_extcon_probe(struct platform_device *pdev)
{ {
struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev); struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);
@ -110,8 +94,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
extcon_data->state_on = pdata->state_on; extcon_data->state_on = pdata->state_on;
extcon_data->state_off = pdata->state_off; extcon_data->state_off = pdata->state_off;
extcon_data->check_on_resume = pdata->check_on_resume; extcon_data->check_on_resume = pdata->check_on_resume;
if (pdata->state_on && pdata->state_off)
extcon_data->edev->print_state = extcon_gpio_print_state;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name); pdev->name);

View File

@ -781,6 +781,15 @@ static int max77843_muic_probe(struct platform_device *pdev)
/* Support virtual irq domain for max77843 MUIC device */ /* Support virtual irq domain for max77843 MUIC device */
INIT_WORK(&info->irq_work, max77843_muic_irq_work); INIT_WORK(&info->irq_work, max77843_muic_irq_work);
/* Clear IRQ bits before request IRQs */
ret = regmap_bulk_read(max77843->regmap_muic,
MAX77843_MUIC_REG_INT1, info->status,
MAX77843_MUIC_IRQ_NUM);
if (ret) {
dev_err(&pdev->dev, "Failed to Clear IRQ bits\n");
goto err_muic_irq;
}
for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) { for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) {
struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i]; struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i];
unsigned int virq = 0; unsigned int virq = 0;

View File

@ -28,6 +28,11 @@
#include <linux/mfd/palmas.h> #include <linux/mfd/palmas.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/workqueue.h>
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
static const unsigned int palmas_extcon_cable[] = { static const unsigned int palmas_extcon_cable[] = {
EXTCON_USB, EXTCON_USB,
@ -35,8 +40,6 @@ static const unsigned int palmas_extcon_cable[] = {
EXTCON_NONE, EXTCON_NONE,
}; };
static const int mutually_exclusive[] = {0x3, 0x0};
static void palmas_usb_wakeup(struct palmas *palmas, int enable) static void palmas_usb_wakeup(struct palmas *palmas, int enable)
{ {
if (enable) if (enable)
@ -120,19 +123,54 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void palmas_gpio_id_detect(struct work_struct *work)
{
int id;
struct palmas_usb *palmas_usb = container_of(to_delayed_work(work),
struct palmas_usb,
wq_detectid);
struct extcon_dev *edev = palmas_usb->edev;
if (!palmas_usb->id_gpiod)
return;
id = gpiod_get_value_cansleep(palmas_usb->id_gpiod);
if (id) {
extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else {
extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
}
}
static irqreturn_t palmas_gpio_id_irq_handler(int irq, void *_palmas_usb)
{
struct palmas_usb *palmas_usb = _palmas_usb;
queue_delayed_work(system_power_efficient_wq, &palmas_usb->wq_detectid,
palmas_usb->sw_debounce_jiffies);
return IRQ_HANDLED;
}
static void palmas_enable_irq(struct palmas_usb *palmas_usb) static void palmas_enable_irq(struct palmas_usb *palmas_usb)
{ {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_VBUS_CTRL_SET, PALMAS_USB_VBUS_CTRL_SET,
PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP); PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, if (palmas_usb->enable_id_detection) {
PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_CTRL_SET,
PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET, PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | PALMAS_USB_ID_INT_EN_HI_SET_ID_GND |
PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
}
if (palmas_usb->enable_vbus_detection) if (palmas_usb->enable_vbus_detection)
palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
@ -171,20 +209,37 @@ static int palmas_usb_probe(struct platform_device *pdev)
palmas_usb->wakeup = pdata->wakeup; palmas_usb->wakeup = pdata->wakeup;
} }
palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id",
GPIOD_IN);
if (IS_ERR(palmas_usb->id_gpiod)) {
dev_err(&pdev->dev, "failed to get id gpio\n");
return PTR_ERR(palmas_usb->id_gpiod);
}
if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) {
palmas_usb->enable_id_detection = false;
palmas_usb->enable_gpio_id_detection = true;
}
if (palmas_usb->enable_gpio_id_detection) {
u32 debounce;
if (of_property_read_u32(node, "debounce-delay-ms", &debounce))
debounce = USB_GPIO_DEBOUNCE_MS;
status = gpiod_set_debounce(palmas_usb->id_gpiod,
debounce * 1000);
if (status < 0)
palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
}
INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect);
palmas->usb = palmas_usb; palmas->usb = palmas_usb;
palmas_usb->palmas = palmas; palmas_usb->palmas = palmas;
palmas_usb->dev = &pdev->dev; palmas_usb->dev = &pdev->dev;
palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_ID_OTG_IRQ);
palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_ID_IRQ);
palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_OTG_IRQ);
palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_IRQ);
palmas_usb_wakeup(palmas, palmas_usb->wakeup); palmas_usb_wakeup(palmas, palmas_usb->wakeup);
platform_set_drvdata(pdev, palmas_usb); platform_set_drvdata(pdev, palmas_usb);
@ -195,7 +250,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to allocate extcon device\n"); dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM; return -ENOMEM;
} }
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
if (status) { if (status) {
@ -204,6 +258,10 @@ static int palmas_usb_probe(struct platform_device *pdev)
} }
if (palmas_usb->enable_id_detection) { if (palmas_usb->enable_id_detection) {
palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_ID_OTG_IRQ);
palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_ID_IRQ);
status = devm_request_threaded_irq(palmas_usb->dev, status = devm_request_threaded_irq(palmas_usb->dev,
palmas_usb->id_irq, palmas_usb->id_irq,
NULL, palmas_id_irq_handler, NULL, palmas_id_irq_handler,
@ -215,9 +273,33 @@ static int palmas_usb_probe(struct platform_device *pdev)
palmas_usb->id_irq, status); palmas_usb->id_irq, status);
return status; return status;
} }
} else if (palmas_usb->enable_gpio_id_detection) {
palmas_usb->gpio_id_irq = gpiod_to_irq(palmas_usb->id_gpiod);
if (palmas_usb->gpio_id_irq < 0) {
dev_err(&pdev->dev, "failed to get id irq\n");
return palmas_usb->gpio_id_irq;
}
status = devm_request_threaded_irq(&pdev->dev,
palmas_usb->gpio_id_irq,
NULL,
palmas_gpio_id_irq_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"palmas_usb_id",
palmas_usb);
if (status < 0) {
dev_err(&pdev->dev,
"failed to request handler for id irq\n");
return status;
}
} }
if (palmas_usb->enable_vbus_detection) { if (palmas_usb->enable_vbus_detection) {
palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_OTG_IRQ);
palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_IRQ);
status = devm_request_threaded_irq(palmas_usb->dev, status = devm_request_threaded_irq(palmas_usb->dev,
palmas_usb->vbus_irq, NULL, palmas_usb->vbus_irq, NULL,
palmas_vbus_irq_handler, palmas_vbus_irq_handler,
@ -232,10 +314,21 @@ static int palmas_usb_probe(struct platform_device *pdev)
} }
palmas_enable_irq(palmas_usb); palmas_enable_irq(palmas_usb);
/* perform initial detection */
palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
device_set_wakeup_capable(&pdev->dev, true); device_set_wakeup_capable(&pdev->dev, true);
return 0; return 0;
} }
static int palmas_usb_remove(struct platform_device *pdev)
{
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&palmas_usb->wq_detectid);
return 0;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int palmas_usb_suspend(struct device *dev) static int palmas_usb_suspend(struct device *dev)
{ {
@ -246,6 +339,8 @@ static int palmas_usb_suspend(struct device *dev)
enable_irq_wake(palmas_usb->vbus_irq); enable_irq_wake(palmas_usb->vbus_irq);
if (palmas_usb->enable_id_detection) if (palmas_usb->enable_id_detection)
enable_irq_wake(palmas_usb->id_irq); enable_irq_wake(palmas_usb->id_irq);
if (palmas_usb->enable_gpio_id_detection)
enable_irq_wake(palmas_usb->gpio_id_irq);
} }
return 0; return 0;
} }
@ -259,6 +354,8 @@ static int palmas_usb_resume(struct device *dev)
disable_irq_wake(palmas_usb->vbus_irq); disable_irq_wake(palmas_usb->vbus_irq);
if (palmas_usb->enable_id_detection) if (palmas_usb->enable_id_detection)
disable_irq_wake(palmas_usb->id_irq); disable_irq_wake(palmas_usb->id_irq);
if (palmas_usb->enable_gpio_id_detection)
disable_irq_wake(palmas_usb->gpio_id_irq);
} }
return 0; return 0;
}; };
@ -276,6 +373,7 @@ static const struct of_device_id of_palmas_match_tbl[] = {
static struct platform_driver palmas_usb_driver = { static struct platform_driver palmas_usb_driver = {
.probe = palmas_usb_probe, .probe = palmas_usb_probe,
.remove = palmas_usb_remove,
.driver = { .driver = {
.name = "palmas-usb", .name = "palmas-usb",
.of_match_table = of_palmas_match_tbl, .of_match_table = of_palmas_match_tbl,

View File

@ -693,7 +693,6 @@ MODULE_DEVICE_TABLE(i2c, rt8973a_i2c_id);
static struct i2c_driver rt8973a_muic_i2c_driver = { static struct i2c_driver rt8973a_muic_i2c_driver = {
.driver = { .driver = {
.name = "rt8973a", .name = "rt8973a",
.owner = THIS_MODULE,
.pm = &rt8973a_muic_pm_ops, .pm = &rt8973a_muic_pm_ops,
.of_match_table = rt8973a_dt_match, .of_match_table = rt8973a_dt_match,
}, },

View File

@ -685,7 +685,6 @@ MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);
static struct i2c_driver sm5502_muic_i2c_driver = { static struct i2c_driver sm5502_muic_i2c_driver = {
.driver = { .driver = {
.name = "sm5502", .name = "sm5502",
.owner = THIS_MODULE,
.pm = &sm5502_muic_pm_ops, .pm = &sm5502_muic_pm_ops,
.of_match_table = sm5502_dt_match, .of_match_table = sm5502_dt_match,
}, },

View File

@ -15,6 +15,7 @@
*/ */
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>

View File

@ -126,7 +126,7 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id
static int find_cable_id_by_name(struct extcon_dev *edev, const char *name) static int find_cable_id_by_name(struct extcon_dev *edev, const char *name)
{ {
unsigned int id = -EINVAL; int id = -EINVAL;
int i = 0; int i = 0;
/* Find the id of extcon cable */ /* Find the id of extcon cable */
@ -143,7 +143,7 @@ static int find_cable_id_by_name(struct extcon_dev *edev, const char *name)
static int find_cable_index_by_name(struct extcon_dev *edev, const char *name) static int find_cable_index_by_name(struct extcon_dev *edev, const char *name)
{ {
unsigned int id; int id;
if (edev->max_supported == 0) if (edev->max_supported == 0)
return -EINVAL; return -EINVAL;
@ -172,14 +172,6 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
int i, count = 0; int i, count = 0;
struct extcon_dev *edev = dev_get_drvdata(dev); struct extcon_dev *edev = dev_get_drvdata(dev);
if (edev->print_state) {
int ret = edev->print_state(edev, buf);
if (ret >= 0)
return ret;
/* Use default if failed */
}
if (edev->max_supported == 0) if (edev->max_supported == 0)
return sprintf(buf, "%u\n", edev->state); return sprintf(buf, "%u\n", edev->state);
@ -272,6 +264,9 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
unsigned long flags; unsigned long flags;
bool attached; bool attached;
if (!edev)
return -EINVAL;
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
if (edev->state != ((edev->state & ~mask) | (state & mask))) { if (edev->state != ((edev->state & ~mask) | (state & mask))) {
@ -345,6 +340,9 @@ EXPORT_SYMBOL_GPL(extcon_update_state);
*/ */
int extcon_set_state(struct extcon_dev *edev, u32 state) int extcon_set_state(struct extcon_dev *edev, u32 state)
{ {
if (!edev)
return -EINVAL;
return extcon_update_state(edev, 0xffffffff, state); return extcon_update_state(edev, 0xffffffff, state);
} }
EXPORT_SYMBOL_GPL(extcon_set_state); EXPORT_SYMBOL_GPL(extcon_set_state);
@ -358,6 +356,9 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
{ {
int index; int index;
if (!edev)
return -EINVAL;
index = find_cable_index_by_id(edev, id); index = find_cable_index_by_id(edev, id);
if (index < 0) if (index < 0)
return index; return index;
@ -378,7 +379,7 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
*/ */
int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name) int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
{ {
unsigned int id; int id;
id = find_cable_id_by_name(edev, cable_name); id = find_cable_id_by_name(edev, cable_name);
if (id < 0) if (id < 0)
@ -402,6 +403,9 @@ int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
u32 state; u32 state;
int index; int index;
if (!edev)
return -EINVAL;
index = find_cable_index_by_id(edev, id); index = find_cable_index_by_id(edev, id);
if (index < 0) if (index < 0)
return index; return index;
@ -426,7 +430,7 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
int extcon_set_cable_state(struct extcon_dev *edev, int extcon_set_cable_state(struct extcon_dev *edev,
const char *cable_name, bool cable_state) const char *cable_name, bool cable_state)
{ {
unsigned int id; int id;
id = find_cable_id_by_name(edev, cable_name); id = find_cable_id_by_name(edev, cable_name);
if (id < 0) if (id < 0)
@ -444,6 +448,9 @@ struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
{ {
struct extcon_dev *sd; struct extcon_dev *sd;
if (!extcon_name)
return ERR_PTR(-EINVAL);
mutex_lock(&extcon_dev_list_lock); mutex_lock(&extcon_dev_list_lock);
list_for_each_entry(sd, &extcon_dev_list, entry) { list_for_each_entry(sd, &extcon_dev_list, entry) {
if (!strcmp(sd->name, extcon_name)) if (!strcmp(sd->name, extcon_name))
@ -572,6 +579,9 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
unsigned long flags; unsigned long flags;
int ret, idx; int ret, idx;
if (!edev || !nb)
return -EINVAL;
idx = find_cable_index_by_id(edev, id); idx = find_cable_index_by_id(edev, id);
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
@ -594,6 +604,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
unsigned long flags; unsigned long flags;
int ret, idx; int ret, idx;
if (!edev || !nb)
return -EINVAL;
idx = find_cable_index_by_id(edev, id); idx = find_cable_index_by_id(edev, id);
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
@ -654,6 +667,9 @@ struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
{ {
struct extcon_dev *edev; struct extcon_dev *edev;
if (!supported_cable)
return ERR_PTR(-EINVAL);
edev = kzalloc(sizeof(*edev), GFP_KERNEL); edev = kzalloc(sizeof(*edev), GFP_KERNEL);
if (!edev) if (!edev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -754,7 +770,7 @@ int extcon_dev_register(struct extcon_dev *edev)
return ret; return ret;
} }
if (!edev->supported_cable) if (!edev || !edev->supported_cable)
return -EINVAL; return -EINVAL;
for (; edev->supported_cable[index] != EXTCON_NONE; index++); for (; edev->supported_cable[index] != EXTCON_NONE; index++);
@ -960,6 +976,9 @@ void extcon_dev_unregister(struct extcon_dev *edev)
{ {
int index; int index;
if (!edev)
return;
mutex_lock(&extcon_dev_list_lock); mutex_lock(&extcon_dev_list_lock);
list_del(&edev->entry); list_del(&edev->entry);
mutex_unlock(&extcon_dev_list_lock); mutex_unlock(&extcon_dev_list_lock);
@ -1066,6 +1085,9 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
struct device_node *node; struct device_node *node;
struct extcon_dev *edev; struct extcon_dev *edev;
if (!dev)
return ERR_PTR(-EINVAL);
if (!dev->of_node) { if (!dev->of_node) {
dev_err(dev, "device does not have a device node entry\n"); dev_err(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);

View File

@ -27,8 +27,6 @@
#define __LINUX_EXTCON_H__ #define __LINUX_EXTCON_H__
#include <linux/device.h> #include <linux/device.h>
#include <linux/notifier.h>
#include <linux/sysfs.h>
/* /*
* Define the unique id of supported external connectors * Define the unique id of supported external connectors
@ -77,8 +75,6 @@ struct extcon_cable;
* be attached simulataneously. {0x7, 0} is equivalent to * be attached simulataneously. {0x7, 0} is equivalent to
* {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
* can be no simultaneous connections. * can be no simultaneous connections.
* @print_state: An optional callback to override the method to print the
* status of the extcon device.
* @dev: Device of this extcon. * @dev: Device of this extcon.
* @state: Attach/detach state of this extcon. Do not provide at * @state: Attach/detach state of this extcon. Do not provide at
* register-time. * register-time.
@ -102,9 +98,6 @@ struct extcon_dev {
const unsigned int *supported_cable; const unsigned int *supported_cable;
const u32 *mutually_exclusive; const u32 *mutually_exclusive;
/* Optional callbacks to override class functions */
ssize_t (*print_state)(struct extcon_dev *edev, char *buf);
/* Internal data. Please do not set. */ /* Internal data. Please do not set. */
struct device dev; struct device dev;
struct raw_notifier_head *nh; struct raw_notifier_head *nh;

View File

@ -21,6 +21,7 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/of_gpio.h>
#include <linux/usb/phy_companion.h> #include <linux/usb/phy_companion.h>
#define PALMAS_NUM_CLIENTS 3 #define PALMAS_NUM_CLIENTS 3
@ -551,10 +552,16 @@ struct palmas_usb {
int vbus_otg_irq; int vbus_otg_irq;
int vbus_irq; int vbus_irq;
int gpio_id_irq;
struct gpio_desc *id_gpiod;
unsigned long sw_debounce_jiffies;
struct delayed_work wq_detectid;
enum palmas_usb_state linkstat; enum palmas_usb_state linkstat;
int wakeup; int wakeup;
bool enable_vbus_detection; bool enable_vbus_detection;
bool enable_id_detection; bool enable_id_detection;
bool enable_gpio_id_detection;
}; };
#define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator) #define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator)