From ba4b27151ade5eab3d32a47bf323bfa8c5cd0726 Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Tue, 5 Jul 2016 19:26:21 +0530 Subject: [PATCH 01/23] extcon: adc-jack: update cable state during boot Update cable state during boot to avoid any missing external cable events occurred before driver initialisation. Signed-off-by: Venkat Reddy Talla Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-adc-jack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 44e48aa78a84..48dec94b487b 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -158,6 +158,7 @@ static int adc_jack_probe(struct platform_device *pdev) if (data->wakeup_source) device_init_wakeup(&pdev->dev, 1); + adc_jack_handler(&data->handler.work); return 0; } From e8752b7a7294a7c19545123eea079d873f8cc243 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 5 Jul 2016 11:57:05 -0700 Subject: [PATCH 02/23] extcon: Move extcon_get_edev_by_phandle() errors to dbg level Sometimes drivers may call this API and expect it to fail because the extcon they're looking for is optional. Let's move these prints to debug level so it doesn't look like there's a problem when there isn't one. Signed-off-by: Stephen Boyd Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 8682efc0f57b..319659c6af28 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -846,13 +846,13 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) return ERR_PTR(-EINVAL); if (!dev->of_node) { - dev_err(dev, "device does not have a device node entry\n"); + 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_err(dev, "failed to get phandle in %s node\n", + dev_dbg(dev, "failed to get phandle in %s node\n", dev->of_node->full_name); return ERR_PTR(-ENODEV); } From c19dc203e61886b4e61201832adc4da2816c5def Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 19 Jul 2016 13:23:56 +0100 Subject: [PATCH 03/23] extcon: arizona: Remove unneeded semi-colon There is no need for a semi-colon at the end of a switch statement so remove it. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 1d8e0a57bd51..be4d93d4c546 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -183,7 +183,7 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info, if (clamp) val = ARIZONA_RMV_SHRT_HP1L; break; - }; + } snd_soc_dapm_mutex_lock(arizona->dapm); From 5475e6317525b37241766c2e87731fd152e1150b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 1 Jul 2016 02:36:49 +0900 Subject: [PATCH 04/23] extcon: arizona: Remove the usage of extcon_update_state() This patch remvoes the usage of extcon_update_state() because the extcon_update_state() use directly the bit masking calculation to change the state of external connector without the unique id of external connector. It makes the code diffcult to read it. So, this patch uses the extcon_set_cable_state_() instead. Signed-off-by: Chanwoo Choi Acked-by: Charles Keepax --- drivers/extcon/extcon-arizona.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index be4d93d4c546..493bd9fe5f67 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -1149,10 +1149,13 @@ 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); - if (ret != 0) - dev_err(arizona->dev, "Removal report failed: %d\n", - ret); + for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) { + ret = extcon_set_cable_state_(info->edev, + arizona_cable[i], false); + if (ret != 0) + dev_err(arizona->dev, + "Removal report failed: %d\n", ret); + } regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, From a7da72eeec78b8ce08a99d132b3e269942b977eb Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 18 Jul 2016 16:16:29 +0900 Subject: [PATCH 05/23] extcon: adc-jack: Remove the usage of extcon_set_state() This patch removes the usage of extcon_set_state() because it uses the bit masking to change the state of external connectors. The extcon framework should handle the state by extcon_set/get_cable_state_() with extcon id. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-adc-jack.c | 26 ++++++++++++++------------ include/linux/extcon/extcon-adc-jack.h | 4 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 48dec94b487b..e62e6cd7e00a 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -3,6 +3,9 @@ * * Analog Jack extcon driver with ADC-based detection capability. * + * Copyright (C) 2016 Samsung Electronics + * Chanwoo Choi + * * Copyright (C) 2012 Samsung Electronics * MyungJoo Ham * @@ -58,7 +61,7 @@ static void adc_jack_handler(struct work_struct *work) struct adc_jack_data *data = container_of(to_delayed_work(work), struct adc_jack_data, handler); - u32 state = 0; + struct adc_jack_cond *def; int ret, adc_val; int i; @@ -70,17 +73,18 @@ static void adc_jack_handler(struct work_struct *work) /* Get state from adc value with adc_conditions */ for (i = 0; i < data->num_conditions; i++) { - struct adc_jack_cond *def = &data->adc_conditions[i]; - if (!def->state) - break; + def = &data->adc_conditions[i]; if (def->min_adc <= adc_val && def->max_adc >= adc_val) { - state = def->state; - break; + extcon_set_cable_state_(data->edev, def->id, true); + return; } } - /* if no def has met, it means state = 0 (no cables attached) */ - extcon_set_state(data->edev, state); + /* Set the detached state if adc value is not included in the range */ + for (i = 0; i < data->num_conditions; i++) { + def = &data->adc_conditions[i]; + extcon_set_cable_state_(data->edev, def->id, false); + } } static irqreturn_t adc_jack_irq_thread(int irq, void *_data) @@ -114,16 +118,14 @@ static int adc_jack_probe(struct platform_device *pdev) return -ENOMEM; } - if (!pdata->adc_conditions || - !pdata->adc_conditions[0].state) { + if (!pdata->adc_conditions) { dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); return -EINVAL; } data->adc_conditions = pdata->adc_conditions; /* Check the length of array and set num_conditions */ - for (i = 0; data->adc_conditions[i].state; i++) - ; + for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++); data->num_conditions = i; data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h index ac85f2061351..a0e03b13b449 100644 --- a/include/linux/extcon/extcon-adc-jack.h +++ b/include/linux/extcon/extcon-adc-jack.h @@ -20,8 +20,8 @@ /** * struct adc_jack_cond - condition to use an extcon state - * @state: the corresponding extcon state (if 0, this struct * denotes the last adc_jack_cond element among the array) + * @id: the unique id of each external connector * @min_adc: min adc value for this condition * @max_adc: max adc value for this condition * @@ -33,7 +33,7 @@ * because when no adc_jack_cond is met, state = 0 is automatically chosen. */ struct adc_jack_cond { - u32 state; /* extcon state value. 0 if invalid */ + unsigned int id; u32 min_adc; u32 max_adc; }; From 7575591c2db8cbf4ba543cb9bcb6a4cdd5a7aa97 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 21 Jul 2016 20:00:29 +0900 Subject: [PATCH 06/23] extcon: gpio: Remove the usage of extcon_set_state() This patch removes the usage of extcon_set_state() because it uses the bit masking to change the state of external connectors. The extcon framework should handle the state by extcon_set_cable_state_() with extcon id. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index d023789f0fda..b1b0264eb12d 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -49,7 +49,7 @@ static void gpio_extcon_work(struct work_struct *work) state = gpiod_get_value_cansleep(data->id_gpiod); if (data->pdata->gpio_active_low) state = !state; - extcon_set_state(data->edev, state); + extcon_set_cable_state_(data->edev, data->pdata->extcon_id, state); } static irqreturn_t gpio_irq_handler(int irq, void *dev_id) From 5d5321e90a69881a6e92604b73f7c8e8ca473373 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 18 Jul 2016 15:39:28 +0900 Subject: [PATCH 07/23] extcon: Remove the state_store() to prevent the wrong access This patch removes the state_store() which change the state of external connectors with bit masking on user-space. It is wrong access to modify the change the state of external connectors. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 319659c6af28..ad5e4546f82c 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -174,26 +174,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, return count; } - -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 = dev_get_drvdata(dev); - - ret = sscanf(buf, "0x%x", &state); - if (ret == 0) - ret = -EINVAL; - else - ret = extcon_set_state(edev, state); - - if (ret < 0) - return ret; - - return count; -} -static DEVICE_ATTR_RW(state); +static DEVICE_ATTR_RO(state); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) From 912465bcf869660900cf77c4761869048f3ff063 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 1 Jul 2016 02:41:18 +0900 Subject: [PATCH 08/23] extcon: Block the bit masking operation for cable state except for extcon core This patch restrict the usage of extcon_update_state() in the extcon core because the extcon_update_state() use the bit masking to change the state of external connector. When this function is used in device drivers, it may occur the probelm with the handling mistake of bit masking. Also, this patch removes the extcon_get/set_state() functions because these functions use the bit masking which is reluctant way. Instead, extcon provides the extcon_set/get_cable_state_() functions. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 20 +------------------- include/linux/extcon.h | 30 ------------------------------ 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index ad5e4546f82c..5b61000cde26 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -224,7 +224,7 @@ static ssize_t cable_state_show(struct device *dev, * Note that the notifier provides which bits are changed in the state * variable with the val parameter (second) to the callback. */ -int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) +static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) { char name_buf[120]; char state_buf[120]; @@ -300,24 +300,6 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) return 0; } -EXPORT_SYMBOL_GPL(extcon_update_state); - -/** - * extcon_set_state() - Set the cable attach states of the extcon device. - * @edev: the extcon device - * @state: new cable attach status for @edev - * - * Note that notifier provides which bits are changed in the state - * variable with the val parameter (second) to the callback. - */ -int extcon_set_state(struct extcon_dev *edev, u32 state) -{ - if (!edev) - return -EINVAL; - - return extcon_update_state(edev, 0xffffffff, state); -} -EXPORT_SYMBOL_GPL(extcon_set_state); /** * extcon_get_cable_state_() - Get the status of a specific cable. diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 61004413dc64..667b1d35af12 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -149,20 +149,6 @@ extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, const unsigned int *cable); extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); -/* - * get/set/update_state access the 32b encoded state value, which represents - * states of all possible cables of the multistate port. For example, if one - * calls extcon_set_state(edev, 0x7), it may mean that all the three cables - * are attached to the port. - */ -static inline u32 extcon_get_state(struct extcon_dev *edev) -{ - return edev->state; -} - -extern int extcon_set_state(struct extcon_dev *edev, u32 state); -extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); - /* * get/set_cable_state access each bit of the 32b encoded state value. * They are used to access the status of each cable based on the cable id. @@ -232,22 +218,6 @@ static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, static inline void devm_extcon_dev_free(struct extcon_dev *edev) { } -static inline u32 extcon_get_state(struct extcon_dev *edev) -{ - return 0; -} - -static inline int extcon_set_state(struct extcon_dev *edev, u32 state) -{ - return 0; -} - -static inline int extcon_update_state(struct extcon_dev *edev, u32 mask, - u32 state) -{ - return 0; -} - static inline int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id) { From 2c8116a1110a93f298b10339d773fb055a0c04d7 Mon Sep 17 00:00:00 2001 From: Maninder Singh Date: Mon, 1 Aug 2016 14:51:30 +0530 Subject: [PATCH 09/23] extcon: Fix compile time warning This patch fixes below compilation warning:- drivers/extcon/extcon.c: In function extcon_register_notifier: drivers/extcon/extcon.c:455:6: warning: idx may be used uninitialized in this function [-Wmaybe-uninitialized] if (idx >= 0) { Signed-off-by: Vaneet Narang Signed-off-by: Maninder Singh [cw00.choi : Modify the patch title using the a captical letter for first char] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 5b61000cde26..9a266e5c7e10 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -391,7 +391,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb) { unsigned long flags; - int ret, idx; + int ret, idx = -EINVAL; if (!nb) return -EINVAL; From 55e4e2f129c6664c14166a30f4e0e933ebb61d9b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 11 Jul 2016 16:34:52 +0900 Subject: [PATCH 10/23] extcon: Add the extcon_type to gather each connector into five category This patch adds the new extcon type to group the each connecotr into following five category. This type would be used to handle the connectors as a group unit instead of a connector unit. - EXTCON_TYPE_USB : USB connector - EXTCON_TYPE_CHG : Charger connector - EXTCON_TYPE_JACK : Jack connector - EXTCON_TYPE_DISP : Display connector - EXTCON_TYPE_MISC : Miscellaneous connector Also, each external connector is possible to belong to one more extcon type. In caes of EXTCON_CHG_USB_SDP, it have the EXTCON_TYPE_CHG and EXTCON_TYPE_USB. Signed-off-by: Chanwoo Choi Tested-by: Chris Zhong Tested-by: Guenter Roeck Signed-off-by: MyungJoo Ham Reviewed-by: Guenter Roeck --- drivers/extcon/extcon.c | 157 +++++++++++++++++++++++++++++++++------- include/linux/extcon.h | 9 +++ 2 files changed, 138 insertions(+), 28 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 9a266e5c7e10..f209a6959fed 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -38,43 +38,144 @@ #define SUPPORTED_CABLE_MAX 32 #define CABLE_NAME_MAX 30 -static const char *extcon_name[] = { - [EXTCON_NONE] = "NONE", +struct __extcon_info { + unsigned int type; + unsigned int id; + const char *name; + +} extcon_info[] = { + [EXTCON_NONE] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_NONE, + .name = "NONE", + }, /* USB external connector */ - [EXTCON_USB] = "USB", - [EXTCON_USB_HOST] = "USB-HOST", + [EXTCON_USB] = { + .type = EXTCON_TYPE_USB, + .id = EXTCON_USB, + .name = "USB", + }, + [EXTCON_USB_HOST] = { + .type = EXTCON_TYPE_USB, + .id = EXTCON_USB_HOST, + .name = "USB_HOST", + }, /* Charging external connector */ - [EXTCON_CHG_USB_SDP] = "SDP", - [EXTCON_CHG_USB_DCP] = "DCP", - [EXTCON_CHG_USB_CDP] = "CDP", - [EXTCON_CHG_USB_ACA] = "ACA", - [EXTCON_CHG_USB_FAST] = "FAST-CHARGER", - [EXTCON_CHG_USB_SLOW] = "SLOW-CHARGER", + [EXTCON_CHG_USB_SDP] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_SDP, + .name = "SDP", + }, + [EXTCON_CHG_USB_DCP] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_DCP, + .name = "DCP", + }, + [EXTCON_CHG_USB_CDP] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_CDP, + .name = "CDP", + }, + [EXTCON_CHG_USB_ACA] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_ACA, + .name = "ACA", + }, + [EXTCON_CHG_USB_FAST] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_FAST, + .name = "FAST-CHARGER", + }, + [EXTCON_CHG_USB_SLOW] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_SLOW, + .name = "SLOW-CHARGER", + }, /* Jack external connector */ - [EXTCON_JACK_MICROPHONE] = "MICROPHONE", - [EXTCON_JACK_HEADPHONE] = "HEADPHONE", - [EXTCON_JACK_LINE_IN] = "LINE-IN", - [EXTCON_JACK_LINE_OUT] = "LINE-OUT", - [EXTCON_JACK_VIDEO_IN] = "VIDEO-IN", - [EXTCON_JACK_VIDEO_OUT] = "VIDEO-OUT", - [EXTCON_JACK_SPDIF_IN] = "SPDIF-IN", - [EXTCON_JACK_SPDIF_OUT] = "SPDIF-OUT", + [EXTCON_JACK_MICROPHONE] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_MICROPHONE, + .name = "MICROPHONE", + }, + [EXTCON_JACK_HEADPHONE] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_HEADPHONE, + .name = "HEADPHONE", + }, + [EXTCON_JACK_LINE_IN] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_LINE_IN, + .name = "LINE-IN", + }, + [EXTCON_JACK_LINE_OUT] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_LINE_OUT, + .name = "LINE-OUT", + }, + [EXTCON_JACK_VIDEO_IN] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_VIDEO_IN, + .name = "VIDEO-IN", + }, + [EXTCON_JACK_VIDEO_OUT] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_VIDEO_OUT, + .name = "VIDEO-OUT", + }, + [EXTCON_JACK_SPDIF_IN] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_SPDIF_IN, + .name = "SPDIF-IN", + }, + [EXTCON_JACK_SPDIF_OUT] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_SPDIF_OUT, + .name = "SPDIF-OUT", + }, /* Display external connector */ - [EXTCON_DISP_HDMI] = "HDMI", - [EXTCON_DISP_MHL] = "MHL", - [EXTCON_DISP_DVI] = "DVI", - [EXTCON_DISP_VGA] = "VGA", + [EXTCON_DISP_HDMI] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_HDMI, + .name = "HDMI", + }, + [EXTCON_DISP_MHL] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_MHL, + .name = "MHL", + }, + [EXTCON_DISP_DVI] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_DVI, + .name = "DVI", + }, + [EXTCON_DISP_VGA] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_VGA, + .name = "VGA", + }, /* Miscellaneous external connector */ - [EXTCON_DOCK] = "DOCK", - [EXTCON_JIG] = "JIG", - [EXTCON_MECHANICAL] = "MECHANICAL", + [EXTCON_DOCK] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_DOCK, + .name = "DOCK", + }, + [EXTCON_JIG] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_JIG, + .name = "JIG", + }, + [EXTCON_MECHANICAL] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_MECHANICAL, + .name = "MECHANICAL", + }, - NULL, + { /* sentinel */ } }; /** @@ -168,7 +269,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, for (i = 0; i < edev->max_supported; i++) { count += sprintf(buf + count, "%s=%d\n", - extcon_name[edev->supported_cable[i]], + extcon_info[edev->supported_cable[i]].name, !!(edev->state & (1 << i))); } @@ -193,7 +294,7 @@ static ssize_t cable_name_show(struct device *dev, int i = cable->cable_index; return sprintf(buf, "%s\n", - extcon_name[cable->edev->supported_cable[i]]); + extcon_info[cable->edev->supported_cable[i]].name); } static ssize_t cable_state_show(struct device *dev, diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 667b1d35af12..46d802892c82 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -28,6 +28,15 @@ #include +/* + * Define the type of supported external connectors + */ +#define EXTCON_TYPE_USB BIT(0) /* USB connector */ +#define EXTCON_TYPE_CHG BIT(1) /* Charger connector */ +#define EXTCON_TYPE_JACK BIT(2) /* Jack connector */ +#define EXTCON_TYPE_DISP BIT(3) /* Display connector */ +#define EXTCON_TYPE_MISC BIT(4) /* Miscellaneous connector */ + /* * Define the unique id of supported external connectors */ From 792e7e9e5d4358bc6157152b2c07b94eb9e261b0 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 11 Jul 2016 19:30:43 +0900 Subject: [PATCH 11/23] extcon: Add the support for extcon property according to extcon type This patch support the extcon property for the external connector because each external connector might have the property according to the H/W design and the specific characteristics. - EXTCON_PROP_USB_[property name] - EXTCON_PROP_CHG_[property name] - EXTCON_PROP_JACK_[property name] - EXTCON_PROP_DISP_[property name] Add the new extcon APIs to get/set the property value as following: - int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value *prop_val) - int extcon_set_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value prop_val) Signed-off-by: Chanwoo Choi Tested-by: Chris Zhong Tested-by: Guenter Roeck Reviewed-by: Guenter Roeck --- drivers/extcon/extcon.c | 201 +++++++++++++++++++++++++++++++++++++++- include/linux/extcon.h | 81 ++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index f209a6959fed..37fa4b51c5a1 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -196,6 +196,11 @@ struct extcon_cable { struct device_attribute attr_state; struct attribute *attrs[3]; /* to be fed to attr_g.attrs */ + + union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT]; + union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; + union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; + union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; }; static struct class *extcon_class; @@ -248,6 +253,27 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id return -EINVAL; } +static int get_extcon_type(unsigned int prop) +{ + switch (prop) { + case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: + return EXTCON_TYPE_USB; + case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: + return EXTCON_TYPE_CHG; + case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: + return EXTCON_TYPE_JACK; + case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: + return EXTCON_TYPE_DISP; + default: + return -EINVAL; + } +} + +static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index) +{ + return !!(edev->state & BIT(index)); +} + static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached) { if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) { @@ -258,6 +284,34 @@ static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached) return false; } +static bool is_extcon_property_supported(unsigned int id, unsigned int prop) +{ + int type; + + /* Check whether the property is supported or not. */ + type = get_extcon_type(prop); + if (type < 0) + return false; + + /* Check whether a specific extcon id supports the property or not. */ + return !!(extcon_info[id].type & type); +} + +static void init_property(struct extcon_dev *edev, unsigned int id, int index) +{ + unsigned int type = extcon_info[id].type; + struct extcon_cable *cable = &edev->cables[index]; + + if (EXTCON_TYPE_USB & type) + memset(cable->usb_propval, 0, sizeof(cable->usb_propval)); + if (EXTCON_TYPE_CHG & type) + memset(cable->chg_propval, 0, sizeof(cable->chg_propval)); + if (EXTCON_TYPE_JACK & type) + memset(cable->jack_propval, 0, sizeof(cable->jack_propval)); + if (EXTCON_TYPE_DISP & type) + memset(cable->disp_propval, 0, sizeof(cable->disp_propval)); +} + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -421,7 +475,7 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id) if (edev->max_supported && edev->max_supported <= index) return -EINVAL; - return !!(edev->state & (1 << index)); + return is_extcon_attached(edev, index); } EXPORT_SYMBOL_GPL(extcon_get_cable_state_); @@ -449,11 +503,156 @@ int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, if (edev->max_supported && edev->max_supported <= index) return -EINVAL; + /* + * Initialize the value of extcon property before setting + * the detached state for an external connector. + */ + if (!cable_state) + init_property(edev, id, index); + state = cable_state ? (1 << index) : 0; return extcon_update_state(edev, 1 << index, state); } EXPORT_SYMBOL_GPL(extcon_set_cable_state_); +/** + * extcon_get_property() - Get the property value of a specific cable. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * @prop_val: the pointer which store the value of property. + * + * When getting the property value of external connector, the external connector + * should be attached. If detached state, function just return 0 without + * property value. Also, the each property should be included in the list of + * supported properties according to the type of external connectors. + * + * Returns 0 if success or error number if fail + */ +int extcon_get_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value *prop_val) +{ + struct extcon_cable *cable; + unsigned long flags; + int index, ret = 0; + + *prop_val = (union extcon_property_value)(0); + + if (!edev) + return -EINVAL; + + /* Check whether the property is supported or not */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + spin_lock_irqsave(&edev->lock, flags); + + /* + * Check whether the external connector is attached. + * If external connector is detached, the user can not + * get the property value. + */ + if (!is_extcon_attached(edev, index)) { + spin_unlock_irqrestore(&edev->lock, flags); + return 0; + } + + cable = &edev->cables[index]; + + /* Get the property value according to extcon type */ + switch (prop) { + case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: + *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN]; + break; + case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: + *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN]; + break; + case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: + *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN]; + break; + case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: + *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN]; + break; + default: + ret = -EINVAL; + break; + } + + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_get_property); + +/** + * extcon_set_property() - Set the property value of a specific cable. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * @prop_val: the pointer including the new value of property. + * + * The each property should be included in the list of supported properties + * according to the type of external connectors. + * + * Returns 0 if success or error number if fail + */ +int extcon_set_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val) +{ + struct extcon_cable *cable; + unsigned long flags; + int index, ret = 0; + + if (!edev) + return -EINVAL; + + /* Check whether the property is supported or not */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + spin_lock_irqsave(&edev->lock, flags); + + cable = &edev->cables[index]; + + /* Set the property value according to extcon type */ + switch (prop) { + case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: + cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val; + break; + case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: + cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val; + break; + case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: + cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val; + break; + case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: + cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val; + break; + default: + ret = -EINVAL; + break; + } + + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_set_property); + /** * extcon_get_extcon_dev() - Get the extcon device instance from the name * @extcon_name: The extcon name provided with extcon_dev_register() diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 46d802892c82..f9d4a44e86d3 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -77,6 +77,63 @@ #define EXTCON_NUM 63 +/* + * Define the property of supported external connectors. + * + * When adding the new extcon property, they *must* have + * the type/value/default information. Also, you *have to* + * modify the EXTCON_PROP_[type]_START/END definitions + * which mean the range of the supported properties + * for each extcon type. + * + * The naming style of property + * : EXTCON_PROP_[type]_[property name] + * + * EXTCON_PROP_USB_[property name] : USB property + * EXTCON_PROP_CHG_[property name] : Charger property + * EXTCON_PROP_JACK_[property name] : Jack property + * EXTCON_PROP_DISP_[property name] : Display property + */ + +/* + * Properties of EXTCON_TYPE_USB. + * + * - EXTCON_PROP_USB_VBUS + * @type: integer (intval) + * @value: 0 (low) or 1 (high) + * @default: 0 (low) + */ +#define EXTCON_PROP_USB_VBUS 0 + +#define EXTCON_PROP_USB_MIN 0 +#define EXTCON_PROP_USB_MAX 0 +#define EXTCON_PROP_USB_CNT (EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1) + +/* Properties of EXTCON_TYPE_CHG. */ +#define EXTCON_PROP_CHG_MIN 50 +#define EXTCON_PROP_CHG_MAX 50 +#define EXTCON_PROP_CHG_CNT (EXTCON_PROP_CHG_MAX - EXTCON_PROP_CHG_MIN + 1) + +/* Properties of EXTCON_TYPE_JACK. */ +#define EXTCON_PROP_JACK_MIN 100 +#define EXTCON_PROP_JACK_MAX 100 +#define EXTCON_PROP_JACK_CNT (EXTCON_PROP_JACK_MAX - EXTCON_PROP_JACK_MIN + 1) + +/* Properties of EXTCON_TYPE_DISP. */ +#define EXTCON_PROP_DISP_MIN 150 +#define EXTCON_PROP_DISP_MAX 150 +#define EXTCON_PROP_DISP_CNT (EXTCON_PROP_DISP_MAX - EXTCON_PROP_DISP_MIN + 1) + +/* + * Define the type of property's value. + * + * Define the property's value as union type. Because each property + * would need the different data type to store it. + */ +union extcon_property_value { + int intval; /* type : integer (intval) */ +}; + struct extcon_cable; /** @@ -166,6 +223,17 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id); extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, bool cable_state); +/* + * get/set_property access the property value of each external connector. + * They are used to access the property of each cable based on the property id. + */ +extern int extcon_get_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value *prop_val); +extern int extcon_set_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val); + /* * Following APIs are to monitor every action of a notifier. * Registrar gets notified for every external port of a connection device. @@ -239,6 +307,19 @@ static inline int extcon_set_cable_state_(struct extcon_dev *edev, return 0; } +static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value *prop_val) +{ + return 0; +} +static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val) +{ + return 0; +} + static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) { return NULL; From 7f2a0a1699b51bfd738f1e0b15e057996fe1f259 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 25 Jul 2016 21:15:19 +0900 Subject: [PATCH 12/23] extcon: Add the support for the capability of each property This patch adds the support of the property capability setting. This function decides the supported properties of each external connector on extcon provider driver. Ths list of new extcon APIs to get/set the capability of property as following: - int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop); - int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop); Signed-off-by: Chanwoo Choi Tested-by: Chris Zhong Tested-by: Guenter Roeck Reviewed-by: Guenter Roeck --- drivers/extcon/extcon.c | 140 ++++++++++++++++++++++++++++++++++++++++ include/linux/extcon.h | 22 +++++++ 2 files changed, 162 insertions(+) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 37fa4b51c5a1..599fc377a965 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -201,6 +201,11 @@ struct extcon_cable { union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; + + unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)]; + unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)]; + unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)]; + unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)]; }; static struct class *extcon_class; @@ -297,6 +302,39 @@ static bool is_extcon_property_supported(unsigned int id, unsigned int prop) return !!(extcon_info[id].type & type); } +static int is_extcon_property_capability(struct extcon_dev *edev, + unsigned int id, int index,unsigned int prop) +{ + struct extcon_cable *cable; + int type, ret; + + /* Check whether the property is supported or not. */ + type = get_extcon_type(prop); + if (type < 0) + return type; + + cable = &edev->cables[index]; + + switch (type) { + case EXTCON_TYPE_USB: + ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); + break; + case EXTCON_TYPE_CHG: + ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); + break; + case EXTCON_TYPE_JACK: + ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); + break; + case EXTCON_TYPE_DISP: + ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); + break; + default: + ret = -EINVAL; + } + + return ret; +} + static void init_property(struct extcon_dev *edev, unsigned int id, int index) { unsigned int type = extcon_info[id].type; @@ -554,6 +592,12 @@ int extcon_get_property(struct extcon_dev *edev, unsigned int id, spin_lock_irqsave(&edev->lock, flags); + /* Check whether the property is available or not. */ + if (!is_extcon_property_capability(edev, id, index, prop)) { + spin_unlock_irqrestore(&edev->lock, flags); + return -EPERM; + } + /* * Check whether the external connector is attached. * If external connector is detached, the user can not @@ -626,6 +670,12 @@ int extcon_set_property(struct extcon_dev *edev, unsigned int id, spin_lock_irqsave(&edev->lock, flags); + /* Check whether the property is available or not. */ + if (!is_extcon_property_capability(edev, id, index, prop)) { + spin_unlock_irqrestore(&edev->lock, flags); + return -EPERM; + } + cable = &edev->cables[index]; /* Set the property value according to extcon type */ @@ -653,6 +703,96 @@ int extcon_set_property(struct extcon_dev *edev, unsigned int id, } EXPORT_SYMBOL_GPL(extcon_set_property); +/** + * extcon_get_property_capability() - Get the capability of property + * of an external connector. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * + * Returns 1 if the property is available or 0 if not available. + */ +int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, + unsigned int prop) +{ + int index; + + if (!edev) + return -EINVAL; + + /* Check whether the property is supported or not */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + return is_extcon_property_capability(edev, id, index, prop); +} +EXPORT_SYMBOL_GPL(extcon_get_property_capability); + +/** + * extcon_set_property_capability() - Set the capability of a property + * of an external connector. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * + * This function set the capability of a property for an external connector + * to mark the bit in capability bitmap which mean the available state of + * a property. + * + * Returns 0 if success or error number if fail + */ +int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, + unsigned int prop) +{ + struct extcon_cable *cable; + int index, type, ret = 0; + + if (!edev) + return -EINVAL; + + /* Check whether the property is supported or not. */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id. */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + type = get_extcon_type(prop); + if (type < 0) + return type; + + cable = &edev->cables[index]; + + switch (type) { + case EXTCON_TYPE_USB: + __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); + break; + case EXTCON_TYPE_CHG: + __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); + break; + case EXTCON_TYPE_JACK: + __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); + break; + case EXTCON_TYPE_DISP: + __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); + break; + default: + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_set_property_capability); + /** * extcon_get_extcon_dev() - Get the extcon device instance from the name * @extcon_name: The extcon name provided with extcon_dev_register() diff --git a/include/linux/extcon.h b/include/linux/extcon.h index f9d4a44e86d3..f08469089f74 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -234,6 +234,16 @@ extern int extcon_set_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value prop_val); +/* + * get/set_property_capability set the capability of the property for each + * external connector. They are used to set the capability of the property + * of each external connector based on the id and property. + */ +extern int extcon_get_property_capability(struct extcon_dev *edev, + unsigned int id, unsigned int prop); +extern int extcon_set_property_capability(struct extcon_dev *edev, + unsigned int id, unsigned int prop); + /* * Following APIs are to monitor every action of a notifier. * Registrar gets notified for every external port of a connection device. @@ -320,6 +330,18 @@ static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id, return 0; } +static inline int extcon_get_property_capability(struct extcon_dev *edev, + unsigned int id, unsigned int prop) +{ + return 0; +} + +static inline int extcon_set_property_capability(struct extcon_dev *edev, + unsigned int id, unsigned int prop) +{ + return 0; +} + static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) { return NULL; From 575c2b867ee0c2affdd309f375c032c0c7dc219c Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 22 Jul 2016 13:03:17 +0900 Subject: [PATCH 13/23] extcon: Rename the extcon_set/get_state() to maintain the function naming pattern This patch just renames the existing extcon_get/set_cable_state_() as following because of maintaining the function naming pattern like as extcon APIs for property. - extcon_set_cable_state_() -> extcon_set_state() - extcon_get_cable_state_() -> extcon_get_state() But, this patch remains the old extcon_set/get_cable_state_() functions to prevent the build break. After altering new APIs, remove the old APIs. Signed-off-by: Chanwoo Choi Tested-by: Chris Zhong Tested-by: Guenter Roeck Reviewed-by: Guenter Roeck --- drivers/extcon/extcon.c | 28 +++++++++++++--------------- include/linux/extcon.h | 25 ++++++++++++++++++------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 599fc377a965..d66adfd21159 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -398,8 +398,7 @@ static ssize_t cable_state_show(struct device *dev, int i = cable->cable_index; return sprintf(buf, "%d\n", - extcon_get_cable_state_(cable->edev, - cable->edev->supported_cable[i])); + extcon_get_state(cable->edev, cable->edev->supported_cable[i])); } /** @@ -495,13 +494,14 @@ static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) } /** - * extcon_get_cable_state_() - Get the status of a specific cable. + * extcon_get_state() - Get the state of a external connector. * @edev: the extcon device that has the cable. * @id: the unique id of each external connector in extcon enumeration. */ -int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id) +int extcon_get_state(struct extcon_dev *edev, const unsigned int id) { - int index; + int index, state; + unsigned long flags; if (!edev) return -EINVAL; @@ -510,22 +510,23 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id) if (index < 0) return index; - if (edev->max_supported && edev->max_supported <= index) - return -EINVAL; + spin_lock_irqsave(&edev->lock, flags); + state = is_extcon_attached(edev, index); + spin_unlock_irqrestore(&edev->lock, flags); - return is_extcon_attached(edev, index); + return state; } -EXPORT_SYMBOL_GPL(extcon_get_cable_state_); +EXPORT_SYMBOL_GPL(extcon_get_state); /** - * extcon_set_cable_state_() - Set the status of a specific cable. + * extcon_set_state() - Set the state of a external connector. * @edev: the extcon device that has the cable. * @id: the unique id of each external connector * in extcon enumeration. * @state: the new cable status. The default semantics is * true: attached / false: detached. */ -int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, +int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool cable_state) { u32 state; @@ -538,9 +539,6 @@ int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, if (index < 0) return index; - if (edev->max_supported && edev->max_supported <= index) - return -EINVAL; - /* * Initialize the value of extcon property before setting * the detached state for an external connector. @@ -551,7 +549,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, state = cable_state ? (1 << index) : 0; return extcon_update_state(edev, 1 << index, state); } -EXPORT_SYMBOL_GPL(extcon_set_cable_state_); +EXPORT_SYMBOL_GPL(extcon_set_state); /** * extcon_get_property() - Get the property value of a specific cable. diff --git a/include/linux/extcon.h b/include/linux/extcon.h index f08469089f74..4fa37385c97a 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -216,11 +216,11 @@ extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); /* - * get/set_cable_state access each bit of the 32b encoded state value. + * get/set_state access each bit of the 32b encoded state value. * They are used to access the status of each cable based on the cable id. */ -extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id); -extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, +extern int extcon_get_state(struct extcon_dev *edev, unsigned int id); +extern int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool cable_state); /* @@ -305,14 +305,14 @@ static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, static inline void devm_extcon_dev_free(struct extcon_dev *edev) { } -static inline int extcon_get_cable_state_(struct extcon_dev *edev, - unsigned int id) + +static inline int extcon_get_state(struct extcon_dev *edev, unsigned int id) { return 0; } -static inline int extcon_set_cable_state_(struct extcon_dev *edev, - unsigned int id, bool cable_state) +static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id, + bool cable_state) { return 0; } @@ -402,4 +402,15 @@ static inline int extcon_unregister_interest(struct extcon_specific_cable_nb { return -EINVAL; } + +static inline int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id) +{ + return extcon_get_state(edev, id); +} + +static inline int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, + bool cable_state) +{ + return extcon_set_state(edev, id, cable_state); +} #endif /* __LINUX_EXTCON_H__ */ From ab11af049f88f059a73f679fb050bd7abb98d24b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 22 Jul 2016 13:16:34 +0900 Subject: [PATCH 14/23] extcon: Add the synchronization extcon APIs to support the notification This patch adds the synchronization extcon APIs to support the notifications for both state and property. When extcon_*_sync() functions is called, the extcon informs the information from extcon provider to extcon client. The extcon driver may need to change the both state and multiple properties at the same time. After setting the data of a external connector, the extcon send the notification to client driver with the extcon_*_sync(). The list of new extcon APIs as following: - extcon_sync() : Send the notification for each external connector to synchronize the information between extcon provider driver and extcon client driver. - extcon_set_state_sync() : Set the state of external connector with noti. - extcon_set_property_sync() : Set the property of external connector with noti. For example, case 1, change the state of external connector and synchronized the data. extcon_set_state_sync(edev, EXTCON_USB, 1); case 2, change both the state and property of external connector and synchronized the data. extcon_set_state(edev, EXTCON_USB, 1); extcon_set_property(edev, EXTCON_USB, EXTCON_PROP_USB_VBUS 1); extcon_sync(edev, EXTCON_USB); case 3, change the property of external connector and synchronized the data. extcon_set_property(edev, EXTCON_USB, EXTCON_PROP_USB_VBUS, 0); extcon_sync(edev, EXTCON_USB); case 4, change the property of external connector and synchronized the data. extcon_set_property_sync(edev, EXTCON_USB, EXTCON_PROP_USB_VBUS, 0); Signed-off-by: Chanwoo Choi Tested-by: Chris Zhong Tested-by: Guenter Roeck Reviewed-by: Guenter Roeck --- drivers/extcon/extcon.c | 216 +++++++++++++++++++++++++--------------- include/linux/extcon.h | 30 +++++- 2 files changed, 167 insertions(+), 79 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index d66adfd21159..8fde4befaa51 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -279,14 +279,11 @@ static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index) return !!(edev->state & BIT(index)); } -static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached) +static bool is_extcon_changed(struct extcon_dev *edev, int index, + bool new_state) { - if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) { - *attached = ((new >> idx) & 0x1) ? true : false; - return true; - } - - return false; + int state = !!(edev->state & BIT(index)); + return (state != new_state); } static bool is_extcon_property_supported(unsigned int id, unsigned int prop) @@ -402,21 +399,13 @@ 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. - * @edev: the extcon device - * @mask: the bit mask to designate updated bits. - * @state: new cable attach status for @edev + * extcon_sync() - Synchronize the states for both the attached/detached + * @edev: the extcon device that has the cable. * - * Changing the state sends uevent with environment variable containing - * the name of extcon device (envp[0]) and the state output (envp[1]). - * Tizen uses this format for extcon device to get events from ports. - * Android uses this format as well. - * - * Note that the notifier provides which bits are changed in the state - * variable with the val parameter (second) to the callback. + * This function send a notification to synchronize the all states of a + * specific external connector */ -static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) +int extcon_sync(struct extcon_dev *edev, unsigned int id) { char name_buf[120]; char state_buf[120]; @@ -425,73 +414,58 @@ static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) int env_offset = 0; int length; int index; + int state; unsigned long flags; - bool attached; if (!edev) return -EINVAL; + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + spin_lock_irqsave(&edev->lock, flags); - if (edev->state != ((edev->state & ~mask) | (state & mask))) { - u32 old_state; + state = !!(edev->state & BIT(index)); + raw_notifier_call_chain(&edev->nh[index], state, edev); - if (check_mutually_exclusive(edev, (edev->state & ~mask) | - (state & mask))) { - spin_unlock_irqrestore(&edev->lock, flags); - return -EPERM; - } - - old_state = edev->state; - edev->state &= ~mask; - edev->state |= state & mask; - - for (index = 0; index < edev->max_supported; index++) { - if (is_extcon_changed(old_state, edev->state, index, - &attached)) - raw_notifier_call_chain(&edev->nh[index], - attached, 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); - if (length > 0) { - if (prop_buf[length - 1] == '\n') - prop_buf[length - 1] = 0; - snprintf(name_buf, sizeof(name_buf), - "NAME=%s", prop_buf); - envp[env_offset++] = name_buf; - } - length = state_show(&edev->dev, NULL, prop_buf); - if (length > 0) { - if (prop_buf[length - 1] == '\n') - prop_buf[length - 1] = 0; - snprintf(state_buf, sizeof(state_buf), - "STATE=%s", prop_buf); - envp[env_offset++] = state_buf; - } - envp[env_offset] = NULL; - /* Unlock early before uevent */ - spin_unlock_irqrestore(&edev->lock, flags); - - 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); - } - } else { - /* No changes */ + /* This could be in interrupt handler */ + prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); + if (!prop_buf) { + /* 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); + + return 0; } + length = name_show(&edev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf); + envp[env_offset++] = name_buf; + } + + length = state_show(&edev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf); + envp[env_offset++] = state_buf; + } + envp[env_offset] = NULL; + + /* Unlock early before uevent */ + spin_unlock_irqrestore(&edev->lock, flags); + kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); + free_page((unsigned long)prop_buf); + return 0; } +EXPORT_SYMBOL_GPL(extcon_sync); /** * extcon_get_state() - Get the state of a external connector. @@ -520,17 +494,22 @@ EXPORT_SYMBOL_GPL(extcon_get_state); /** * extcon_set_state() - Set the state of a external connector. + * without a notification. * @edev: the extcon device that has the cable. * @id: the unique id of each external connector * in extcon enumeration. * @state: the new cable status. The default semantics is * true: attached / false: detached. + * + * This function only set the state of a external connector without + * a notification. To synchronize the data of a external connector, + * use extcon_set_state_sync() and extcon_sync(). */ int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool cable_state) { - u32 state; - int index; + unsigned long flags; + int index, ret = 0; if (!edev) return -EINVAL; @@ -539,6 +518,18 @@ int extcon_set_state(struct extcon_dev *edev, unsigned int id, if (index < 0) return index; + spin_lock_irqsave(&edev->lock, flags); + + /* Check whether the external connector's state is changed. */ + if (!is_extcon_changed(edev, index, cable_state)) + goto out; + + if (check_mutually_exclusive(edev, + (edev->state & ~BIT(index)) | (cable_state & BIT(index)))) { + ret = -EPERM; + goto out; + } + /* * Initialize the value of extcon property before setting * the detached state for an external connector. @@ -546,11 +537,55 @@ int extcon_set_state(struct extcon_dev *edev, unsigned int id, if (!cable_state) init_property(edev, id, index); - state = cable_state ? (1 << index) : 0; - return extcon_update_state(edev, 1 << index, state); + /* Update the state for a external connector. */ + if (cable_state) + edev->state |= BIT(index); + else + edev->state &= ~(BIT(index)); +out: + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; } EXPORT_SYMBOL_GPL(extcon_set_state); +/** + * extcon_set_state_sync() - Set the state of a external connector + * with a notification. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @state: the new cable status. The default semantics is + * true: attached / false: detached. + * + * This function set the state of external connector and synchronize the data + * by usning a notification. + */ +int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, + bool cable_state) +{ + int ret, index; + unsigned long flags; + + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + /* Check whether the external connector's state is changed. */ + spin_lock_irqsave(&edev->lock, flags); + ret = is_extcon_changed(edev, index, cable_state); + spin_unlock_irqrestore(&edev->lock, flags); + if (!ret) + return 0; + + ret = extcon_set_state(edev, id, cable_state); + if (ret < 0) + return ret; + + return extcon_sync(edev, id); +} +EXPORT_SYMBOL_GPL(extcon_set_state_sync); + /** * extcon_get_property() - Get the property value of a specific cable. * @edev: the extcon device that has the cable. @@ -701,6 +736,31 @@ int extcon_set_property(struct extcon_dev *edev, unsigned int id, } EXPORT_SYMBOL_GPL(extcon_set_property); +/** + * extcon_set_property_sync() - Set the property value of a specific cable + with a notification. + * @prop_val: the pointer including the new value of property. + * + * When setting the property value of external connector, the external connector + * should be attached. The each property should be included in the list of + * supported properties according to the type of external connectors. + * + * Returns 0 if success or error number if fail + */ +int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val) +{ + int ret; + + ret = extcon_set_property(edev, id, prop, prop_val); + if (ret < 0) + return ret; + + return extcon_sync(edev, id); +} +EXPORT_SYMBOL_GPL(extcon_set_property_sync); + /** * extcon_get_property_capability() - Get the capability of property * of an external connector. diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 4fa37385c97a..162c46a42bac 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -222,6 +222,13 @@ extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); extern int extcon_get_state(struct extcon_dev *edev, unsigned int id); extern int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool cable_state); +extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, + bool cable_state); + +/* + * Synchronize the state and property data for a specific external connector. + */ +extern int extcon_sync(struct extcon_dev *edev, unsigned int id); /* * get/set_property access the property value of each external connector. @@ -233,6 +240,9 @@ extern int extcon_get_property(struct extcon_dev *edev, unsigned int id, extern int extcon_set_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value prop_val); +extern int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val); /* * get/set_property_capability set the capability of the property for each @@ -317,6 +327,17 @@ static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id, return 0; } +static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, + bool cable_state) +{ + return 0; +} + +static inline int extcon_sync(struct extcon_dev *edev, unsigned int id) +{ + return 0; +} + static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value *prop_val) @@ -330,6 +351,13 @@ static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id, return 0; } +static inline int extcon_set_property_sync(struct extcon_dev *edev, + unsigned int id, unsigned int prop, + union extcon_property_value prop_val) +{ + return 0; +} + static inline int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop) { @@ -411,6 +439,6 @@ static inline int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int static inline int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, bool cable_state) { - return extcon_set_state(edev, id, cable_state); + return extcon_set_state_sync(edev, id, cable_state); } #endif /* __LINUX_EXTCON_H__ */ From 2164188d57f8862f1b69e2c5b7c0585f01077bee Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Fri, 22 Jul 2016 01:13:02 +0900 Subject: [PATCH 15/23] extcon: Add EXTCON_DISP_DP and the property for USB Type-C Add EXTCON_DISP_DP for the Display external connector. For Type-C connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort Alt Mode on USB Type-C Standard). The Type-C support both normal and flipped orientation, so add a property to extcon. Signed-off-by: Chris Zhong Signed-off-by: Chanwoo Choi Tested-by: Chris Zhong Tested-by: Guenter Roeck Reviewed-by: Guenter Roeck --- drivers/extcon/extcon.c | 5 +++++ include/linux/extcon.h | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 8fde4befaa51..a0a1eea18727 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -157,6 +157,11 @@ struct __extcon_info { .id = EXTCON_DISP_VGA, .name = "VGA", }, + [EXTCON_DISP_DP] = { + .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, + .id = EXTCON_DISP_DP, + .name = "DP", + }, /* Miscellaneous external connector */ [EXTCON_DOCK] = { diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 162c46a42bac..ad7a1606a7f3 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -69,6 +69,7 @@ #define EXTCON_DISP_MHL 41 /* Mobile High-Definition Link */ #define EXTCON_DISP_DVI 42 /* Digital Visual Interface */ #define EXTCON_DISP_VGA 43 /* Video Graphics Array */ +#define EXTCON_DISP_DP 44 /* Display Port */ /* Miscellaneous external connector */ #define EXTCON_DOCK 60 @@ -102,11 +103,16 @@ * @type: integer (intval) * @value: 0 (low) or 1 (high) * @default: 0 (low) + * - EXTCON_PROP_USB_TYPEC_POLARITY + * @type: integer (intval) + * @value: 0 (normal) or 1 (flip) + * @default: 0 (normal) */ #define EXTCON_PROP_USB_VBUS 0 +#define EXTCON_PROP_USB_TYPEC_POLARITY 1 #define EXTCON_PROP_USB_MIN 0 -#define EXTCON_PROP_USB_MAX 0 +#define EXTCON_PROP_USB_MAX 1 #define EXTCON_PROP_USB_CNT (EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1) /* Properties of EXTCON_TYPE_CHG. */ From 9c0595d688e9deb337ff8901bb25281cdb19050b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 5 Aug 2016 17:49:23 +0900 Subject: [PATCH 16/23] extcon: Add new EXTCON_DISP_HMD for Head-mounted Display device This patch adds the new EXTCON_DISP_HMD id for Head-mounted Display[1] device. The HMD device is usually for USB connector type So, the HMD connector has the two extcon types of both EXTCON_TYPE_DISP and EXTCON_TYPE_USB. [1] https://en.wikipedia.org/wiki/Head-mounted_display Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 5 +++++ include/linux/extcon.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index a0a1eea18727..10ba876ea77a 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -162,6 +162,11 @@ struct __extcon_info { .id = EXTCON_DISP_DP, .name = "DP", }, + [EXTCON_DISP_HMD] = { + .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, + .id = EXTCON_DISP_HMD, + .name = "HMD", + }, /* Miscellaneous external connector */ [EXTCON_DOCK] = { diff --git a/include/linux/extcon.h b/include/linux/extcon.h index ad7a1606a7f3..e79b644f41a7 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -70,6 +70,7 @@ #define EXTCON_DISP_DVI 42 /* Digital Visual Interface */ #define EXTCON_DISP_VGA 43 /* Video Graphics Array */ #define EXTCON_DISP_DP 44 /* Display Port */ +#define EXTCON_DISP_HMD 45 /* Head-Mounted Display */ /* Miscellaneous external connector */ #define EXTCON_DOCK 60 From 7fe95fb889faf07ee438384858d38b820973397e Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 5 Aug 2016 18:15:46 +0900 Subject: [PATCH 17/23] extcon: Add new EXTCON_CHG_WPT for Wireless Power Transfer device This patchs add the new EXTCON_CHG_WPT for Wireless Power Transfer[1]. The Wireless Power Transfer is the transmission of electronical energy from a power source. The EXTCON_CHG_WPT has the EXTCON_TYPE_CHG. [1] https://en.wikipedia.org/wiki/Wireless_power_transfer Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 5 +++++ include/linux/extcon.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 10ba876ea77a..78298460d168 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -93,6 +93,11 @@ struct __extcon_info { .id = EXTCON_CHG_USB_SLOW, .name = "SLOW-CHARGER", }, + [EXTCON_CHG_WPT] = { + .type = EXTCON_TYPE_CHG, + .id = EXTCON_CHG_WPT, + .name = "WPT", + }, /* Jack external connector */ [EXTCON_JACK_MICROPHONE] = { diff --git a/include/linux/extcon.h b/include/linux/extcon.h index e79b644f41a7..461abee969b7 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -53,6 +53,7 @@ #define EXTCON_CHG_USB_ACA 8 /* Accessory Charger Adapter */ #define EXTCON_CHG_USB_FAST 9 #define EXTCON_CHG_USB_SLOW 10 +#define EXTCON_CHG_WPT 11 /* Wireless Power Transfer */ /* Jack external connector */ #define EXTCON_JACK_MICROPHONE 20 From 8457a1b49a2af0a0e71f80afed9f7c80de361610 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Aug 2016 06:15:35 -0700 Subject: [PATCH 18/23] extcon: Introduce EXTCON_PROP_USB_SS property for SuperSpeed mode EXTCON_PROP_USB_SS (SuperSpeed)[1] is necessary to distinguish between USB/USB2 and USB3 connections on USB Type-C cables. [1] https://en.wikipedia.org/wiki/USB#Overview Cc: Chris Zhong Signed-off-by: Guenter Roeck Signed-off-by: Chanwoo Choi --- include/linux/extcon.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 461abee969b7..b34d1ae9011f 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -109,12 +109,18 @@ * @type: integer (intval) * @value: 0 (normal) or 1 (flip) * @default: 0 (normal) + * - EXTCON_PROP_USB_SS (SuperSpeed) + * @type: integer (intval) + * @value: 0 (USB/USB2) or 1 (USB3) + * @default: 0 (USB/USB2) + * */ #define EXTCON_PROP_USB_VBUS 0 #define EXTCON_PROP_USB_TYPEC_POLARITY 1 +#define EXTCON_PROP_USB_SS 2 #define EXTCON_PROP_USB_MIN 0 -#define EXTCON_PROP_USB_MAX 1 +#define EXTCON_PROP_USB_MAX 2 #define EXTCON_PROP_USB_CNT (EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1) /* Properties of EXTCON_TYPE_CHG. */ From c7914e8dfa4032d24ef7af4c86b9c841ec6b74e6 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Fri, 9 Sep 2016 19:15:44 -0700 Subject: [PATCH 19/23] extcon: Introduce EXTCON_PROP_DISP_HPD property EXTCON_PROP_DISP_HPD is need by display port, if the system has no hpd interrupt, this property can be used. - HPD (Hot Plug Detect) send the signal whether display device is on or off to source device. Signed-off-by: Chris Zhong Reviewed-by: Guenter Roeck [cw00.choi: Add the description of HPD and full name of HPD] Signed-off-by: Chanwoo Choi --- include/linux/extcon.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/linux/extcon.h b/include/linux/extcon.h index b34d1ae9011f..5c35f9d1822c 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -133,9 +133,20 @@ #define EXTCON_PROP_JACK_MAX 100 #define EXTCON_PROP_JACK_CNT (EXTCON_PROP_JACK_MAX - EXTCON_PROP_JACK_MIN + 1) +/* + * Properties of EXTCON_TYPE_DISP. + * + * - EXTCON_PROP_DISP_HPD (Hot Plug Detect) + * @type: integer (intval) + * @value: 0 (no hpd) or 1 (hpd) + * @default: 0 (no hpd) + * + */ +#define EXTCON_PROP_DISP_HPD 150 + /* Properties of EXTCON_TYPE_DISP. */ #define EXTCON_PROP_DISP_MIN 150 -#define EXTCON_PROP_DISP_MAX 150 +#define EXTCON_PROP_DISP_MAX 151 #define EXTCON_PROP_DISP_CNT (EXTCON_PROP_DISP_MAX - EXTCON_PROP_DISP_MIN + 1) /* From e5b07e555b02da6a171d8c4c670c4da0f219abde Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Aug 2016 14:07:44 +0200 Subject: [PATCH 20/23] extcon: max14577: Change Krzysztof Kozlowski's email to kernel.org Change my email address to kernel.org instead of Samsung one for the purpose of any future contact. The copyrights remain untouched and are attributed to Samsung. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max14577.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index 852a7112f451..a0cd6522a0ea 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -3,7 +3,7 @@ * * Copyright (C) 2013,2014 Samsung Electronics * Chanwoo Choi - * Krzysztof Kozlowski + * Krzysztof Kozlowski * * 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 @@ -791,6 +791,6 @@ static struct platform_driver max14577_muic_driver = { module_platform_driver(max14577_muic_driver); MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver"); -MODULE_AUTHOR("Chanwoo Choi , Krzysztof Kozlowski "); +MODULE_AUTHOR("Chanwoo Choi , Krzysztof Kozlowski "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:extcon-max14577"); From 525867dbd11899835a029da3532eeee9f773db31 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 17 Aug 2016 19:02:35 +0100 Subject: [PATCH 21/23] extcon: axp288: Fix spelling mistake This patch fixes the spelling mistake in dev_dbg messages. Signed-off-by: Colin Ian King [cw00.choi: Modify the patch title/description] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index fd55c2f2080a..04610f851437 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -189,19 +189,19 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) switch (chrg_type) { case DET_STAT_SDP: - dev_dbg(info->dev, "sdp cable is connecetd\n"); + dev_dbg(info->dev, "sdp cable is connected\n"); notify_otg = true; notify_charger = true; cable = EXTCON_CHG_USB_SDP; break; case DET_STAT_CDP: - dev_dbg(info->dev, "cdp cable is connecetd\n"); + dev_dbg(info->dev, "cdp cable is connected\n"); notify_otg = true; notify_charger = true; cable = EXTCON_CHG_USB_CDP; break; case DET_STAT_DCP: - dev_dbg(info->dev, "dcp cable is connecetd\n"); + dev_dbg(info->dev, "dcp cable is connected\n"); notify_charger = true; cable = EXTCON_CHG_USB_DCP; break; From 8670b4598064007abfc44554e713fa2004734e1d Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 16 Aug 2016 15:55:34 +0900 Subject: [PATCH 22/23] extcon: Use the extcon_set_state_sync() instead of deprecated functions This patch alters the renamed extcon API to set the state of the external connectors instead of deprecated extcon_set_cable_state_(). Because the patch[1] modifies the function name to maintain the function naming pattern. - extcon_set_cable_state_() -> extcon_set_state_sync() - extcon_get_cable_state_() -> extcon_get_state() [1] https://lkml.org/lkml/2016/8/4/729 - extcon: Rename the extcon_set/get_state() to maintain the function naming pattern Signed-off-by: Chanwoo Choi Acked-by: Charles Keepax Acked-by: Roger Quadros --- drivers/extcon/extcon-adc-jack.c | 4 +-- drivers/extcon/extcon-arizona.c | 16 +++++------ drivers/extcon/extcon-axp288.c | 2 +- drivers/extcon/extcon-gpio.c | 2 +- drivers/extcon/extcon-max14577.c | 14 +++++----- drivers/extcon/extcon-max3355.c | 8 +++--- drivers/extcon/extcon-max77693.c | 46 ++++++++++++++++---------------- drivers/extcon/extcon-max77843.c | 22 +++++++-------- drivers/extcon/extcon-max8997.c | 20 +++++++------- drivers/extcon/extcon-palmas.c | 16 +++++------ drivers/extcon/extcon-rt8973a.c | 4 +-- drivers/extcon/extcon-sm5502.c | 4 +-- drivers/extcon/extcon-usb-gpio.c | 8 +++--- 13 files changed, 83 insertions(+), 83 deletions(-) diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index e62e6cd7e00a..bc538708c753 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -75,7 +75,7 @@ static void adc_jack_handler(struct work_struct *work) for (i = 0; i < data->num_conditions; i++) { def = &data->adc_conditions[i]; if (def->min_adc <= adc_val && def->max_adc >= adc_val) { - extcon_set_cable_state_(data->edev, def->id, true); + extcon_set_state_sync(data->edev, def->id, true); return; } } @@ -83,7 +83,7 @@ static void adc_jack_handler(struct work_struct *work) /* Set the detached state if adc value is not included in the range */ for (i = 0; i < data->num_conditions; i++) { def = &data->adc_conditions[i]; - extcon_set_cable_state_(data->edev, def->id, false); + extcon_set_state_sync(data->edev, def->id, false); } } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 493bd9fe5f67..56e6c4c7c60d 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -614,7 +614,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, EXTCON_MECHANICAL); + ret = extcon_get_state(info->edev, EXTCON_MECHANICAL); if (ret < 0) { dev_err(arizona->dev, "Failed to check cable state: %d\n", ret); @@ -649,7 +649,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) else report = EXTCON_JACK_HEADPHONE; - ret = extcon_set_cable_state_(info->edev, report, true); + ret = extcon_set_state_sync(info->edev, report, true); if (ret != 0) dev_err(arizona->dev, "Failed to report HP/line: %d\n", ret); @@ -732,7 +732,7 @@ err: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* Just report headphone */ - ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true); + ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true); if (ret != 0) dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); @@ -789,7 +789,7 @@ err: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* Just report headphone */ - ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true); + ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true); if (ret != 0) dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); @@ -829,7 +829,7 @@ static void arizona_micd_detect(struct work_struct *work) mutex_lock(&info->lock); /* If the cable was removed while measuring ignore the result */ - ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL); + ret = extcon_get_state(info->edev, EXTCON_MECHANICAL); if (ret < 0) { dev_err(arizona->dev, "Failed to check cable state: %d\n", ret); @@ -914,7 +914,7 @@ static void arizona_micd_detect(struct work_struct *work) arizona_identify_headphone(info); - ret = extcon_set_cable_state_(info->edev, + ret = extcon_set_state_sync(info->edev, EXTCON_JACK_MICROPHONE, true); if (ret != 0) dev_err(arizona->dev, "Headset report failed: %d\n", @@ -1108,7 +1108,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_state_sync(info->edev, EXTCON_MECHANICAL, true); if (ret != 0) @@ -1150,7 +1150,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) input_sync(info->input); for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) { - ret = extcon_set_cable_state_(info->edev, + ret = extcon_set_state_sync(info->edev, arizona_cable[i], false); if (ret != 0) dev_err(arizona->dev, diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 04610f851437..42f41e808292 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -226,7 +226,7 @@ notify_otg: } if (notify_charger) - extcon_set_cable_state_(info->edev, cable, vbus_attach); + extcon_set_state_sync(info->edev, cable, vbus_attach); /* Clear the flags on disconnect event */ if (!vbus_attach) diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index b1b0264eb12d..e7aebbc945f6 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -49,7 +49,7 @@ static void gpio_extcon_work(struct work_struct *work) state = gpiod_get_value_cansleep(data->id_gpiod); if (data->pdata->gpio_active_low) state = !state; - extcon_set_cable_state_(data->edev, data->pdata->extcon_id, state); + extcon_set_state_sync(data->edev, data->pdata->extcon_id, state); } static irqreturn_t gpio_irq_handler(int irq, void *dev_id) diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index a0cd6522a0ea..12e26c4e7763 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -357,7 +357,7 @@ static int max14577_muic_jig_handler(struct max14577_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -454,24 +454,24 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX14577_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX14577_CHARGER_TYPE_SPECIAL_500MA: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX14577_CHARGER_TYPE_SPECIAL_1A: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX14577_CHARGER_TYPE_NONE: diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c index c24abec5d06c..533e16a952b8 100644 --- a/drivers/extcon/extcon-max3355.c +++ b/drivers/extcon/extcon-max3355.c @@ -39,16 +39,16 @@ static irqreturn_t max3355_id_irq(int irq, void *dev_id) * As we don't have event for USB peripheral cable attached, * we simulate USB peripheral attach here. */ - extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, false); - extcon_set_cable_state_(data->edev, EXTCON_USB, true); + extcon_set_state_sync(data->edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(data->edev, EXTCON_USB, true); } else { /* * ID = 0 means USB HOST cable attached. * As we don't have event for USB peripheral cable detached, * we simulate USB peripheral detach here. */ - extcon_set_cable_state_(data->edev, EXTCON_USB, false); - extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(data->edev, EXTCON_USB, false); + extcon_set_state_sync(data->edev, EXTCON_USB_HOST, true); } return IRQ_HANDLED; diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index f17cb76b567c..68dbcb814b2f 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -505,8 +505,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached); - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); goto out; case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE: /* Dock-Desk */ dock_id = EXTCON_DOCK; @@ -514,8 +514,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD: /* Dock-Audio */ dock_id = EXTCON_DOCK; if (!attached) { - extcon_set_cable_state_(info->edev, EXTCON_USB, false); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, false); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, false); } break; @@ -530,7 +530,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, attached); if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, dock_id, attached); + extcon_set_state_sync(info->edev, dock_id, attached); out: return 0; @@ -596,7 +596,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info) attached); if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); break; case MAX77693_MUIC_GND_AV_CABLE_LOAD: /* Audio Video Cable with load, PATH:AUDIO */ @@ -604,14 +604,14 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info) attached); if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX77693_MUIC_GND_MHL: case MAX77693_MUIC_GND_MHL_VB: /* MHL or MHL with USB/TA cable */ - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; default: dev_err(info->dev, "failed to detect %s cable of gnd type\n", @@ -653,7 +653,7 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -807,10 +807,10 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) * - Support charging through micro-usb port without * data connection */ - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); if (!cable_attached) - extcon_set_cable_state_(info->edev, + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, cable_attached); break; } @@ -834,13 +834,13 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) * - Support charging through micro-usb port without * data connection. */ - extcon_set_cable_state_(info->edev, EXTCON_USB, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); if (!cable_attached) - extcon_set_cable_state_(info->edev, EXTCON_DOCK, + extcon_set_state_sync(info->edev, EXTCON_DOCK, cable_attached); break; case MAX77693_MUIC_ADC_RESERVED_ACC_3: /* Dock-Smart */ @@ -869,9 +869,9 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_DOCK, + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; } @@ -905,28 +905,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX77693_CHARGER_TYPE_DEDICATED_CHG: /* Only TA cable */ - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; } break; case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX77693_CHARGER_TYPE_APPLE_500MA: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX77693_CHARGER_TYPE_APPLE_1A_2A: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX77693_CHARGER_TYPE_DEAD_BATTERY: diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index b188bd650efa..5d11fdf36e94 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -346,7 +346,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); break; case MAX77843_MUIC_GND_MHL_VB: case MAX77843_MUIC_GND_MHL: @@ -356,7 +356,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; default: dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n", @@ -392,7 +392,7 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -486,8 +486,8 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX77843_MUIC_CHG_DOWNSTREAM: @@ -497,7 +497,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX77843_MUIC_CHG_DEDICATED: @@ -507,7 +507,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; case MAX77843_MUIC_CHG_SPECIAL_500MA: @@ -517,7 +517,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX77843_MUIC_CHG_SPECIAL_1A: @@ -527,7 +527,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX77843_MUIC_CHG_GND: @@ -536,10 +536,10 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) /* Charger cable on MHL accessory is attach or detach */ if (gnd_type == MAX77843_MUIC_GND_MHL_VB) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, true); else if (gnd_type == MAX77843_MUIC_GND_MHL) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, false); break; case MAX77843_MUIC_CHG_NONE: diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 9a89320d09a8..4a0612fb9c07 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -331,11 +331,11 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info, switch (usb_type) { case MAX8997_USB_HOST: - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); break; case MAX8997_USB_DEVICE: - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; default: @@ -361,7 +361,7 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info, switch (cable_type) { case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD: case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON: - extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached); + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); break; default: dev_err(info->dev, "failed to detect %s dock device\n", @@ -384,7 +384,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, return ret; } - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -406,7 +406,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info) return ret; break; case MAX8997_MUIC_ADC_MHL: - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF: case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON: @@ -489,19 +489,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info) } break; case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX8997_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; case MAX8997_CHARGER_TYPE_500MA: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX8997_CHARGER_TYPE_1A: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; default: diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index caff46c0e214..634ba70782de 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -61,7 +61,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_(edev, EXTCON_USB, true); + extcon_set_state_sync(edev, EXTCON_USB, true); dev_info(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, @@ -70,7 +70,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_(edev, EXTCON_USB, false); + extcon_set_state_sync(edev, EXTCON_USB, false); dev_info(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, @@ -98,7 +98,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) 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_(edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { @@ -106,17 +106,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) 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_(edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(edev, EXTCON_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_(edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(edev, EXTCON_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_(edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); } @@ -137,10 +137,10 @@ static void palmas_gpio_id_detect(struct work_struct *work) id = gpiod_get_value_cansleep(palmas_usb->id_gpiod); if (id) { - extcon_set_cable_state_(edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(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); + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } } diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 97e074d70eca..174c388739ea 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -398,9 +398,9 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info, return ret; /* Change the state of external accessory */ - extcon_set_cable_state_(info->edev, id, attached); + extcon_set_state_sync(info->edev, id, attached); if (id == EXTCON_USB) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); return 0; diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index df769a17e736..b22325688503 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -411,9 +411,9 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, return ret; /* Change the state of external accessory */ - extcon_set_cable_state_(info->edev, id, attached); + extcon_set_state_sync(info->edev, id, attached); if (id == EXTCON_USB) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); return 0; diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 2512660dc4b9..a27d350f69e3 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -63,16 +63,16 @@ static void usb_extcon_detect_cable(struct work_struct *work) * As we don't have event for USB peripheral cable attached, * we simulate USB peripheral attach here. */ - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, false); - extcon_set_cable_state_(info->edev, EXTCON_USB, true); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(info->edev, EXTCON_USB, true); } else { /* * ID = 0 means USB HOST cable attached. * As we don't have event for USB peripheral cable detached, * we simulate USB peripheral detach here. */ - extcon_set_cable_state_(info->edev, EXTCON_USB, false); - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(info->edev, EXTCON_USB, false); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true); } } From 38085c987f52674c2cc84fa0c0788eb71137cb2b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 9 Sep 2016 14:48:47 -0700 Subject: [PATCH 23/23] extcon: Add support for qcom SPMI PMIC USB id detection hardware Some Qualcomm PMICs have a misc device that performs USB id pin detection via an interrupt. When the interrupt triggers, we should read the interrupt line to see if it has gone high or low. If the interrupt is low then the ID pin is grounded, and if the interrupt is high then the ID pin is being held high. Cc: Roger Quadros Signed-off-by: Stephen Boyd [cw00.choi: Edited the driver description and added the author information] Signed-off-by: Chanwoo Choi --- .../bindings/extcon/qcom,pm8941-misc.txt | 41 +++++ drivers/extcon/Kconfig | 6 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-qcom-spmi-misc.c | 170 ++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt create mode 100644 drivers/extcon/extcon-qcom-spmi-misc.c diff --git a/Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt b/Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt new file mode 100644 index 000000000000..35383adb10f1 --- /dev/null +++ b/Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt @@ -0,0 +1,41 @@ +Qualcomm's PM8941 USB ID Extcon device + +Some Qualcomm PMICs have a "misc" module that can be used to detect when +the USB ID pin has been pulled low or high. + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: Should contain "qcom,pm8941-misc"; + +- reg: + Usage: required + Value type: + Definition: Should contain the offset to the misc address space + +- interrupts: + Usage: required + Value type: + Definition: Should contain the usb id interrupt + +- interrupt-names: + Usage: required + Value type: + Definition: Should contain the string "usb_id" for the usb id interrupt + +Example: + + pmic { + usb_id: misc@900 { + compatible = "qcom,pm8941-misc"; + reg = <0x900>; + interrupts = <0x0 0x9 0 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "usb_id"; + }; + } + + usb-controller { + extcon = <&usb_id>; + }; diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 3d89e60a3e71..04788d92ea52 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -96,6 +96,12 @@ config EXTCON_PALMAS Say Y here to enable support for USB peripheral and USB host detection by palmas usb. +config EXTCON_QCOM_SPMI_MISC + tristate "Qualcomm USB extcon support" + help + Say Y here to enable SPMI PMIC based USB cable detection + support on Qualcomm PMICs such as PM8941. + config EXTCON_RT8973A tristate "Richtek RT8973A EXTCON support" depends on I2C diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 972c813c375b..31a0a999c4fb 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o +obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c new file mode 100644 index 000000000000..ca957a5f4291 --- /dev/null +++ b/drivers/extcon/extcon-qcom-spmi-misc.c @@ -0,0 +1,170 @@ +/** + * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID + * detection based on extcon-usb-gpio.c. + * + * Copyright (C) 2016 Linaro, Ltd. + * Stephen Boyd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define USB_ID_DEBOUNCE_MS 5 /* ms */ + +struct qcom_usb_extcon_info { + struct extcon_dev *edev; + int irq; + struct delayed_work wq_detcable; + unsigned long debounce_jiffies; +}; + +static const unsigned int qcom_usb_extcon_cable[] = { + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +static void qcom_usb_extcon_detect_cable(struct work_struct *work) +{ + bool id; + int ret; + struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work), + struct qcom_usb_extcon_info, + wq_detcable); + + /* check ID and update cable state */ + ret = irq_get_irqchip_state(info->irq, IRQCHIP_STATE_LINE_LEVEL, &id); + if (ret) + return; + + extcon_set_state(info->edev, EXTCON_USB_HOST, !id); +} + +static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id) +{ + struct qcom_usb_extcon_info *info = dev_id; + + queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, + info->debounce_jiffies); + + return IRQ_HANDLED; +} + +static int qcom_usb_extcon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_usb_extcon_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(dev, "failed to allocate extcon device\n"); + return -ENOMEM; + } + + ret = devm_extcon_dev_register(dev, info->edev); + if (ret < 0) { + dev_err(dev, "failed to register extcon device\n"); + return ret; + } + + info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS); + INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable); + + info->irq = platform_get_irq_byname(pdev, "usb_id"); + if (info->irq < 0) + return info->irq; + + ret = devm_request_threaded_irq(dev, info->irq, NULL, + qcom_usb_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdev->name, info); + if (ret < 0) { + dev_err(dev, "failed to request handler for ID IRQ\n"); + return ret; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(dev, 1); + + /* Perform initial detection */ + qcom_usb_extcon_detect_cable(&info->wq_detcable.work); + + return 0; +} + +static int qcom_usb_extcon_remove(struct platform_device *pdev) +{ + struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&info->wq_detcable); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int qcom_usb_extcon_suspend(struct device *dev) +{ + struct qcom_usb_extcon_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) + ret = enable_irq_wake(info->irq); + + return ret; +} + +static int qcom_usb_extcon_resume(struct device *dev) +{ + struct qcom_usb_extcon_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) + ret = disable_irq_wake(info->irq); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(qcom_usb_extcon_pm_ops, + qcom_usb_extcon_suspend, qcom_usb_extcon_resume); + +static const struct of_device_id qcom_usb_extcon_dt_match[] = { + { .compatible = "qcom,pm8941-misc", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match); + +static struct platform_driver qcom_usb_extcon_driver = { + .probe = qcom_usb_extcon_probe, + .remove = qcom_usb_extcon_remove, + .driver = { + .name = "extcon-pm8941-misc", + .pm = &qcom_usb_extcon_pm_ops, + .of_match_table = qcom_usb_extcon_dt_match, + }, +}; +module_platform_driver(qcom_usb_extcon_driver); + +MODULE_DESCRIPTION("QCOM USB ID extcon driver"); +MODULE_AUTHOR("Stephen Boyd "); +MODULE_LICENSE("GPL v2");