diff options
Diffstat (limited to 'drivers/extcon')
| -rw-r--r-- | drivers/extcon/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/extcon/Makefile | 3 | ||||
| -rw-r--r-- | drivers/extcon/extcon-adc-jack.c | 70 | ||||
| -rw-r--r-- | drivers/extcon/extcon-arizona.c | 178 | ||||
| -rw-r--r-- | drivers/extcon/extcon-class.c | 296 | ||||
| -rw-r--r-- | drivers/extcon/extcon-gpio.c | 78 | ||||
| -rw-r--r-- | drivers/extcon/extcon-max14577.c | 823 | ||||
| -rw-r--r-- | drivers/extcon/extcon-max77693.c | 157 | ||||
| -rw-r--r-- | drivers/extcon/extcon-max8997.c | 25 | ||||
| -rw-r--r-- | drivers/extcon/extcon-palmas.c | 62 | ||||
| -rw-r--r-- | drivers/extcon/of_extcon.c | 64 | 
11 files changed, 1399 insertions, 371 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index f1d54a3985b..aebde489c29 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -14,10 +14,6 @@ if EXTCON  comment "Extcon Device Drivers" -config OF_EXTCON -	def_tristate y -	depends on OF -  config EXTCON_GPIO  	tristate "GPIO extcon support"  	depends on GPIOLIB @@ -31,6 +27,16 @@ config EXTCON_ADC_JACK  	help  	  Say Y here to enable extcon device driver based on ADC values. +config EXTCON_MAX14577 +	tristate "MAX14577/77836 EXTCON Support" +	depends on MFD_MAX14577 +	select IRQ_DOMAIN +	select REGMAP_I2C +	help +	  If you say yes here you get support for the MUIC device of +	  Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory +	  detector and switch. +  config EXTCON_MAX77693  	tristate "MAX77693 EXTCON Support"  	depends on MFD_MAX77693 && INPUT diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 759fdae46f9..bf7861ec090 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -2,11 +2,10 @@  # Makefile for external connector class (extcon) devices  # -obj-$(CONFIG_OF_EXTCON)		+= of_extcon.o -  obj-$(CONFIG_EXTCON)		+= extcon-class.o  obj-$(CONFIG_EXTCON_GPIO)	+= extcon-gpio.o  obj-$(CONFIG_EXTCON_ADC_JACK)	+= extcon-adc-jack.o +obj-$(CONFIG_EXTCON_MAX14577)	+= extcon-max14577.o  obj-$(CONFIG_EXTCON_MAX77693)	+= extcon-max77693.o  obj-$(CONFIG_EXTCON_MAX8997)	+= extcon-max8997.o  obj-$(CONFIG_EXTCON_ARIZONA)	+= extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 5985807e52c..e18f95be373 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -27,19 +27,19 @@  /**   * struct adc_jack_data - internal data for adc_jack device driver - * @edev        - extcon device. - * @cable_names - list of supported cables. - * @num_cables  - size of cable_names. - * @adc_conditions       - list of adc value conditions. - * @num_conditions       - size of adc_conditions. - * @irq         - irq number of attach/detach event (0 if not exist). - * @handling_delay      - interrupt handler will schedule extcon event - *                      handling at handling_delay jiffies. - * @handler     - extcon event handler called by interrupt handler. - * @chan       - iio channel being queried. + * @edev:		extcon device. + * @cable_names:	list of supported cables. + * @num_cables:		size of cable_names. + * @adc_conditions:	list of adc value conditions. + * @num_conditions:	size of adc_conditions. + * @irq:		irq number of attach/detach event (0 if not exist). + * @handling_delay:	interrupt handler will schedule extcon event + *			handling at handling_delay jiffies. + * @handler:		extcon event handler called by interrupt handler. + * @chan:		iio channel being queried.   */  struct adc_jack_data { -	struct extcon_dev edev; +	struct extcon_dev *edev;  	const char **cable_names;  	int num_cables; @@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)  	ret = iio_read_channel_raw(data->chan, &adc_val);  	if (ret < 0) { -		dev_err(data->edev.dev, "read channel() error: %d\n", ret); +		dev_err(&data->edev->dev, "read channel() error: %d\n", ret);  		return;  	} @@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work)  	}  	/* if no def has met, it means state = 0 (no cables attached) */ -	extcon_set_state(&data->edev, state); +	extcon_set_state(data->edev, state);  }  static irqreturn_t adc_jack_irq_thread(int irq, void *_data) @@ -95,39 +95,40 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data)  static int adc_jack_probe(struct platform_device *pdev)  {  	struct adc_jack_data *data; -	struct adc_jack_pdata *pdata = pdev->dev.platform_data; +	struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev);  	int i, err = 0;  	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);  	if (!data)  		return -ENOMEM; -	data->edev.name = pdata->name; -  	if (!pdata->cable_names) { -		err = -EINVAL;  		dev_err(&pdev->dev, "error: cable_names not defined.\n"); -		goto out; +		return -EINVAL;  	} -	data->edev.supported_cable = pdata->cable_names; +	data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names); +	if (IS_ERR(data->edev)) { +		dev_err(&pdev->dev, "failed to allocate extcon device\n"); +		return -ENOMEM; +	} +	data->edev->dev.parent = &pdev->dev; +	data->edev->name = pdata->name;  	/* Check the length of array and set num_cables */ -	for (i = 0; data->edev.supported_cable[i]; i++) +	for (i = 0; data->edev->supported_cable[i]; i++)  		;  	if (i == 0 || i > SUPPORTED_CABLE_MAX) { -		err = -EINVAL;  		dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",  				i - 1); -		goto out; +		return -EINVAL;  	}  	data->num_cables = i;  	if (!pdata->adc_conditions ||  			!pdata->adc_conditions[0].state) { -		err = -EINVAL;  		dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); -		goto out; +		return -EINVAL;  	}  	data->adc_conditions = pdata->adc_conditions; @@ -137,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)  	data->num_conditions = i;  	data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); -	if (IS_ERR(data->chan)) { -		err = PTR_ERR(data->chan); -		goto out; -	} +	if (IS_ERR(data->chan)) +		return PTR_ERR(data->chan);  	data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); @@ -148,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, data); -	err = extcon_dev_register(&data->edev, &pdev->dev); +	err = devm_extcon_dev_register(&pdev->dev, data->edev);  	if (err) -		goto out; +		return err;  	data->irq = platform_get_irq(pdev, 0);  	if (!data->irq) {  		dev_err(&pdev->dev, "platform_get_irq failed\n"); -		err = -ENODEV; -		goto err_irq; +		return -ENODEV;  	}  	err = request_any_context_irq(data->irq, adc_jack_irq_thread, @@ -164,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev)  	if (err < 0) {  		dev_err(&pdev->dev, "error: irq %d\n", data->irq); -		goto err_irq; +		return err;  	}  	return 0; - -err_irq: -	extcon_dev_unregister(&data->edev); -out: -	return err;  }  static int adc_jack_remove(struct platform_device *pdev) @@ -181,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev)  	free_irq(data->irq, data);  	cancel_work_sync(&data->handler.work); -	extcon_dev_unregister(&data->edev);  	return 0;  } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index e55713083c7..6c84e3d1204 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -44,6 +44,15 @@  #define HPDET_DEBOUNCE 500  #define DEFAULT_MICD_TIMEOUT 2000 +#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_5 | ARIZONA_MICD_LVL_6 | \ +			 ARIZONA_MICD_LVL_7) + +#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7) + +#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8) +  struct arizona_extcon_info {  	struct device *dev;  	struct arizona *arizona; @@ -82,12 +91,12 @@ struct arizona_extcon_info {  	int hpdet_ip; -	struct extcon_dev edev; +	struct extcon_dev *edev;  };  static const struct arizona_micd_config micd_default_modes[] = { -	{ ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 }, -	{ 0,                  2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, +	{ ARIZONA_ACCDET_SRC, 1, 0 }, +	{ 0,                  2, 1 },  };  static const struct arizona_micd_range micd_default_ranges[] = { @@ -182,7 +191,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)  					info->micd_modes[mode].gpio);  	regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,  			   ARIZONA_MICD_BIAS_SRC_MASK, -			   info->micd_modes[mode].bias); +			   info->micd_modes[mode].bias << +			   ARIZONA_MICD_BIAS_SRC_SHIFT);  	regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,  			   ARIZONA_ACCDET_SRC, info->micd_modes[mode].src); @@ -193,7 +203,7 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)  static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)  { -	switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) { +	switch (info->micd_modes[0].bias) {  	case 1:  		return "MICBIAS1";  	case 2: @@ -212,27 +222,19 @@ static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info)  	struct snd_soc_dapm_context *dapm = arizona->dapm;  	int ret; -	mutex_lock(&dapm->card->dapm_mutex); -  	ret = snd_soc_dapm_force_enable_pin(dapm, widget);  	if (ret != 0)  		dev_warn(arizona->dev, "Failed to enable %s: %d\n",  			 widget, ret); -	mutex_unlock(&dapm->card->dapm_mutex); -  	snd_soc_dapm_sync(dapm);  	if (!arizona->pdata.micd_force_micbias) { -		mutex_lock(&dapm->card->dapm_mutex); -  		ret = snd_soc_dapm_disable_pin(arizona->dapm, widget);  		if (ret != 0)  			dev_warn(arizona->dev, "Failed to disable %s: %d\n",  				 widget, ret); -		mutex_unlock(&dapm->card->dapm_mutex); -  		snd_soc_dapm_sync(dapm);  	}  } @@ -294,16 +296,12 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)  				 ARIZONA_MICD_ENA, 0,  				 &change); -	mutex_lock(&dapm->card->dapm_mutex); -  	ret = snd_soc_dapm_disable_pin(dapm, widget);  	if (ret != 0)  		dev_warn(arizona->dev,  			 "Failed to disable %s: %d\n",  			 widget, ret); -	mutex_unlock(&dapm->card->dapm_mutex); -  	snd_soc_dapm_sync(dapm);  	if (info->micd_reva) { @@ -388,7 +386,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)  			   >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;  		if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && -		    (val < 100 || val > 0x3fb)) { +		    (val < 100 || val >= 0x3fb)) {  			range++;  			dev_dbg(arizona->dev, "Moving to HPDET range %d\n",  				range); @@ -401,7 +399,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)  		}  		/* If we go out of range report top of range */ -		if (val < 100 || val > 0x3fb) { +		if (val < 100 || val >= 0x3fb) {  			dev_dbg(arizona->dev, "Measurement out of range\n");  			return ARIZONA_HPDET_MAX;  		} @@ -425,26 +423,15 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)  		}  		val &= ARIZONA_HP_LVL_B_MASK; +		/* Convert to ohms, the value is in 0.5 ohm increments */ +		val /= 2;  		regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,  			    &range);  		range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)  			   >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; -		/* Skip up or down a range? */ -		if (range && (val < arizona_hpdet_c_ranges[range].min)) { -			range--; -			dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", -				arizona_hpdet_c_ranges[range].min, -				arizona_hpdet_c_ranges[range].max); -			regmap_update_bits(arizona->regmap, -					   ARIZONA_HEADPHONE_DETECT_1, -					   ARIZONA_HP_IMPEDANCE_RANGE_MASK, -					   range << -					   ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); -			return -EAGAIN; -		} - +		/* Skip up a range, or report? */  		if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&  		    (val >= arizona_hpdet_c_ranges[range].max)) {  			range++; @@ -458,6 +445,12 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)  					   ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);  			return -EAGAIN;  		} + +		if (range && (val < arizona_hpdet_c_ranges[range].min)) { +			dev_dbg(arizona->dev, "Reporting range boundary %d\n", +				arizona_hpdet_c_ranges[range].min); +			val = arizona_hpdet_c_ranges[range].min; +		}  	}  	dev_dbg(arizona->dev, "HP impedance %d ohms\n", val); @@ -514,7 +507,7 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,  		}  		/* -		 * If we measure the mic as  +		 * If we measure the mic as high impedance  		 */  		if (!id_gpio || info->hpdet_res[1] > 50) {  			dev_dbg(arizona->dev, "Detected mic\n"); @@ -553,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)  	}  	/* If the cable was removed while measuring ignore the result */ -	ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); +	ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);  	if (ret < 0) {  		dev_err(arizona->dev, "Failed to check cable state: %d\n",  			ret); @@ -564,11 +557,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)  	}  	ret = arizona_hpdet_read(info); -	if (ret == -EAGAIN) { +	if (ret == -EAGAIN)  		goto out; -	} else if (ret < 0) { +	else if (ret < 0)  		goto done; -	}  	reading = ret;  	/* Reset back to starting range */ @@ -578,11 +570,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)  			   0);  	ret = arizona_hpdet_do_id(info, &reading, &mic); -	if (ret == -EAGAIN) { +	if (ret == -EAGAIN)  		goto out; -	} else if (ret < 0) { +	else if (ret < 0)  		goto done; -	}  	/* Report high impedence cables as line outputs */  	if (reading >= 5000) @@ -590,14 +581,20 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)  	else  		report = ARIZONA_CABLE_HEADPHONE; -	ret = extcon_set_cable_state_(&info->edev, report, true); +	ret = extcon_set_cable_state_(info->edev, report, true);  	if (ret != 0)  		dev_err(arizona->dev, "Failed to report HP/line: %d\n",  			ret); +done: +	/* Reset back to starting range */ +	regmap_update_bits(arizona->regmap, +			   ARIZONA_HEADPHONE_DETECT_1, +			   ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, +			   0); +  	arizona_extcon_do_magic(info, 0); -done:  	if (id_gpio)  		gpio_set_value_cansleep(id_gpio, 0); @@ -667,7 +664,7 @@ err:  			   ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);  	/* Just report headphone */ -	ret = extcon_update_state(&info->edev, +	ret = extcon_update_state(info->edev,  				  1 << ARIZONA_CABLE_HEADPHONE,  				  1 << ARIZONA_CABLE_HEADPHONE);  	if (ret != 0) @@ -726,7 +723,7 @@ err:  			   ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);  	/* Just report headphone */ -	ret = extcon_update_state(&info->edev, +	ret = extcon_update_state(info->edev,  				  1 << ARIZONA_CABLE_HEADPHONE,  				  1 << ARIZONA_CABLE_HEADPHONE);  	if (ret != 0) @@ -738,8 +735,8 @@ err:  static void arizona_micd_timeout_work(struct work_struct *work)  {  	struct arizona_extcon_info *info = container_of(work, -							struct arizona_extcon_info, -							micd_timeout_work.work); +						struct arizona_extcon_info, +						micd_timeout_work.work);  	mutex_lock(&info->lock); @@ -756,8 +753,8 @@ static void arizona_micd_timeout_work(struct work_struct *work)  static void arizona_micd_detect(struct work_struct *work)  {  	struct arizona_extcon_info *info = container_of(work, -							struct arizona_extcon_info, -							micd_detect_work.work); +						struct arizona_extcon_info, +						micd_detect_work.work);  	struct arizona *arizona = info->arizona;  	unsigned int val = 0, lvl;  	int ret, i, key; @@ -766,10 +763,24 @@ static void arizona_micd_detect(struct work_struct *work)  	mutex_lock(&info->lock); -	for (i = 0; i < 10 && !(val & 0x7fc); i++) { +	/* If the cable was removed while measuring ignore the result */ +	ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL); +	if (ret < 0) { +		dev_err(arizona->dev, "Failed to check cable state: %d\n", +				ret); +		mutex_unlock(&info->lock); +		return; +	} else if (!ret) { +		dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); +		mutex_unlock(&info->lock); +		return; +	} + +	for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {  		ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);  		if (ret != 0) { -			dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret); +			dev_err(arizona->dev, +				"Failed to read MICDET: %d\n", ret);  			mutex_unlock(&info->lock);  			return;  		} @@ -777,13 +788,14 @@ static void arizona_micd_detect(struct work_struct *work)  		dev_dbg(arizona->dev, "MICDET: %x\n", val);  		if (!(val & ARIZONA_MICD_VALID)) { -			dev_warn(arizona->dev, "Microphone detection state invalid\n"); +			dev_warn(arizona->dev, +				 "Microphone detection state invalid\n");  			mutex_unlock(&info->lock);  			return;  		}  	} -	if (i == 10 && !(val & 0x7fc)) { +	if (i == 10 && !(val & MICD_LVL_0_TO_8)) {  		dev_err(arizona->dev, "Failed to get valid MICDET value\n");  		mutex_unlock(&info->lock);  		return; @@ -797,10 +809,10 @@ static void arizona_micd_detect(struct work_struct *work)  	}  	/* If we got a high impedence we should have a headset, report it. */ -	if (info->detecting && (val & 0x400)) { +	if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {  		arizona_identify_headphone(info); -		ret = extcon_update_state(&info->edev, +		ret = extcon_update_state(info->edev,  					  1 << ARIZONA_CABLE_MICROPHONE,  					  1 << ARIZONA_CABLE_MICROPHONE); @@ -826,7 +838,7 @@ static void arizona_micd_detect(struct work_struct *work)  	 * plain headphones.  If both polarities report a low  	 * impedence then give up and report headphones.  	 */ -	if (info->detecting && (val & 0x3f8)) { +	if (info->detecting && (val & MICD_LVL_1_TO_7)) {  		if (info->jack_flips >= info->micd_num_modes * 10) {  			dev_dbg(arizona->dev, "Detected HP/line\n");  			arizona_identify_headphone(info); @@ -850,7 +862,7 @@ static void arizona_micd_detect(struct work_struct *work)  	 * If we're still detecting and we detect a short then we've  	 * got a headphone.  Otherwise it's a button press.  	 */ -	if (val & 0x3fc) { +	if (val & MICD_LVL_0_TO_7) {  		if (info->mic) {  			dev_dbg(arizona->dev, "Mic button detected\n"); @@ -925,8 +937,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)  static void arizona_hpdet_work(struct work_struct *work)  {  	struct arizona_extcon_info *info = container_of(work, -							struct arizona_extcon_info, -							hpdet_work.work); +						struct arizona_extcon_info, +						hpdet_work.work);  	mutex_lock(&info->lock);  	arizona_start_hpdet_acc_id(info); @@ -973,10 +985,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)  					   &info->hpdet_work,  					   msecs_to_jiffies(HPDET_DEBOUNCE)); -		if (cancelled_mic) +		if (cancelled_mic) { +			int micd_timeout = info->micd_timeout; +  			queue_delayed_work(system_power_efficient_wq,  					   &info->micd_timeout_work, -					   msecs_to_jiffies(info->micd_timeout)); +					   msecs_to_jiffies(micd_timeout)); +		}  		goto out;  	} @@ -984,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)  	if (info->last_jackdet == present) {  		dev_dbg(arizona->dev, "Detected jack\n"); -		ret = extcon_set_cable_state_(&info->edev, +		ret = extcon_set_cable_state_(info->edev,  					      ARIZONA_CABLE_MECHANICAL, true);  		if (ret != 0) @@ -1023,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)  					 info->micd_ranges[i].key, 0);  		input_sync(info->input); -		ret = extcon_update_state(&info->edev, 0xffffffff, 0); +		ret = extcon_update_state(info->edev, 0xffffffff, 0);  		if (ret != 0)  			dev_err(arizona->dev, "Removal report failed: %d\n",  				ret); @@ -1039,6 +1054,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)  	else  		info->micd_timeout = DEFAULT_MICD_TIMEOUT; +out:  	/* Clear trig_sts to make sure DCVDD is not forced up */  	regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,  		     ARIZONA_MICD_CLAMP_FALL_TRIG_STS | @@ -1046,7 +1062,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data)  		     ARIZONA_JD1_FALL_TRIG_STS |  		     ARIZONA_JD1_RISE_TRIG_STS); -out:  	mutex_unlock(&info->lock);  	pm_runtime_mark_last_busy(info->dev); @@ -1078,7 +1093,7 @@ static void arizona_micd_set_level(struct arizona *arizona, int index,  static int arizona_extcon_probe(struct platform_device *pdev)  {  	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); -	struct arizona_pdata *pdata; +	struct arizona_pdata *pdata = &arizona->pdata;  	struct arizona_extcon_info *info;  	unsigned int val;  	int jack_irq_fall, jack_irq_rise; @@ -1087,20 +1102,17 @@ static int arizona_extcon_probe(struct platform_device *pdev)  	if (!arizona->dapm || !arizona->dapm->card)  		return -EPROBE_DEFER; -	pdata = dev_get_platdata(arizona->dev); -  	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);  	if (!info) {  		dev_err(&pdev->dev, "Failed to allocate memory\n"); -		ret = -ENOMEM; -		goto err; +		return -ENOMEM;  	}  	info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");  	if (IS_ERR(info->micvdd)) {  		ret = PTR_ERR(info->micvdd);  		dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret); -		goto err; +		return ret;  	}  	mutex_init(&info->lock); @@ -1124,18 +1136,33 @@ static int arizona_extcon_probe(struct platform_device *pdev)  			break;  		}  		break; +	case WM5110: +		switch (arizona->rev) { +		case 0 ... 2: +			break; +		default: +			info->micd_clamp = true; +			info->hpdet_ip = 2; +			break; +		} +		break;  	default:  		break;  	} -	info->edev.name = "Headset Jack"; -	info->edev.supported_cable = arizona_cable; +	info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable); +	if (IS_ERR(info->edev)) { +		dev_err(&pdev->dev, "failed to allocate extcon device\n"); +		return -ENOMEM; +	} +	info->edev->name = "Headset Jack"; +	info->edev->dev.parent = arizona->dev; -	ret = extcon_dev_register(&info->edev, arizona->dev); +	ret = devm_extcon_dev_register(&pdev->dev, info->edev);  	if (ret < 0) {  		dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",  			ret); -		goto err; +		return ret;  	}  	info->input = devm_input_allocate_device(&pdev->dev); @@ -1386,8 +1413,6 @@ err_rise:  err_input:  err_register:  	pm_runtime_disable(&pdev->dev); -	extcon_dev_unregister(&info->edev); -err:  	return ret;  } @@ -1421,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)  	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,  			   ARIZONA_JD1_ENA, 0);  	arizona_clk32k_disable(arizona); -	extcon_dev_unregister(&info->edev);  	return 0;  } diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 148382faded..18d42c0e458 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -31,6 +31,7 @@  #include <linux/extcon.h>  #include <linux/slab.h>  #include <linux/sysfs.h> +#include <linux/of.h>  /*   * extcon_cable_name suggests the standard cable names for commonly used @@ -74,7 +75,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);  /**   * check_mutually_exclusive - Check if new_state violates mutually_exclusive - *			    condition. + *			      condition.   * @edev:	the extcon device   * @new_state:	new cable attach status for @edev   * @@ -105,7 +106,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,  			  char *buf)  {  	int i, count = 0; -	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); +	struct extcon_dev *edev = dev_get_drvdata(dev);  	if (edev->print_state) {  		int ret = edev->print_state(edev, buf); @@ -129,13 +130,12 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,  	return count;  } -int extcon_set_state(struct extcon_dev *edev, u32 state);  static ssize_t state_store(struct device *dev, struct device_attribute *attr,  			   const char *buf, size_t count)  {  	u32 state;  	ssize_t ret = 0; -	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); +	struct extcon_dev *edev = dev_get_drvdata(dev);  	ret = sscanf(buf, "0x%x", &state);  	if (ret == 0) @@ -153,7 +153,7 @@ static DEVICE_ATTR_RW(state);  static ssize_t name_show(struct device *dev, struct device_attribute *attr,  		char *buf)  { -	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); +	struct extcon_dev *edev = dev_get_drvdata(dev);  	/* Optional callback given by the user */  	if (edev->print_name) { @@ -162,7 +162,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,  			return ret;  	} -	return sprintf(buf, "%s\n", dev_name(edev->dev)); +	return sprintf(buf, "%s\n", dev_name(&edev->dev));  }  static DEVICE_ATTR_RO(name); @@ -189,7 +189,7 @@ static ssize_t cable_state_show(struct device *dev,  /**   * extcon_update_state() - Update the cable attach states of the extcon device - *			only for the masked bits. + *			   only for the masked bits.   * @edev:	the extcon device   * @mask:	the bit mask to designate updated bits.   * @state:	new cable attach status for @edev @@ -227,11 +227,10 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)  		edev->state |= state & mask;  		raw_notifier_call_chain(&edev->nh, old_state, edev); -  		/* This could be in interrupt handler */  		prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);  		if (prop_buf) { -			length = name_show(edev->dev, NULL, prop_buf); +			length = name_show(&edev->dev, NULL, prop_buf);  			if (length > 0) {  				if (prop_buf[length - 1] == '\n')  					prop_buf[length - 1] = 0; @@ -239,7 +238,7 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)  					"NAME=%s", prop_buf);  				envp[env_offset++] = name_buf;  			} -			length = state_show(edev->dev, NULL, prop_buf); +			length = state_show(&edev->dev, NULL, prop_buf);  			if (length > 0) {  				if (prop_buf[length - 1] == '\n')  					prop_buf[length - 1] = 0; @@ -251,14 +250,14 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)  			/* Unlock early before uevent */  			spin_unlock_irqrestore(&edev->lock, flags); -			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp); +			kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);  			free_page((unsigned long)prop_buf);  		} else {  			/* Unlock early before uevent */  			spin_unlock_irqrestore(&edev->lock, flags); -			dev_err(edev->dev, "out of memory in extcon_set_state\n"); -			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE); +			dev_err(&edev->dev, "out of memory in extcon_set_state\n"); +			kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);  		}  	} else {  		/* No changes */ @@ -339,8 +338,9 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state);  /**   * extcon_set_cable_state_() - Set the status of a specific cable. - * @edev:	the extcon device that has the cable. - * @index:	cable index that can be retrieved by extcon_find_cable_index(). + * @edev:		the extcon device that has the cable. + * @index:		cable index that can be retrieved by + *			extcon_find_cable_index().   * @cable_state:	the new cable status. The default semantics is   *			true: attached / false: detached.   */ @@ -359,8 +359,8 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_);  /**   * extcon_set_cable_state() - Set the status of a specific cable. - * @edev:	the extcon device that has the cable. - * @cable_name:	cable name. + * @edev:		the extcon device that has the cable. + * @cable_name:		cable name.   * @cable_state:	the new cable status. The default semantics is   *			true: attached / false: detached.   * @@ -419,14 +419,14 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,  /**   * extcon_register_interest() - Register a notifier for a state change of a - *			      specific cable, not an entier set of cables of a - *			      extcon device. - * @obj:	an empty extcon_specific_cable_nb object to be returned. + *				specific cable, not an entier set of cables of a + *				extcon device. + * @obj:		an empty extcon_specific_cable_nb object to be returned.   * @extcon_name:	the name of extcon device.   *			if NULL, extcon_register_interest will register   *			every cable with the target cable_name given.   * @cable_name:		the target cable name. - * @nb:		the notifier block to get notified. + * @nb:			the notifier block to get notified.   *   * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets   * the struct for you. @@ -452,7 +452,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,  		if (!obj->edev)  			return -ENODEV; -		obj->cable_index = extcon_find_cable_index(obj->edev, cable_name); +		obj->cable_index = extcon_find_cable_index(obj->edev, +							  cable_name);  		if (obj->cable_index < 0)  			return obj->cable_index; @@ -460,7 +461,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,  		obj->internal_nb.notifier_call = _call_per_cable; -		return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb); +		return raw_notifier_chain_register(&obj->edev->nh, +						  &obj->internal_nb);  	} else {  		struct class_dev_iter iter;  		struct extcon_dev *extd; @@ -470,7 +472,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,  			return -ENODEV;  		class_dev_iter_init(&iter, extcon_class, NULL, NULL);  		while ((dev = class_dev_iter_next(&iter))) { -			extd = (struct extcon_dev *)dev_get_drvdata(dev); +			extd = dev_get_drvdata(dev);  			if (extcon_find_cable_index(extd, cable_name) < 0)  				continue; @@ -487,7 +489,7 @@ EXPORT_SYMBOL_GPL(extcon_register_interest);  /**   * extcon_unregister_interest() - Unregister the notifier registered by - *				extcon_register_interest(). + *				  extcon_register_interest().   * @obj:	the extcon_specific_cable_nb object returned by   *		extcon_register_interest().   */ @@ -502,7 +504,7 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);  /**   * extcon_register_notifier() - Register a notifiee to get notified by - *			      any attach status changes from the extcon. + *				any attach status changes from the extcon.   * @edev:	the extcon device.   * @nb:		a notifier block to be registered.   * @@ -556,7 +558,6 @@ static int create_extcon_class(void)  static void extcon_dev_release(struct device *dev)  { -	kfree(dev);  }  static const char *muex_name = "mutually_exclusive"; @@ -564,17 +565,110 @@ static void dummy_sysfs_dev_release(struct device *dev)  {  } +/* + * extcon_dev_allocate() - Allocate the memory of extcon device. + * @supported_cable:	Array of supported cable names ending with NULL. + *			If supported_cable is NULL, cable name related APIs + *			are disabled. + * + * This function allocates the memory for extcon device without allocating + * memory in each extcon provider driver and initialize default setting for + * extcon device. + * + * Return the pointer of extcon device if success or ERR_PTR(err) if fail + */ +struct extcon_dev *extcon_dev_allocate(const char **supported_cable) +{ +	struct extcon_dev *edev; + +	edev = kzalloc(sizeof(*edev), GFP_KERNEL); +	if (!edev) +		return ERR_PTR(-ENOMEM); + +	edev->max_supported = 0; +	edev->supported_cable = supported_cable; + +	return edev; +} + +/* + * extcon_dev_free() - Free the memory of extcon device. + * @edev:	the extcon device to free + */ +void extcon_dev_free(struct extcon_dev *edev) +{ +	kfree(edev); +} +EXPORT_SYMBOL_GPL(extcon_dev_free); + +static int devm_extcon_dev_match(struct device *dev, void *res, void *data) +{ +	struct extcon_dev **r = res; + +	if (WARN_ON(!r || !*r)) +		return 0; + +	return *r == data; +} + +static void devm_extcon_dev_release(struct device *dev, void *res) +{ +	extcon_dev_free(*(struct extcon_dev **)res); +} + +/** + * devm_extcon_dev_allocate - Allocate managed extcon device + * @dev:		device owning the extcon device being created + * @supported_cable:	Array of supported cable names ending with NULL. + *			If supported_cable is NULL, cable name related APIs + *			are disabled. + * + * This function manages automatically the memory of extcon device using device + * resource management and simplify the control of freeing the memory of extcon + * device. + * + * Returns the pointer memory of allocated extcon_dev if success + * or ERR_PTR(err) if fail + */ +struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, +					    const char **supported_cable) +{ +	struct extcon_dev **ptr, *edev; + +	ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL); +	if (!ptr) +		return ERR_PTR(-ENOMEM); + +	edev = extcon_dev_allocate(supported_cable); +	if (IS_ERR(edev)) { +		devres_free(ptr); +		return edev; +	} + +	*ptr = edev; +	devres_add(dev, ptr); + +	return edev; +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate); + +void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev) +{ +	WARN_ON(devres_release(dev, devm_extcon_dev_release, +			       devm_extcon_dev_match, edev)); +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_free); +  /**   * extcon_dev_register() - Register a new extcon device   * @edev	: the new extcon device (should be allocated before calling) - * @dev		: the parent device for this extcon device.   *   * Among the members of edev struct, please set the "user initializing data"   * in any case and set the "optional callbacks" if required. However, please   * do not set the values of "internal data", which are initialized by   * this function.   */ -int extcon_dev_register(struct extcon_dev *edev, struct device *dev) +int extcon_dev_register(struct extcon_dev *edev)  {  	int ret, index = 0; @@ -594,19 +688,20 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)  	}  	if (index > SUPPORTED_CABLE_MAX) { -		dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n"); +		dev_err(&edev->dev, "extcon: maximum number of supported cables exceeded.\n");  		return -EINVAL;  	} -	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL); -	if (!edev->dev) -		return -ENOMEM; -	edev->dev->parent = dev; -	edev->dev->class = extcon_class; -	edev->dev->release = extcon_dev_release; +	edev->dev.class = extcon_class; +	edev->dev.release = extcon_dev_release; -	edev->name = edev->name ? edev->name : dev_name(dev); -	dev_set_name(edev->dev, "%s", edev->name); +	edev->name = edev->name ? edev->name : dev_name(edev->dev.parent); +	if (IS_ERR_OR_NULL(edev->name)) { +		dev_err(&edev->dev, +			"extcon device name is null\n"); +		return -EINVAL; +	} +	dev_set_name(&edev->dev, "%s", edev->name);  	if (edev->max_supported) {  		char buf[10]; @@ -714,7 +809,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)  			goto err_alloc_groups;  		} -		edev->extcon_dev_type.name = dev_name(edev->dev); +		edev->extcon_dev_type.name = dev_name(&edev->dev);  		edev->extcon_dev_type.release = dummy_sysfs_dev_release;  		for (index = 0; index < edev->max_supported; index++) @@ -724,25 +819,24 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)  			edev->extcon_dev_type.groups[index] =  				&edev->attr_g_muex; -		edev->dev->type = &edev->extcon_dev_type; +		edev->dev.type = &edev->extcon_dev_type;  	} -	ret = device_register(edev->dev); +	ret = device_register(&edev->dev);  	if (ret) { -		put_device(edev->dev); +		put_device(&edev->dev);  		goto err_dev;  	}  #if defined(CONFIG_ANDROID)  	if (switch_class) -		ret = class_compat_create_link(switch_class, edev->dev, -					       NULL); +		ret = class_compat_create_link(switch_class, &edev->dev, NULL);  #endif /* CONFIG_ANDROID */  	spin_lock_init(&edev->lock);  	RAW_INIT_NOTIFIER_HEAD(&edev->nh); -	dev_set_drvdata(edev->dev, edev); +	dev_set_drvdata(&edev->dev, edev);  	edev->state = 0;  	mutex_lock(&extcon_dev_list_lock); @@ -768,7 +862,6 @@ err_alloc_cables:  	if (edev->max_supported)  		kfree(edev->cables);  err_sysfs_alloc: -	kfree(edev->dev);  	return ret;  }  EXPORT_SYMBOL_GPL(extcon_dev_register); @@ -788,12 +881,14 @@ void extcon_dev_unregister(struct extcon_dev *edev)  	list_del(&edev->entry);  	mutex_unlock(&extcon_dev_list_lock); -	if (IS_ERR_OR_NULL(get_device(edev->dev))) { -		dev_err(edev->dev, "Failed to unregister extcon_dev (%s)\n", -				dev_name(edev->dev)); +	if (IS_ERR_OR_NULL(get_device(&edev->dev))) { +		dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n", +				dev_name(&edev->dev));  		return;  	} +	device_unregister(&edev->dev); +  	if (edev->mutually_exclusive && edev->max_supported) {  		for (index = 0; edev->mutually_exclusive[index];  				index++) @@ -812,13 +907,110 @@ void extcon_dev_unregister(struct extcon_dev *edev)  #if defined(CONFIG_ANDROID)  	if (switch_class) -		class_compat_remove_link(switch_class, edev->dev, NULL); +		class_compat_remove_link(switch_class, &edev->dev, NULL);  #endif -	device_unregister(edev->dev); -	put_device(edev->dev); +	put_device(&edev->dev);  }  EXPORT_SYMBOL_GPL(extcon_dev_unregister); +static void devm_extcon_dev_unreg(struct device *dev, void *res) +{ +	extcon_dev_unregister(*(struct extcon_dev **)res); +} + +/** + * devm_extcon_dev_register() - Resource-managed extcon_dev_register() + * @dev:	device to allocate extcon device + * @edev:	the new extcon device to register + * + * Managed extcon_dev_register() function. If extcon device is attached with + * this function, that extcon device is automatically unregistered on driver + * detach. Internally this function calls extcon_dev_register() function. + * To get more information, refer that function. + * + * If extcon device is registered with this function and the device needs to be + * unregistered separately, devm_extcon_dev_unregister() should be used. + * + * Returns 0 if success or negaive error number if failure. + */ +int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev) +{ +	struct extcon_dev **ptr; +	int ret; + +	ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL); +	if (!ptr) +		return -ENOMEM; + +	ret = extcon_dev_register(edev); +	if (ret) { +		devres_free(ptr); +		return ret; +	} + +	*ptr = edev; +	devres_add(dev, ptr); + +	return 0; +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_register); + +/** + * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister() + * @dev:	device the extcon belongs to + * @edev:	the extcon device to unregister + * + * Unregister extcon device that is registered with devm_extcon_dev_register() + * function. + */ +void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev) +{ +	WARN_ON(devres_release(dev, devm_extcon_dev_unreg, +			       devm_extcon_dev_match, edev)); +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister); + +#ifdef CONFIG_OF +/* + * extcon_get_edev_by_phandle - Get the extcon device from devicetree + * @dev - instance to the given device + * @index - index into list of extcon_dev + * + * return the instance of extcon device + */ +struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) +{ +	struct device_node *node; +	struct extcon_dev *edev; + +	if (!dev->of_node) { +		dev_err(dev, "device does not have a device node entry\n"); +		return ERR_PTR(-EINVAL); +	} + +	node = of_parse_phandle(dev->of_node, "extcon", index); +	if (!node) { +		dev_err(dev, "failed to get phandle in %s node\n", +			dev->of_node->full_name); +		return ERR_PTR(-ENODEV); +	} + +	edev = extcon_get_extcon_dev(node->name); +	if (!edev) { +		dev_err(dev, "unable to get extcon device : %s\n", node->name); +		return ERR_PTR(-ENODEV); +	} + +	return edev; +} +#else +struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) +{ +	return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_OF */ +EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); +  static int __init extcon_class_init(void)  {  	return create_extcon_class(); diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index f874c30ddbf..645b2835681 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -32,13 +32,15 @@  #include <linux/extcon/extcon-gpio.h>  struct gpio_extcon_data { -	struct extcon_dev edev; +	struct extcon_dev *edev;  	unsigned gpio; +	bool gpio_active_low;  	const char *state_on;  	const char *state_off;  	int irq;  	struct delayed_work work;  	unsigned long debounce_jiffies; +	bool check_on_resume;  };  static void gpio_extcon_work(struct work_struct *work) @@ -49,7 +51,9 @@ static void gpio_extcon_work(struct work_struct *work)  			     work);  	state = gpio_get_value(data->gpio); -	extcon_set_state(&data->edev, state); +	if (data->gpio_active_low) +		state = !state; +	extcon_set_state(data->edev, state);  }  static irqreturn_t gpio_irq_handler(int irq, void *dev_id) @@ -63,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)  static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)  { -	struct gpio_extcon_data	*extcon_data = -		container_of(edev, struct gpio_extcon_data, edev); +	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 @@ -78,9 +83,9 @@ static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)  static int gpio_extcon_probe(struct platform_device *pdev)  { -	struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data; +	struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);  	struct gpio_extcon_data *extcon_data; -	int ret = 0; +	int ret;  	if (!pdata)  		return -EBUSY; @@ -94,47 +99,56 @@ static int gpio_extcon_probe(struct platform_device *pdev)  	if (!extcon_data)  		return -ENOMEM; -	extcon_data->edev.name = pdata->name; +	extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL); +	if (IS_ERR(extcon_data->edev)) { +		dev_err(&pdev->dev, "failed to allocate extcon device\n"); +		return -ENOMEM; +	} +	extcon_data->edev->name = pdata->name; +	extcon_data->edev->dev.parent = &pdev->dev; +  	extcon_data->gpio = pdata->gpio; +	extcon_data->gpio_active_low = pdata->gpio_active_low;  	extcon_data->state_on = pdata->state_on;  	extcon_data->state_off = pdata->state_off; +	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; -	extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce); +		extcon_data->edev->print_state = extcon_gpio_print_state; -	ret = extcon_dev_register(&extcon_data->edev, &pdev->dev); +	ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, +				    pdev->name);  	if (ret < 0)  		return ret; -	ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, -				    pdev->name); +	if (pdata->debounce) { +		ret = gpio_set_debounce(extcon_data->gpio, +					pdata->debounce * 1000); +		if (ret < 0) +			extcon_data->debounce_jiffies = +				msecs_to_jiffies(pdata->debounce); +	} + +	ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);  	if (ret < 0) -		goto err; +		return ret;  	INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);  	extcon_data->irq = gpio_to_irq(extcon_data->gpio); -	if (extcon_data->irq < 0) { -		ret = extcon_data->irq; -		goto err; -	} +	if (extcon_data->irq < 0) +		return extcon_data->irq;  	ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,  				      pdata->irq_flags, pdev->name,  				      extcon_data);  	if (ret < 0) -		goto err; +		return ret;  	platform_set_drvdata(pdev, extcon_data);  	/* Perform initial detection */  	gpio_extcon_work(&extcon_data->work.work);  	return 0; - -err: -	extcon_dev_unregister(&extcon_data->edev); - -	return ret;  }  static int gpio_extcon_remove(struct platform_device *pdev) @@ -143,17 +157,33 @@ static int gpio_extcon_remove(struct platform_device *pdev)  	cancel_delayed_work_sync(&extcon_data->work);  	free_irq(extcon_data->irq, extcon_data); -	extcon_dev_unregister(&extcon_data->edev);  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int gpio_extcon_resume(struct device *dev) +{ +	struct gpio_extcon_data *extcon_data; + +	extcon_data = dev_get_drvdata(dev); +	if (extcon_data->check_on_resume) +		queue_delayed_work(system_power_efficient_wq, +			&extcon_data->work, extcon_data->debounce_jiffies); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume); +  static struct platform_driver gpio_extcon_driver = {  	.probe		= gpio_extcon_probe,  	.remove		= gpio_extcon_remove,  	.driver		= {  		.name	= "extcon-gpio",  		.owner	= THIS_MODULE, +		.pm	= &gpio_extcon_pm_ops,  	},  }; diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c new file mode 100644 index 00000000000..d49e891b567 --- /dev/null +++ b/drivers/extcon/extcon-max14577.c @@ -0,0 +1,823 @@ +/* + * extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC + * + * Copyright (C) 2013,2014 Samsung Electrnoics + * Chanwoo Choi <cw00.choi@samsung.com> + * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/mfd/max14577.h> +#include <linux/mfd/max14577-private.h> +#include <linux/extcon.h> + +#define	DELAY_MS_DEFAULT		17000		/* unit: millisecond */ + +enum max14577_muic_adc_debounce_time { +	ADC_DEBOUNCE_TIME_5MS = 0, +	ADC_DEBOUNCE_TIME_10MS, +	ADC_DEBOUNCE_TIME_25MS, +	ADC_DEBOUNCE_TIME_38_62MS, +}; + +enum max14577_muic_status { +	MAX14577_MUIC_STATUS1 = 0, +	MAX14577_MUIC_STATUS2 = 1, +	MAX14577_MUIC_STATUS_END, +}; + +/** + * struct max14577_muic_irq + * @irq: the index of irq list of MUIC device. + * @name: the name of irq. + * @virq: the virtual irq to use irq domain + */ +struct max14577_muic_irq { +	unsigned int irq; +	const char *name; +	unsigned int virq; +}; + +static struct max14577_muic_irq max14577_muic_irqs[] = { +	{ MAX14577_IRQ_INT1_ADC,	"muic-ADC" }, +	{ MAX14577_IRQ_INT1_ADCLOW,	"muic-ADCLOW" }, +	{ MAX14577_IRQ_INT1_ADCERR,	"muic-ADCError" }, +	{ MAX14577_IRQ_INT2_CHGTYP,	"muic-CHGTYP" }, +	{ MAX14577_IRQ_INT2_CHGDETRUN,	"muic-CHGDETRUN" }, +	{ MAX14577_IRQ_INT2_DCDTMR,	"muic-DCDTMR" }, +	{ MAX14577_IRQ_INT2_DBCHG,	"muic-DBCHG" }, +	{ MAX14577_IRQ_INT2_VBVOLT,	"muic-VBVOLT" }, +}; + +static struct max14577_muic_irq max77836_muic_irqs[] = { +	{ MAX14577_IRQ_INT1_ADC,	"muic-ADC" }, +	{ MAX14577_IRQ_INT1_ADCLOW,	"muic-ADCLOW" }, +	{ MAX14577_IRQ_INT1_ADCERR,	"muic-ADCError" }, +	{ MAX77836_IRQ_INT1_ADC1K,	"muic-ADC1K" }, +	{ MAX14577_IRQ_INT2_CHGTYP,	"muic-CHGTYP" }, +	{ MAX14577_IRQ_INT2_CHGDETRUN,	"muic-CHGDETRUN" }, +	{ MAX14577_IRQ_INT2_DCDTMR,	"muic-DCDTMR" }, +	{ MAX14577_IRQ_INT2_DBCHG,	"muic-DBCHG" }, +	{ MAX14577_IRQ_INT2_VBVOLT,	"muic-VBVOLT" }, +	{ MAX77836_IRQ_INT2_VIDRM,	"muic-VIDRM" }, +}; + +struct max14577_muic_info { +	struct device *dev; +	struct max14577 *max14577; +	struct extcon_dev *edev; +	int prev_cable_type; +	int prev_chg_type; +	u8 status[MAX14577_MUIC_STATUS_END]; + +	struct max14577_muic_irq *muic_irqs; +	unsigned int muic_irqs_num; +	bool irq_adc; +	bool irq_chg; +	struct work_struct irq_work; +	struct mutex mutex; + +	/* +	 * Use delayed workqueue to detect cable state and then +	 * notify cable state to notifiee/platform through uevent. +	 * After completing the booting of platform, the extcon provider +	 * driver should notify cable state to upper layer. +	 */ +	struct delayed_work wq_detcable; + +	/* +	 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB +	 * h/w path of COMP2/COMN1 on CONTROL1 register. +	 */ +	int path_usb; +	int path_uart; +}; + +enum max14577_muic_cable_group { +	MAX14577_CABLE_GROUP_ADC = 0, +	MAX14577_CABLE_GROUP_CHG, +}; + +/* Define supported accessory type */ +enum max14577_muic_acc_type { +	MAX14577_MUIC_ADC_GROUND = 0x0, +	MAX14577_MUIC_ADC_SEND_END_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S1_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S2_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S3_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S4_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S5_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S6_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S7_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S8_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S9_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S10_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S11_BUTTON, +	MAX14577_MUIC_ADC_REMOTE_S12_BUTTON, +	MAX14577_MUIC_ADC_RESERVED_ACC_1, +	MAX14577_MUIC_ADC_RESERVED_ACC_2, +	MAX14577_MUIC_ADC_RESERVED_ACC_3, +	MAX14577_MUIC_ADC_RESERVED_ACC_4, +	MAX14577_MUIC_ADC_RESERVED_ACC_5, +	MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2, +	MAX14577_MUIC_ADC_PHONE_POWERED_DEV, +	MAX14577_MUIC_ADC_TTY_CONVERTER, +	MAX14577_MUIC_ADC_UART_CABLE, +	MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG, +	MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF, +	MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON, +	MAX14577_MUIC_ADC_AV_CABLE_NOLOAD, +	MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG, +	MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF, +	MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON, +	MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1, /* with Remote and Simple Ctrl */ +	MAX14577_MUIC_ADC_OPEN, +}; + +/* max14577 MUIC device support below list of accessories(external connector) */ +enum { +	EXTCON_CABLE_USB = 0, +	EXTCON_CABLE_TA, +	EXTCON_CABLE_FAST_CHARGER, +	EXTCON_CABLE_SLOW_CHARGER, +	EXTCON_CABLE_CHARGE_DOWNSTREAM, +	EXTCON_CABLE_JIG_USB_ON, +	EXTCON_CABLE_JIG_USB_OFF, +	EXTCON_CABLE_JIG_UART_OFF, +	EXTCON_CABLE_JIG_UART_ON, + +	_EXTCON_CABLE_NUM, +}; + +static const char *max14577_extcon_cable[] = { +	[EXTCON_CABLE_USB]			= "USB", +	[EXTCON_CABLE_TA]			= "TA", +	[EXTCON_CABLE_FAST_CHARGER]		= "Fast-charger", +	[EXTCON_CABLE_SLOW_CHARGER]		= "Slow-charger", +	[EXTCON_CABLE_CHARGE_DOWNSTREAM]	= "Charge-downstream", +	[EXTCON_CABLE_JIG_USB_ON]		= "JIG-USB-ON", +	[EXTCON_CABLE_JIG_USB_OFF]		= "JIG-USB-OFF", +	[EXTCON_CABLE_JIG_UART_OFF]		= "JIG-UART-OFF", +	[EXTCON_CABLE_JIG_UART_ON]		= "JIG-UART-ON", + +	NULL, +}; + +/* + * max14577_muic_set_debounce_time - Set the debounce time of ADC + * @info: the instance including private data of max14577 MUIC + * @time: the debounce time of ADC + */ +static int max14577_muic_set_debounce_time(struct max14577_muic_info *info, +		enum max14577_muic_adc_debounce_time time) +{ +	u8 ret; + +	switch (time) { +	case ADC_DEBOUNCE_TIME_5MS: +	case ADC_DEBOUNCE_TIME_10MS: +	case ADC_DEBOUNCE_TIME_25MS: +	case ADC_DEBOUNCE_TIME_38_62MS: +		ret = max14577_update_reg(info->max14577->regmap, +					  MAX14577_MUIC_REG_CONTROL3, +					  CTRL3_ADCDBSET_MASK, +					  time << CTRL3_ADCDBSET_SHIFT); +		if (ret) { +			dev_err(info->dev, "failed to set ADC debounce time\n"); +			return ret; +		} +		break; +	default: +		dev_err(info->dev, "invalid ADC debounce time\n"); +		return -EINVAL; +	} + +	return 0; +}; + +/* + * max14577_muic_set_path - Set hardware line according to attached cable + * @info: the instance including private data of max14577 MUIC + * @value: the path according to attached cable + * @attached: the state of cable (true:attached, false:detached) + * + * The max14577 MUIC device share outside H/W line among a varity of cables + * so, this function set internal path of H/W line according to the type of + * attached cable. + */ +static int max14577_muic_set_path(struct max14577_muic_info *info, +		u8 val, bool attached) +{ +	int ret = 0; +	u8 ctrl1, ctrl2 = 0; + +	/* Set open state to path before changing hw path */ +	ret = max14577_update_reg(info->max14577->regmap, +				MAX14577_MUIC_REG_CONTROL1, +				CLEAR_IDBEN_MICEN_MASK, CTRL1_SW_OPEN); +	if (ret < 0) { +		dev_err(info->dev, "failed to update MUIC register\n"); +		return ret; +	} + +	if (attached) +		ctrl1 = val; +	else +		ctrl1 = CTRL1_SW_OPEN; + +	ret = max14577_update_reg(info->max14577->regmap, +				MAX14577_MUIC_REG_CONTROL1, +				CLEAR_IDBEN_MICEN_MASK, ctrl1); +	if (ret < 0) { +		dev_err(info->dev, "failed to update MUIC register\n"); +		return ret; +	} + +	if (attached) +		ctrl2 |= CTRL2_CPEN_MASK;	/* LowPwr=0, CPEn=1 */ +	else +		ctrl2 |= CTRL2_LOWPWR_MASK;	/* LowPwr=1, CPEn=0 */ + +	ret = max14577_update_reg(info->max14577->regmap, +			MAX14577_REG_CONTROL2, +			CTRL2_LOWPWR_MASK | CTRL2_CPEN_MASK, ctrl2); +	if (ret < 0) { +		dev_err(info->dev, "failed to update MUIC register\n"); +		return ret; +	} + +	dev_dbg(info->dev, +		"CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", +		ctrl1, ctrl2, attached ? "attached" : "detached"); + +	return 0; +} + +/* + * max14577_muic_get_cable_type - Return cable type and check cable state + * @info: the instance including private data of max14577 MUIC + * @group: the path according to attached cable + * @attached: store cable state and return + * + * This function check the cable state either attached or detached, + * and then divide precise type of cable according to cable group. + *	- max14577_CABLE_GROUP_ADC + *	- max14577_CABLE_GROUP_CHG + */ +static int max14577_muic_get_cable_type(struct max14577_muic_info *info, +		enum max14577_muic_cable_group group, bool *attached) +{ +	int cable_type = 0; +	int adc; +	int chg_type; + +	switch (group) { +	case MAX14577_CABLE_GROUP_ADC: +		/* +		 * Read ADC value to check cable type and decide cable state +		 * according to cable type +		 */ +		adc = info->status[MAX14577_MUIC_STATUS1] & STATUS1_ADC_MASK; +		adc >>= STATUS1_ADC_SHIFT; + +		/* +		 * Check current cable state/cable type and store cable type +		 * (info->prev_cable_type) for handling cable when cable is +		 * detached. +		 */ +		if (adc == MAX14577_MUIC_ADC_OPEN) { +			*attached = false; + +			cable_type = info->prev_cable_type; +			info->prev_cable_type = MAX14577_MUIC_ADC_OPEN; +		} else { +			*attached = true; + +			cable_type = info->prev_cable_type = adc; +		} +		break; +	case MAX14577_CABLE_GROUP_CHG: +		/* +		 * Read charger type to check cable type and decide cable state +		 * according to type of charger cable. +		 */ +		chg_type = info->status[MAX14577_MUIC_STATUS2] & +			STATUS2_CHGTYP_MASK; +		chg_type >>= STATUS2_CHGTYP_SHIFT; + +		if (chg_type == MAX14577_CHARGER_TYPE_NONE) { +			*attached = false; + +			cable_type = info->prev_chg_type; +			info->prev_chg_type = MAX14577_CHARGER_TYPE_NONE; +		} else { +			*attached = true; + +			/* +			 * Check current cable state/cable type and store cable +			 * type(info->prev_chg_type) for handling cable when +			 * charger cable is detached. +			 */ +			cable_type = info->prev_chg_type = chg_type; +		} + +		break; +	default: +		dev_err(info->dev, "Unknown cable group (%d)\n", group); +		cable_type = -EINVAL; +		break; +	} + +	return cable_type; +} + +static int max14577_muic_jig_handler(struct max14577_muic_info *info, +		int cable_type, bool attached) +{ +	char cable_name[32]; +	int ret = 0; +	u8 path = CTRL1_SW_OPEN; + +	dev_dbg(info->dev, +		"external connector is %s (adc:0x%02x)\n", +		attached ? "attached" : "detached", cable_type); + +	switch (cable_type) { +	case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF:	/* ADC_JIG_USB_OFF */ +		/* PATH:AP_USB */ +		strcpy(cable_name, "JIG-USB-OFF"); +		path = CTRL1_SW_USB; +		break; +	case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON:	/* ADC_JIG_USB_ON */ +		/* PATH:AP_USB */ +		strcpy(cable_name, "JIG-USB-ON"); +		path = CTRL1_SW_USB; +		break; +	case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF:	/* ADC_JIG_UART_OFF */ +		/* PATH:AP_UART */ +		strcpy(cable_name, "JIG-UART-OFF"); +		path = CTRL1_SW_UART; +		break; +	default: +		dev_err(info->dev, "failed to detect %s jig cable\n", +			attached ? "attached" : "detached"); +		return -EINVAL; +	} + +	ret = max14577_muic_set_path(info, path, attached); +	if (ret < 0) +		return ret; + +	extcon_set_cable_state(info->edev, cable_name, attached); + +	return 0; +} + +static int max14577_muic_adc_handler(struct max14577_muic_info *info) +{ +	int cable_type; +	bool attached; +	int ret = 0; + +	/* Check accessory state which is either detached or attached */ +	cable_type = max14577_muic_get_cable_type(info, +				MAX14577_CABLE_GROUP_ADC, &attached); + +	dev_dbg(info->dev, +		"external connector is %s (adc:0x%02x, prev_adc:0x%x)\n", +		attached ? "attached" : "detached", cable_type, +		info->prev_cable_type); + +	switch (cable_type) { +	case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: +	case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: +	case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: +		/* JIG */ +		ret = max14577_muic_jig_handler(info, cable_type, attached); +		if (ret < 0) +			return ret; +		break; +	case MAX14577_MUIC_ADC_GROUND: +	case MAX14577_MUIC_ADC_SEND_END_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S1_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S2_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S3_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S4_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S5_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S6_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S7_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S8_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S9_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S10_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S11_BUTTON: +	case MAX14577_MUIC_ADC_REMOTE_S12_BUTTON: +	case MAX14577_MUIC_ADC_RESERVED_ACC_1: +	case MAX14577_MUIC_ADC_RESERVED_ACC_2: +	case MAX14577_MUIC_ADC_RESERVED_ACC_3: +	case MAX14577_MUIC_ADC_RESERVED_ACC_4: +	case MAX14577_MUIC_ADC_RESERVED_ACC_5: +	case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2: +	case MAX14577_MUIC_ADC_PHONE_POWERED_DEV: +	case MAX14577_MUIC_ADC_TTY_CONVERTER: +	case MAX14577_MUIC_ADC_UART_CABLE: +	case MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG: +	case MAX14577_MUIC_ADC_AV_CABLE_NOLOAD: +	case MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG: +	case MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON: +	case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1: +		/* +		 * This accessory isn't used in general case if it is specially +		 * needed to detect additional accessory, should implement +		 * proper operation when this accessory is attached/detached. +		 */ +		dev_info(info->dev, +			"accessory is %s but it isn't used (adc:0x%x)\n", +			attached ? "attached" : "detached", cable_type); +		return -EAGAIN; +	default: +		dev_err(info->dev, +			"failed to detect %s accessory (adc:0x%x)\n", +			attached ? "attached" : "detached", cable_type); +		return -EINVAL; +	} + +	return 0; +} + +static int max14577_muic_chg_handler(struct max14577_muic_info *info) +{ +	int chg_type; +	bool attached; +	int ret = 0; + +	chg_type = max14577_muic_get_cable_type(info, +				MAX14577_CABLE_GROUP_CHG, &attached); + +	dev_dbg(info->dev, +		"external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n", +			attached ? "attached" : "detached", +			chg_type, info->prev_chg_type); + +	switch (chg_type) { +	case MAX14577_CHARGER_TYPE_USB: +		/* PATH:AP_USB */ +		ret = max14577_muic_set_path(info, info->path_usb, attached); +		if (ret < 0) +			return ret; + +		extcon_set_cable_state(info->edev, "USB", attached); +		break; +	case MAX14577_CHARGER_TYPE_DEDICATED_CHG: +		extcon_set_cable_state(info->edev, "TA", attached); +		break; +	case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: +		extcon_set_cable_state(info->edev, +				"Charge-downstream", attached); +		break; +	case MAX14577_CHARGER_TYPE_SPECIAL_500MA: +		extcon_set_cable_state(info->edev, "Slow-charger", attached); +		break; +	case MAX14577_CHARGER_TYPE_SPECIAL_1A: +		extcon_set_cable_state(info->edev, "Fast-charger", attached); +		break; +	case MAX14577_CHARGER_TYPE_NONE: +	case MAX14577_CHARGER_TYPE_DEAD_BATTERY: +		break; +	default: +		dev_err(info->dev, +			"failed to detect %s accessory (chg_type:0x%x)\n", +			attached ? "attached" : "detached", chg_type); +		return -EINVAL; +	} + +	return 0; +} + +static void max14577_muic_irq_work(struct work_struct *work) +{ +	struct max14577_muic_info *info = container_of(work, +			struct max14577_muic_info, irq_work); +	int ret = 0; + +	if (!info->edev) +		return; + +	mutex_lock(&info->mutex); + +	ret = max14577_bulk_read(info->max14577->regmap, +			MAX14577_MUIC_REG_STATUS1, info->status, 2); +	if (ret) { +		dev_err(info->dev, "failed to read MUIC register\n"); +		mutex_unlock(&info->mutex); +		return; +	} + +	if (info->irq_adc) { +		ret = max14577_muic_adc_handler(info); +		info->irq_adc = false; +	} +	if (info->irq_chg) { +		ret = max14577_muic_chg_handler(info); +		info->irq_chg = false; +	} + +	if (ret < 0) +		dev_err(info->dev, "failed to handle MUIC interrupt\n"); + +	mutex_unlock(&info->mutex); + +	return; +} + +/* + * Sets irq_adc or irq_chg in max14577_muic_info and returns 1. + * Returns 0 if irq_type does not match registered IRQ for this device type. + */ +static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type) +{ +	switch (irq_type) { +	case MAX14577_IRQ_INT1_ADC: +	case MAX14577_IRQ_INT1_ADCLOW: +	case MAX14577_IRQ_INT1_ADCERR: +		/* Handle all of accessory except for +		   type of charger accessory */ +		info->irq_adc = true; +		return 1; +	case MAX14577_IRQ_INT2_CHGTYP: +	case MAX14577_IRQ_INT2_CHGDETRUN: +	case MAX14577_IRQ_INT2_DCDTMR: +	case MAX14577_IRQ_INT2_DBCHG: +	case MAX14577_IRQ_INT2_VBVOLT: +		/* Handle charger accessory */ +		info->irq_chg = true; +		return 1; +	default: +		return 0; +	} +} + +/* + * Sets irq_adc or irq_chg in max14577_muic_info and returns 1. + * Returns 0 if irq_type does not match registered IRQ for this device type. + */ +static int max77836_parse_irq(struct max14577_muic_info *info, int irq_type) +{ +	/* First check common max14577 interrupts */ +	if (max14577_parse_irq(info, irq_type)) +		return 1; + +	switch (irq_type) { +	case MAX77836_IRQ_INT1_ADC1K: +		info->irq_adc = true; +		return 1; +	case MAX77836_IRQ_INT2_VIDRM: +		/* Handle charger accessory */ +		info->irq_chg = true; +		return 1; +	default: +		return 0; +	} +} + +static irqreturn_t max14577_muic_irq_handler(int irq, void *data) +{ +	struct max14577_muic_info *info = data; +	int i, irq_type = -1; +	bool irq_parsed; + +	/* +	 * We may be called multiple times for different nested IRQ-s. +	 * Including changes in INT1_ADC and INT2_CGHTYP at once. +	 * However we only need to know whether it was ADC, charger +	 * or both interrupts so decode IRQ and turn on proper flags. +	 */ +	for (i = 0; i < info->muic_irqs_num; i++) +		if (irq == info->muic_irqs[i].virq) +			irq_type = info->muic_irqs[i].irq; + +	switch (info->max14577->dev_type) { +	case MAXIM_DEVICE_TYPE_MAX77836: +		irq_parsed = max77836_parse_irq(info, irq_type); +		break; +	case MAXIM_DEVICE_TYPE_MAX14577: +	default: +		irq_parsed = max14577_parse_irq(info, irq_type); +		break; +	} + +	if (!irq_parsed) { +		dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n", +				irq_type); +		return IRQ_HANDLED; +	} +	schedule_work(&info->irq_work); + +	return IRQ_HANDLED; +} + +static int max14577_muic_detect_accessory(struct max14577_muic_info *info) +{ +	int ret = 0; +	int adc; +	int chg_type; +	bool attached; + +	mutex_lock(&info->mutex); + +	/* Read STATUSx register to detect accessory */ +	ret = max14577_bulk_read(info->max14577->regmap, +			MAX14577_MUIC_REG_STATUS1, info->status, 2); +	if (ret) { +		dev_err(info->dev, "failed to read MUIC register\n"); +		mutex_unlock(&info->mutex); +		return ret; +	} + +	adc = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_ADC, +					&attached); +	if (attached && adc != MAX14577_MUIC_ADC_OPEN) { +		ret = max14577_muic_adc_handler(info); +		if (ret < 0) { +			dev_err(info->dev, "Cannot detect accessory\n"); +			mutex_unlock(&info->mutex); +			return ret; +		} +	} + +	chg_type = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_CHG, +					&attached); +	if (attached && chg_type != MAX14577_CHARGER_TYPE_NONE) { +		ret = max14577_muic_chg_handler(info); +		if (ret < 0) { +			dev_err(info->dev, "Cannot detect charger accessory\n"); +			mutex_unlock(&info->mutex); +			return ret; +		} +	} + +	mutex_unlock(&info->mutex); + +	return 0; +} + +static void max14577_muic_detect_cable_wq(struct work_struct *work) +{ +	struct max14577_muic_info *info = container_of(to_delayed_work(work), +				struct max14577_muic_info, wq_detcable); + +	max14577_muic_detect_accessory(info); +} + +static int max14577_muic_probe(struct platform_device *pdev) +{ +	struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); +	struct max14577_muic_info *info; +	int delay_jiffies; +	int ret; +	int i; +	u8 id; + +	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); +	if (!info) { +		dev_err(&pdev->dev, "failed to allocate memory\n"); +		return -ENOMEM; +	} +	info->dev = &pdev->dev; +	info->max14577 = max14577; + +	platform_set_drvdata(pdev, info); +	mutex_init(&info->mutex); + +	INIT_WORK(&info->irq_work, max14577_muic_irq_work); + +	switch (max14577->dev_type) { +	case MAXIM_DEVICE_TYPE_MAX77836: +		info->muic_irqs = max77836_muic_irqs; +		info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs); +		break; +	case MAXIM_DEVICE_TYPE_MAX14577: +	default: +		info->muic_irqs = max14577_muic_irqs; +		info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs); +	} + +	/* Support irq domain for max14577 MUIC device */ +	for (i = 0; i < info->muic_irqs_num; i++) { +		struct max14577_muic_irq *muic_irq = &info->muic_irqs[i]; +		unsigned int virq = 0; + +		virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq); +		if (virq <= 0) +			return -EINVAL; +		muic_irq->virq = virq; + +		ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, +				max14577_muic_irq_handler, +				IRQF_NO_SUSPEND, +				muic_irq->name, info); +		if (ret) { +			dev_err(&pdev->dev, +				"failed: irq request (IRQ: %d," +				" error :%d)\n", +				muic_irq->irq, ret); +			return ret; +		} +	} + +	/* Initialize extcon device */ +	info->edev = devm_extcon_dev_allocate(&pdev->dev, +					      max14577_extcon_cable); +	if (IS_ERR(info->edev)) { +		dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); +		return -ENOMEM; +	} + +	info->edev->name = dev_name(&pdev->dev); + +	ret = devm_extcon_dev_register(&pdev->dev, info->edev); +	if (ret) { +		dev_err(&pdev->dev, "failed to register extcon device\n"); +		return ret; +	} + +	/* Default h/w line path */ +	info->path_usb = CTRL1_SW_USB; +	info->path_uart = CTRL1_SW_UART; +	delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); + +	/* Set initial path for UART */ +	max14577_muic_set_path(info, info->path_uart, true); + +	/* Check revision number of MUIC device*/ +	ret = max14577_read_reg(info->max14577->regmap, +			MAX14577_REG_DEVICEID, &id); +	if (ret < 0) { +		dev_err(&pdev->dev, "failed to read revision number\n"); +		return ret; +	} +	dev_info(info->dev, "device ID : 0x%x\n", id); + +	/* Set ADC debounce time */ +	max14577_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); + +	/* +	 * Detect accessory after completing the initialization of platform +	 * +	 * - Use delayed workqueue to detect cable state and then +	 * notify cable state to notifiee/platform through uevent. +	 * After completing the booting of platform, the extcon provider +	 * driver should notify cable state to upper layer. +	 */ +	INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq); +	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, +			delay_jiffies); + +	return ret; +} + +static int max14577_muic_remove(struct platform_device *pdev) +{ +	struct max14577_muic_info *info = platform_get_drvdata(pdev); + +	cancel_work_sync(&info->irq_work); + +	return 0; +} + +static const struct platform_device_id max14577_muic_id[] = { +	{ "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, }, +	{ "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, }, +	{ } +}; +MODULE_DEVICE_TABLE(platform, max14577_muic_id); + +static struct platform_driver max14577_muic_driver = { +	.driver		= { +		.name	= "max14577-muic", +		.owner	= THIS_MODULE, +	}, +	.probe		= max14577_muic_probe, +	.remove		= max14577_muic_remove, +	.id_table	= max14577_muic_id, +}; + +module_platform_driver(max14577_muic_driver); + +MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:extcon-max14577"); diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index b56bdaa27d4..2c7c3e19159 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -189,14 +189,17 @@ enum max77693_muic_acc_type {  	/* The below accessories have same ADC value so ADCLow and  	   ADC1K bit is used to separate specific accessory */ -	MAX77693_MUIC_GND_USB_OTG = 0x100,	/* ADC:0x0, VBVolot:0, ADCLow:0, ADC1K:0 */ -	MAX77693_MUIC_GND_USB_OTG_VB = 0x104,	/* ADC:0x0, VBVolot:1, ADCLow:0, ADC1K:0 */ -	MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:0 */ -	MAX77693_MUIC_GND_MHL = 0x103,		/* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:1 */ -	MAX77693_MUIC_GND_MHL_VB = 0x107,	/* ADC:0x0, VBVolot:1, ADCLow:1, ADC1K:1 */ +						/* ADC|VBVolot|ADCLow|ADC1K| */ +	MAX77693_MUIC_GND_USB_OTG = 0x100,	/* 0x0|      0|     0|    0| */ +	MAX77693_MUIC_GND_USB_OTG_VB = 0x104,	/* 0x0|      1|     0|    0| */ +	MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* 0x0|      0|     1|    0| */ +	MAX77693_MUIC_GND_MHL = 0x103,		/* 0x0|      0|     1|    1| */ +	MAX77693_MUIC_GND_MHL_VB = 0x107,	/* 0x0|      1|     1|    1| */  }; -/* MAX77693 MUIC device support below list of accessories(external connector) */ +/* + * MAX77693 MUIC device support below list of accessories(external connector) + */  enum {  	EXTCON_CABLE_USB = 0,  	EXTCON_CABLE_USB_HOST, @@ -395,12 +398,12 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info,  			vbvolt >>= STATUS2_VBVOLT_SHIFT;  			/** -			 * [0x1][VBVolt][ADCLow][ADC1K] -			 * [0x1    0	   0       0  ]	: USB_OTG -			 * [0x1    1	   0       0  ]	: USB_OTG_VB -			 * [0x1    0       1       0  ] : Audio Video Cable with load -			 * [0x1    0       1       1  ] : MHL without charging connector -			 * [0x1    1       1       1  ] : MHL with charging connector +			 * [0x1|VBVolt|ADCLow|ADC1K] +			 * [0x1|     0|     0|    0] USB_OTG +			 * [0x1|     1|     0|    0] USB_OTG_VB +			 * [0x1|     0|     1|    0] Audio Video cable with load +			 * [0x1|     0|     1|    1] MHL without charging cable +			 * [0x1|     1|     1|    1] MHL with charging cable  			 */  			cable_type = ((0x1 << 8)  					| (vbvolt << 2) @@ -723,11 +726,11 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)  		if (ret < 0)  			return ret;  		break; -	case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:	/* DOCK_KEY_PREV */ -	case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:	/* DOCK_KEY_NEXT */ -	case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:	/* DOCK_VOL_DOWN */ -	case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:	/* DOCK_VOL_UP */ -	case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:	/* DOCK_KEY_PLAY_PAUSE */ +	case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:      /* DOCK_KEY_PREV */ +	case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:      /* DOCK_KEY_NEXT */ +	case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:      /* DOCK_VOL_DOWN */ +	case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:     /* DOCK_VOL_UP */ +	case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:     /* DOCK_KEY_PLAY_PAUSE */  		/*  		 * Button of DOCK device  		 * - the Prev/Next/Volume Up/Volume Down/Play-Pause button @@ -815,19 +818,21 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)  		case MAX77693_MUIC_GND_MHL_VB:  			/*  			 * MHL cable with MHL_TA(USB/TA) cable -			 * - MHL cable include two port(HDMI line and separate micro- -			 * usb port. When the target connect MHL cable, extcon driver -			 * check whether MHL_TA(USB/TA) cable is connected. If MHL_TA -			 * cable is connected, extcon driver notify state to notifiee -			 * for charging battery. +			 * - MHL cable include two port(HDMI line and separate +			 * micro-usb port. When the target connect MHL cable, +			 * extcon driver check whether MHL_TA(USB/TA) cable is +			 * connected. If MHL_TA cable is connected, extcon +			 * driver notify state to notifiee for charging battery.  			 *  			 * Features of 'MHL_TA(USB/TA) with MHL cable'  			 * - Support MHL -			 * - Support charging through micro-usb port without data connection +			 * - Support charging through micro-usb port without +			 *   data connection  			 */  			extcon_set_cable_state(info->edev, "MHL_TA", attached);  			if (!cable_attached) -				extcon_set_cable_state(info->edev, "MHL", cable_attached); +				extcon_set_cable_state(info->edev, +						      "MHL", cable_attached);  			break;  		} @@ -839,47 +844,51 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)  		case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:		/* Dock-Audio */  			/*  			 * Dock-Audio device with USB/TA cable -			 * - Dock device include two port(Dock-Audio and micro-usb -			 * port). When the target connect Dock-Audio device, extcon -			 * driver check whether USB/TA cable is connected. If USB/TA -			 * cable is connected, extcon driver notify state to notifiee -			 * for charging battery. +			 * - Dock device include two port(Dock-Audio and micro- +			 * usb port). When the target connect Dock-Audio device, +			 * extcon driver check whether USB/TA cable is connected +			 * or not. If USB/TA cable is connected, extcon driver +			 * notify state to notifiee for charging battery.  			 *  			 * Features of 'USB/TA cable with Dock-Audio device'  			 * - Support external output feature of audio. -			 * - Support charging through micro-usb port without data -			 *           connection. +			 * - Support charging through micro-usb port without +			 *   data connection.  			 */  			extcon_set_cable_state(info->edev, "USB", attached);  			if (!cable_attached) -				extcon_set_cable_state(info->edev, "Dock-Audio", cable_attached); +				extcon_set_cable_state(info->edev, "Dock-Audio", +						      cable_attached);  			break;  		case MAX77693_MUIC_ADC_RESERVED_ACC_3:		/* Dock-Smart */  			/*  			 * Dock-Smart device with USB/TA cable  			 * - Dock-Desk device include three type of cable which  			 * are HDMI, USB for mouse/keyboard and micro-usb port -			 * for USB/TA cable. Dock-Smart device need always exteranl -			 * power supply(USB/TA cable through micro-usb cable). Dock- -			 * Smart device support screen output of target to separate -			 * monitor and mouse/keyboard for desktop mode. +			 * for USB/TA cable. Dock-Smart device need always +			 * exteranl power supply(USB/TA cable through micro-usb +			 * cable). Dock-Smart device support screen output of +			 * target to separate monitor and mouse/keyboard for +			 * desktop mode.  			 *  			 * Features of 'USB/TA cable with Dock-Smart device'  			 * - Support MHL  			 * - Support external output feature of audio -			 * - Support charging through micro-usb port without data -			 *	     connection if TA cable is connected to target. -			 * - Support charging and data connection through micro-usb port -			 *           if USB cable is connected between target and host -			 *	     device. +			 * - Support charging through micro-usb port without +			 *   data connection if TA cable is connected to target. +			 * - Support charging and data connection through micro- +			 *   usb port if USB cable is connected between target +			 *   and host device  			 * - Support OTG device (Mouse/Keyboard)  			 */ -			ret = max77693_muic_set_path(info, info->path_usb, attached); +			ret = max77693_muic_set_path(info, info->path_usb, +						    attached);  			if (ret < 0)  				return ret; -			extcon_set_cable_state(info->edev, "Dock-Smart", attached); +			extcon_set_cable_state(info->edev, "Dock-Smart", +					      attached);  			extcon_set_cable_state(info->edev, "MHL", attached);  			break; @@ -889,25 +898,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)  		switch (chg_type) {  		case MAX77693_CHARGER_TYPE_NONE:  			/* -			 * When MHL(with USB/TA cable) or Dock-Audio with USB/TA cable -			 * is attached, muic device happen below two interrupt. -			 * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting MHL/Dock-Audio. -			 * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting USB/TA cable -			 *   connected to MHL or Dock-Audio. -			 * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC interrupt -			 * than MAX77693_MUIC_IRQ_INT2_CHGTYP interrupt. +			 * When MHL(with USB/TA cable) or Dock-Audio with USB/TA +			 * cable is attached, muic device happen below two irq. +			 * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting +			 *    MHL/Dock-Audio. +			 * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting +			 *    USB/TA cable connected to MHL or Dock-Audio. +			 * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC +			 * irq than MAX77693_MUIC_IRQ_INT2_CHGTYP irq.  			 * -			 * If user attach MHL (with USB/TA cable and immediately detach -			 * MHL with USB/TA cable before MAX77693_MUIC_IRQ_INT2_CHGTYP -			 * interrupt is happened, USB/TA cable remain connected state to -			 * target. But USB/TA cable isn't connected to target. The user -			 * be face with unusual action. So, driver should check this -			 * situation in spite of, that previous charger type is N/A. +			 * If user attach MHL (with USB/TA cable and immediately +			 * detach MHL with USB/TA cable before MAX77693_MUIC_IRQ +			 * _INT2_CHGTYP irq is happened, USB/TA cable remain +			 * connected state to target. But USB/TA cable isn't +			 * connected to target. The user be face with unusual +			 * action. So, driver should check this situation in +			 * spite of, that previous charger type is N/A.  			 */  			break;  		case MAX77693_CHARGER_TYPE_USB:  			/* Only USB cable, PATH:AP_USB */ -			ret = max77693_muic_set_path(info, info->path_usb, attached); +			ret = max77693_muic_set_path(info, info->path_usb, +						    attached);  			if (ret < 0)  				return ret; @@ -953,7 +965,7 @@ static void max77693_muic_irq_work(struct work_struct *work)  	mutex_lock(&info->mutex); -	for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) +	for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)  		if (info->irq == muic_irqs[i].virq)  			irq_type = muic_irqs[i].irq; @@ -1163,24 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev)  	}  	/* Initialize extcon device */ -	info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), -				  GFP_KERNEL); -	if (!info->edev) { +	info->edev = devm_extcon_dev_allocate(&pdev->dev, +					      max77693_extcon_cable); +	if (IS_ERR(info->edev)) {  		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");  		ret = -ENOMEM;  		goto err_irq;  	}  	info->edev->name = DEV_NAME; -	info->edev->supported_cable = max77693_extcon_cable; -	ret = extcon_dev_register(info->edev, NULL); +	info->edev->dev.parent = &pdev->dev; + +	ret = devm_extcon_dev_register(&pdev->dev, info->edev);  	if (ret) {  		dev_err(&pdev->dev, "failed to register extcon device\n");  		goto err_irq;  	} -  	/* Initialize MUIC register by using platform data or default data */ -	if (pdata->muic_data) { +	if (pdata && pdata->muic_data) {  		init_data = pdata->muic_data->init_data;  		num_init_data = pdata->muic_data->num_init_data;  	} else { @@ -1188,7 +1200,7 @@ static int max77693_muic_probe(struct platform_device *pdev)  		num_init_data = ARRAY_SIZE(default_init_data);  	} -	for (i = 0 ; i < num_init_data ; i++) { +	for (i = 0; i < num_init_data; i++) {  		enum max77693_irq_source irq_src  				= MAX77693_IRQ_GROUP_NR; @@ -1213,8 +1225,9 @@ static int max77693_muic_probe(struct platform_device *pdev)  				= init_data[i].data;  	} -	if (pdata->muic_data) { -		struct max77693_muic_platform_data *muic_pdata = pdata->muic_data; +	if (pdata && pdata->muic_data) { +		struct max77693_muic_platform_data *muic_pdata +						   = pdata->muic_data;  		/*  		 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB @@ -1253,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev)  			MAX77693_MUIC_REG_ID, &id);  	if (ret < 0) {  		dev_err(&pdev->dev, "failed to read revision number\n"); -		goto err_extcon; +		goto err_irq;  	}  	dev_info(info->dev, "device ID : 0x%x\n", id); @@ -1269,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev)  	 * driver should notify cable state to upper layer.  	 */  	INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq); -	schedule_delayed_work(&info->wq_detcable, delay_jiffies); +	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, +			delay_jiffies);  	return ret; -err_extcon: -	extcon_dev_unregister(info->edev);  err_irq:  	while (--i >= 0)  		free_irq(muic_irqs[i].virq, info); @@ -1290,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev)  		free_irq(muic_irqs[i].virq, info);  	cancel_work_sync(&info->irq_work);  	input_unregister_device(info->dock); -	extcon_dev_unregister(info->edev);  	return 0;  } diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 67d6738d85a..d9f7f1baaa0 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -426,7 +426,8 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)  		break;  	case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:  	case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON: -		ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, attached); +		ret = max8997_muic_handle_usb(info, +					     MAX8997_USB_DEVICE, attached);  		if (ret < 0)  			return ret;  		break; @@ -504,7 +505,8 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)  		}  		break;  	case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: -		extcon_set_cable_state(info->edev, "Charge-downstream", attached); +		extcon_set_cable_state(info->edev, +				      "Charge-downstream", attached);  		break;  	case MAX8997_CHARGER_TYPE_DEDICATED_CHG:  		extcon_set_cable_state(info->edev, "TA", attached); @@ -537,7 +539,7 @@ static void max8997_muic_irq_work(struct work_struct *work)  	mutex_lock(&info->mutex); -	for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) +	for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)  		if (info->irq == muic_irqs[i].virq)  			irq_type = muic_irqs[i].irq; @@ -697,22 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev)  	}  	/* External connector */ -	info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), -				  GFP_KERNEL); -	if (!info->edev) { +	info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable); +	if (IS_ERR(info->edev)) {  		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");  		ret = -ENOMEM;  		goto err_irq;  	}  	info->edev->name = DEV_NAME; -	info->edev->supported_cable = max8997_extcon_cable; -	ret = extcon_dev_register(info->edev, NULL); +	info->edev->dev.parent = &pdev->dev; + +	ret = devm_extcon_dev_register(&pdev->dev, info->edev);  	if (ret) {  		dev_err(&pdev->dev, "failed to register extcon device\n");  		goto err_irq;  	} -	if (pdata->muic_pdata) { +	if (pdata && pdata->muic_pdata) {  		struct max8997_muic_platform_data *muic_pdata  			= pdata->muic_pdata; @@ -767,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev)  	 * driver should notify cable state to upper layer.  	 */  	INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq); -	schedule_delayed_work(&info->wq_detcable, delay_jiffies); +	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, +			delay_jiffies);  	return 0; @@ -786,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev)  		free_irq(muic_irqs[i].virq, info);  	cancel_work_sync(&info->irq_work); -	extcon_dev_unregister(info->edev); -  	return 0;  } diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 89fdd05c5fd..7417ce84eb2 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -23,6 +23,7 @@  #include <linux/module.h>  #include <linux/interrupt.h>  #include <linux/platform_device.h> +#include <linux/slab.h>  #include <linux/err.h>  #include <linux/mfd/palmas.h>  #include <linux/of.h> @@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)  	if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {  		if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {  			palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; -			extcon_set_cable_state(&palmas_usb->edev, "USB", true); +			extcon_set_cable_state(palmas_usb->edev, "USB", true);  			dev_info(palmas_usb->dev, "USB cable is attached\n");  		} else {  			dev_dbg(palmas_usb->dev, @@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)  	} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {  		if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {  			palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; -			extcon_set_cable_state(&palmas_usb->edev, "USB", false); +			extcon_set_cable_state(palmas_usb->edev, "USB", false);  			dev_info(palmas_usb->dev, "USB cable is detached\n");  		} else {  			dev_dbg(palmas_usb->dev, @@ -78,31 +79,40 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)  static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)  { -	unsigned int set; +	unsigned int set, id_src;  	struct palmas_usb *palmas_usb = _palmas_usb;  	palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,  		PALMAS_USB_ID_INT_LATCH_SET, &set); +	palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, +		PALMAS_USB_ID_INT_SRC, &id_src); -	if (set & PALMAS_USB_ID_INT_SRC_ID_GND) { +	if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) && +				(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {  		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,  			PALMAS_USB_ID_INT_LATCH_CLR,  			PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);  		palmas_usb->linkstat = PALMAS_USB_STATE_ID; -		extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); +		extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);  		dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); -	} else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) { +	} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && +				(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {  		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,  			PALMAS_USB_ID_INT_LATCH_CLR,  			PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);  		palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; -		extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); +		extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);  		dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");  	} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&  				(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {  		palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; -		extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); +		extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);  		dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); +	} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && +				(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { +		palmas_usb->linkstat = PALMAS_USB_STATE_ID; +		extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true); +		dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");  	}  	return IRQ_HANDLED; @@ -135,7 +145,7 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb)  static int palmas_usb_probe(struct platform_device *pdev)  {  	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); -	struct palmas_usb_platform_data	*pdata = pdev->dev.platform_data; +	struct palmas_usb_platform_data	*pdata = dev_get_platdata(&pdev->dev);  	struct device_node *node = pdev->dev.of_node;  	struct palmas_usb *palmas_usb;  	int status; @@ -177,12 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, palmas_usb); -	palmas_usb->edev.supported_cable = palmas_extcon_cable; -	palmas_usb->edev.mutually_exclusive = mutually_exclusive; +	palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev, +						    palmas_extcon_cable); +	if (IS_ERR(palmas_usb->edev)) { +		dev_err(&pdev->dev, "failed to allocate extcon device\n"); +		return -ENOMEM; +	} +	palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL); +	palmas_usb->edev->dev.parent = palmas_usb->dev; +	palmas_usb->edev->mutually_exclusive = mutually_exclusive; -	status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev); +	status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);  	if (status) {  		dev_err(&pdev->dev, "failed to register extcon device\n"); +		kfree(palmas_usb->edev->name);  		return status;  	} @@ -196,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev)  		if (status < 0) {  			dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",  					palmas_usb->id_irq, status); -			goto fail_extcon; +			kfree(palmas_usb->edev->name); +			return status;  		}  	} @@ -210,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev)  		if (status < 0) {  			dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",  					palmas_usb->vbus_irq, status); -			goto fail_extcon; +			kfree(palmas_usb->edev->name); +			return status;  		}  	}  	palmas_enable_irq(palmas_usb);  	device_set_wakeup_capable(&pdev->dev, true);  	return 0; - -fail_extcon: -	extcon_dev_unregister(&palmas_usb->edev); - -	return status;  }  static int palmas_usb_remove(struct platform_device *pdev)  {  	struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); -	extcon_dev_unregister(&palmas_usb->edev); +	kfree(palmas_usb->edev->name);  	return 0;  } @@ -261,14 +276,13 @@ static int palmas_usb_resume(struct device *dev)  };  #endif -static const struct dev_pm_ops palmas_pm_ops = { -	SET_SYSTEM_SLEEP_PM_OPS(palmas_usb_suspend, -				palmas_usb_resume) -}; +static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);  static struct of_device_id of_palmas_match_tbl[] = {  	{ .compatible = "ti,palmas-usb", }, +	{ .compatible = "ti,palmas-usb-vid", },  	{ .compatible = "ti,twl6035-usb", }, +	{ .compatible = "ti,twl6035-usb-vid", },  	{ /* end */ }  }; diff --git a/drivers/extcon/of_extcon.c b/drivers/extcon/of_extcon.c deleted file mode 100644 index 72173ecbb31..00000000000 --- a/drivers/extcon/of_extcon.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * OF helpers for External connector (extcon) framework - * - * Copyright (C) 2013 Texas Instruments, Inc. - * Kishon Vijay Abraham I <kishon@ti.com> - * - * Copyright (C) 2013 Samsung Electronics - * Chanwoo Choi <cw00.choi@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/extcon.h> -#include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/extcon/of_extcon.h> - -/* - * of_extcon_get_extcon_dev - Get the name of extcon device from devicetree - * @dev - instance to the given device - * @index - index into list of extcon_dev - * - * return the instance of extcon device - */ -struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index) -{ -	struct device_node *node; -	struct extcon_dev *edev; -	struct platform_device *extcon_parent_dev; - -	if (!dev->of_node) { -		dev_dbg(dev, "device does not have a device node entry\n"); -		return ERR_PTR(-EINVAL); -	} - -	node = of_parse_phandle(dev->of_node, "extcon", index); -	if (!node) { -		dev_dbg(dev, "failed to get phandle in %s node\n", -			dev->of_node->full_name); -		return ERR_PTR(-ENODEV); -	} - -	extcon_parent_dev = of_find_device_by_node(node); -	if (!extcon_parent_dev) { -		dev_dbg(dev, "unable to find device by node\n"); -		return ERR_PTR(-EPROBE_DEFER); -	} - -	edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev)); -	if (!edev) { -		dev_dbg(dev, "unable to get extcon device : %s\n", -				dev_name(&extcon_parent_dev->dev)); -		return ERR_PTR(-ENODEV); -	} - -	return edev; -} -EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev);  | 
