diff options
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/Kconfig | 1 | ||||
-rw-r--r-- | drivers/extcon/extcon-axp288.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-fsa9480.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-palmas.c | 1 | ||||
-rw-r--r-- | drivers/extcon/extcon-ptn5150.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-qcom-spmi-misc.c | 4 | ||||
-rw-r--r-- | drivers/extcon/extcon-rt8973a.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-sm5502.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-usbc-tusb320.c | 155 | ||||
-rw-r--r-- | drivers/extcon/extcon.c | 368 | ||||
-rw-r--r-- | drivers/extcon/extcon.h | 8 |
11 files changed, 350 insertions, 197 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 290186e44e6b..0ef1971d22bb 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -185,6 +185,7 @@ config EXTCON_USBC_TUSB320 tristate "TI TUSB320 USB-C extcon support" depends on I2C && TYPEC select REGMAP_I2C + depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH help Say Y here to enable support for USB Type C cable detection extcon support using a TUSB320. diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 180be768c215..a703a8315634 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -393,7 +393,7 @@ static int axp288_extcon_probe(struct platform_device *pdev) adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1); if (adev) { info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev)); - put_device(&adev->dev); + acpi_dev_put(adev); if (IS_ERR(info->id_extcon)) return PTR_ERR(info->id_extcon); diff --git a/drivers/extcon/extcon-fsa9480.c b/drivers/extcon/extcon-fsa9480.c index e8b2671eb29b..e458ce0c45ab 100644 --- a/drivers/extcon/extcon-fsa9480.c +++ b/drivers/extcon/extcon-fsa9480.c @@ -369,7 +369,7 @@ static struct i2c_driver fsa9480_i2c_driver = { .pm = &fsa9480_pm_ops, .of_match_table = fsa9480_of_match, }, - .probe_new = fsa9480_probe, + .probe = fsa9480_probe, .id_table = fsa9480_id, }; diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 32f8b541770b..d339b8680445 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -18,7 +18,6 @@ #include <linux/mfd/palmas.h> #include <linux/of.h> #include <linux/of_platform.h> -#include <linux/of_gpio.h> #include <linux/gpio/consumer.h> #include <linux/workqueue.h> diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index 017a07197f38..4616da7e5430 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -348,7 +348,7 @@ static struct i2c_driver ptn5150_i2c_driver = { .name = "ptn5150", .of_match_table = ptn5150_dt_match, }, - .probe_new = ptn5150_i2c_probe, + .probe = ptn5150_i2c_probe, .id_table = ptn5150_i2c_id, }; module_i2c_driver(ptn5150_i2c_driver); diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c index eb02cb962b5e..f72e90ceca53 100644 --- a/drivers/extcon/extcon-qcom-spmi-misc.c +++ b/drivers/extcon/extcon-qcom-spmi-misc.c @@ -123,7 +123,7 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev) if (ret) return ret; - info->id_irq = platform_get_irq_byname(pdev, "usb_id"); + info->id_irq = platform_get_irq_byname_optional(pdev, "usb_id"); if (info->id_irq > 0) { ret = devm_request_threaded_irq(dev, info->id_irq, NULL, qcom_usb_irq_handler, @@ -136,7 +136,7 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev) } } - info->vbus_irq = platform_get_irq_byname(pdev, "usb_vbus"); + info->vbus_irq = platform_get_irq_byname_optional(pdev, "usb_vbus"); if (info->vbus_irq > 0) { ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL, qcom_usb_irq_handler, diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index afc9b405d103..19bb49f13fb0 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -695,7 +695,7 @@ static struct i2c_driver rt8973a_muic_i2c_driver = { .pm = &rt8973a_muic_pm_ops, .of_match_table = rt8973a_dt_match, }, - .probe_new = rt8973a_muic_i2c_probe, + .probe = rt8973a_muic_i2c_probe, .remove = rt8973a_muic_i2c_remove, .id_table = rt8973a_i2c_id, }; diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index 8401e8b27788..c8c4b9ef72aa 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -840,7 +840,7 @@ static struct i2c_driver sm5502_muic_i2c_driver = { .pm = &sm5502_muic_pm_ops, .of_match_table = sm5502_dt_match, }, - .probe_new = sm5022_muic_i2c_probe, + .probe = sm5022_muic_i2c_probe, .id_table = sm5502_i2c_id, }; diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index b408ce989c22..4d08c2123e59 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -15,6 +15,8 @@ #include <linux/module.h> #include <linux/regmap.h> #include <linux/usb/typec.h> +#include <linux/usb/typec_altmode.h> +#include <linux/usb/role.h> #define TUSB320_REG8 0x8 #define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6) @@ -26,16 +28,16 @@ #define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1 #define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2 #define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3 -#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 2) +#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 1) #define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0 #define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4 -#define TUSB320_REG8_ACCESSORY_CONNECTED_ACC 0x5 -#define TUSB320_REG8_ACCESSORY_CONNECTED_DEBUG 0x6 +#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG 0x5 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP 0x6 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP 0x7 #define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0) #define TUSB320_REG9 0x9 -#define TUSB320_REG9_ATTACHED_STATE_SHIFT 6 -#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3 +#define TUSB320_REG9_ATTACHED_STATE GENMASK(7, 6) #define TUSB320_REG9_CABLE_DIRECTION BIT(5) #define TUSB320_REG9_INTERRUPT_STATUS BIT(4) @@ -78,6 +80,8 @@ struct tusb320_priv { struct typec_capability cap; enum typec_port_type port_type; enum typec_pwr_opmode pwr_opmode; + struct fwnode_handle *connector_fwnode; + struct usb_role_switch *role_sw; }; static const char * const tusb_attached_states[] = { @@ -249,8 +253,7 @@ static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg) { int state, polarity; - state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) & - TUSB320_REG9_ATTACHED_STATE_MASK; + state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg); polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION); dev_dbg(priv->dev, "attached state: %s, polarity: %d\n", @@ -276,32 +279,86 @@ static void tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9) { struct typec_port *port = priv->port; struct device *dev = priv->dev; - u8 mode, role, state; + int typec_mode; + enum usb_role usb_role; + enum typec_role pwr_role; + enum typec_data_role data_role; + u8 state, mode, accessory; int ret, reg8; bool ori; + ret = regmap_read(priv->regmap, TUSB320_REG8, ®8); + if (ret) { + dev_err(dev, "error during reg8 i2c read, ret=%d!\n", ret); + return; + } + ori = reg9 & TUSB320_REG9_CABLE_DIRECTION; typec_set_orientation(port, ori ? TYPEC_ORIENTATION_REVERSE : TYPEC_ORIENTATION_NORMAL); - state = (reg9 >> TUSB320_REG9_ATTACHED_STATE_SHIFT) & - TUSB320_REG9_ATTACHED_STATE_MASK; - if (state == TUSB320_ATTACHED_STATE_DFP) - role = TYPEC_SOURCE; - else - role = TYPEC_SINK; + state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9); + accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8); + + switch (state) { + case TUSB320_ATTACHED_STATE_DFP: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_HOST; + pwr_role = TYPEC_SOURCE; + data_role = TYPEC_HOST; + break; + case TUSB320_ATTACHED_STATE_UFP: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_DEVICE; + pwr_role = TYPEC_SINK; + data_role = TYPEC_DEVICE; + break; + case TUSB320_ATTACHED_STATE_ACC: + /* + * Accessory detected. For debug accessories, just make some + * qualified guesses as to the role for lack of a better option. + */ + if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO || + accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) { + typec_mode = TYPEC_MODE_AUDIO; + usb_role = USB_ROLE_NONE; + pwr_role = TYPEC_SINK; + data_role = TYPEC_DEVICE; + break; + } else if (accessory == + TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) { + typec_mode = TYPEC_MODE_DEBUG; + pwr_role = TYPEC_SOURCE; + usb_role = USB_ROLE_HOST; + data_role = TYPEC_HOST; + break; + } else if (accessory == + TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) { + typec_mode = TYPEC_MODE_DEBUG; + pwr_role = TYPEC_SINK; + usb_role = USB_ROLE_DEVICE; + data_role = TYPEC_DEVICE; + break; + } - typec_set_vconn_role(port, role); - typec_set_pwr_role(port, role); - typec_set_data_role(port, role == TYPEC_SOURCE ? - TYPEC_HOST : TYPEC_DEVICE); + dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n", + accessory); - ret = regmap_read(priv->regmap, TUSB320_REG8, ®8); - if (ret) { - dev_err(dev, "error during reg8 i2c read, ret=%d!\n", ret); - return; + fallthrough; + default: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_NONE; + pwr_role = TYPEC_SINK; + data_role = TYPEC_DEVICE; + break; } + typec_set_vconn_role(port, pwr_role); + typec_set_pwr_role(port, pwr_role); + typec_set_data_role(port, data_role); + typec_set_mode(port, typec_mode); + usb_role_switch_set_role(priv->role_sw, usb_role); + mode = FIELD_GET(TUSB320_REG8_CURRENT_MODE_DETECT, reg8); if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_DEF) typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB); @@ -391,27 +448,25 @@ static int tusb320_typec_probe(struct i2c_client *client, /* Type-C connector found. */ ret = typec_get_fw_cap(&priv->cap, connector); if (ret) - return ret; + goto err_put; priv->port_type = priv->cap.type; /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */ ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str); if (ret) - return ret; + goto err_put; ret = typec_find_pwr_opmode(cap_str); if (ret < 0) - return ret; - if (ret == TYPEC_PWR_MODE_PD) - return -EINVAL; + goto err_put; priv->pwr_opmode = ret; /* Initialize the hardware with the devicetree settings. */ ret = tusb320_set_adv_pwr_mode(priv); if (ret) - return ret; + goto err_put; priv->cap.revision = USB_TYPEC_REV_1_1; priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO; @@ -422,10 +477,36 @@ static int tusb320_typec_probe(struct i2c_client *client, priv->cap.fwnode = connector; priv->port = typec_register_port(&client->dev, &priv->cap); - if (IS_ERR(priv->port)) - return PTR_ERR(priv->port); + if (IS_ERR(priv->port)) { + ret = PTR_ERR(priv->port); + goto err_put; + } + + /* Find any optional USB role switch that needs reporting to */ + priv->role_sw = fwnode_usb_role_switch_get(connector); + if (IS_ERR(priv->role_sw)) { + ret = PTR_ERR(priv->role_sw); + goto err_unreg; + } + + priv->connector_fwnode = connector; return 0; + +err_unreg: + typec_unregister_port(priv->port); + +err_put: + fwnode_handle_put(connector); + + return ret; +} + +static void tusb320_typec_remove(struct tusb320_priv *priv) +{ + usb_role_switch_put(priv->role_sw); + typec_unregister_port(priv->port); + fwnode_handle_put(priv->connector_fwnode); } static int tusb320_probe(struct i2c_client *client) @@ -438,7 +519,9 @@ static int tusb320_probe(struct i2c_client *client) priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->dev = &client->dev; + i2c_set_clientdata(client, priv); priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config); if (IS_ERR(priv->regmap)) @@ -489,10 +572,19 @@ static int tusb320_probe(struct i2c_client *client) tusb320_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, priv); + if (ret) + tusb320_typec_remove(priv); return ret; } +static void tusb320_remove(struct i2c_client *client) +{ + struct tusb320_priv *priv = i2c_get_clientdata(client); + + tusb320_typec_remove(priv); +} + static const struct of_device_id tusb320_extcon_dt_match[] = { { .compatible = "ti,tusb320", .data = &tusb320_ops, }, { .compatible = "ti,tusb320l", .data = &tusb320l_ops, }, @@ -501,7 +593,8 @@ static const struct of_device_id tusb320_extcon_dt_match[] = { MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); static struct i2c_driver tusb320_extcon_driver = { - .probe_new = tusb320_probe, + .probe = tusb320_probe, + .remove = tusb320_remove, .driver = { .name = "extcon-tusb320", .of_match_table = tusb320_extcon_dt_match, diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index d43ba8e7260d..6f7a60d2ed91 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/types.h> +#include <linux/idr.h> #include <linux/init.h> #include <linux/device.h> #include <linux/fs.h> @@ -206,6 +207,14 @@ static const struct __extcon_info { * @attr_name: "name" sysfs entry * @attr_state: "state" sysfs entry * @attrs: the array pointing to attr_name and attr_state for attr_g + * @usb_propval: the array of USB connector properties + * @chg_propval: the array of charger connector properties + * @jack_propval: the array of jack connector properties + * @disp_propval: the array of display connector properties + * @usb_bits: the bit array of the USB connector property capabilities + * @chg_bits: the bit array of the charger connector property capabilities + * @jack_bits: the bit array of the jack connector property capabilities + * @disp_bits: the bit array of the display connector property capabilities */ struct extcon_cable { struct extcon_dev *edev; @@ -222,20 +231,21 @@ struct extcon_cable { 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)]; + DECLARE_BITMAP(usb_bits, EXTCON_PROP_USB_CNT); + DECLARE_BITMAP(chg_bits, EXTCON_PROP_CHG_CNT); + DECLARE_BITMAP(jack_bits, EXTCON_PROP_JACK_CNT); + DECLARE_BITMAP(disp_bits, EXTCON_PROP_DISP_CNT); }; static struct class *extcon_class; +static DEFINE_IDA(extcon_dev_ids); static LIST_HEAD(extcon_dev_list); static DEFINE_MUTEX(extcon_dev_list_lock); static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) { - int i = 0; + int i; if (!edev->mutually_exclusive) return 0; @@ -362,12 +372,12 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, struct extcon_dev *edev = dev_get_drvdata(dev); if (edev->max_supported == 0) - return sprintf(buf, "%u\n", edev->state); + return sysfs_emit(buf, "%u\n", edev->state); for (i = 0; i < edev->max_supported; i++) { - count += sprintf(buf + count, "%s=%d\n", - extcon_info[edev->supported_cable[i]].name, - !!(edev->state & BIT(i))); + count += sysfs_emit_at(buf, count, "%s=%d\n", + extcon_info[edev->supported_cable[i]].name, + !!(edev->state & BIT(i))); } return count; @@ -379,7 +389,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, { struct extcon_dev *edev = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", edev->name); + return sysfs_emit(buf, "%s\n", edev->name); } static DEVICE_ATTR_RO(name); @@ -390,8 +400,8 @@ static ssize_t cable_name_show(struct device *dev, attr_name); int i = cable->cable_index; - return sprintf(buf, "%s\n", - extcon_info[cable->edev->supported_cable[i]].name); + return sysfs_emit(buf, "%s\n", + extcon_info[cable->edev->supported_cable[i]].name); } static ssize_t cable_state_show(struct device *dev, @@ -402,8 +412,8 @@ static ssize_t cable_state_show(struct device *dev, int i = cable->cable_index; - return sprintf(buf, "%d\n", - extcon_get_state(cable->edev, cable->edev->supported_cable[i])); + return sysfs_emit(buf, "%d\n", + extcon_get_state(cable->edev, cable->edev->supported_cable[i])); } /** @@ -1012,12 +1022,13 @@ ATTRIBUTE_GROUPS(extcon); static int create_extcon_class(void) { - if (!extcon_class) { - extcon_class = class_create("extcon"); - if (IS_ERR(extcon_class)) - return PTR_ERR(extcon_class); - extcon_class->dev_groups = extcon_groups; - } + if (extcon_class) + return 0; + + extcon_class = class_create("extcon"); + if (IS_ERR(extcon_class)) + return PTR_ERR(extcon_class); + extcon_class->dev_groups = extcon_groups; return 0; } @@ -1070,6 +1081,156 @@ void extcon_dev_free(struct extcon_dev *edev) EXPORT_SYMBOL_GPL(extcon_dev_free); /** + * extcon_alloc_cables() - alloc the cables for extcon device + * @edev: extcon device which has cables + * + * Returns 0 if success or error number if fail. + */ +static int extcon_alloc_cables(struct extcon_dev *edev) +{ + int index; + char *str; + struct extcon_cable *cable; + + if (!edev) + return -EINVAL; + + if (!edev->max_supported) + return 0; + + edev->cables = kcalloc(edev->max_supported, sizeof(*edev->cables), + GFP_KERNEL); + if (!edev->cables) + return -ENOMEM; + + for (index = 0; index < edev->max_supported; index++) { + cable = &edev->cables[index]; + + str = kasprintf(GFP_KERNEL, "cable.%d", index); + if (!str) { + for (index--; index >= 0; index--) { + cable = &edev->cables[index]; + kfree(cable->attr_g.name); + } + + kfree(edev->cables); + return -ENOMEM; + } + + cable->edev = edev; + cable->cable_index = index; + cable->attrs[0] = &cable->attr_name.attr; + cable->attrs[1] = &cable->attr_state.attr; + cable->attrs[2] = NULL; + cable->attr_g.name = str; + cable->attr_g.attrs = cable->attrs; + + sysfs_attr_init(&cable->attr_name.attr); + cable->attr_name.attr.name = "name"; + cable->attr_name.attr.mode = 0444; + cable->attr_name.show = cable_name_show; + + sysfs_attr_init(&cable->attr_state.attr); + cable->attr_state.attr.name = "state"; + cable->attr_state.attr.mode = 0444; + cable->attr_state.show = cable_state_show; + } + + return 0; +} + +/** + * extcon_alloc_muex() - alloc the mutual exclusive for extcon device + * @edev: extcon device + * + * Returns 0 if success or error number if fail. + */ +static int extcon_alloc_muex(struct extcon_dev *edev) +{ + char *name; + int index; + + if (!edev) + return -EINVAL; + + if (!(edev->max_supported && edev->mutually_exclusive)) + return 0; + + /* Count the size of mutually_exclusive array */ + for (index = 0; edev->mutually_exclusive[index]; index++) + ; + + edev->attrs_muex = kcalloc(index + 1, sizeof(*edev->attrs_muex), + GFP_KERNEL); + if (!edev->attrs_muex) + return -ENOMEM; + + edev->d_attrs_muex = kcalloc(index, sizeof(*edev->d_attrs_muex), + GFP_KERNEL); + if (!edev->d_attrs_muex) { + kfree(edev->attrs_muex); + return -ENOMEM; + } + + for (index = 0; edev->mutually_exclusive[index]; index++) { + name = kasprintf(GFP_KERNEL, "0x%x", + edev->mutually_exclusive[index]); + if (!name) { + for (index--; index >= 0; index--) + kfree(edev->d_attrs_muex[index].attr.name); + + kfree(edev->d_attrs_muex); + kfree(edev->attrs_muex); + return -ENOMEM; + } + sysfs_attr_init(&edev->d_attrs_muex[index].attr); + edev->d_attrs_muex[index].attr.name = name; + edev->d_attrs_muex[index].attr.mode = 0000; + edev->attrs_muex[index] = &edev->d_attrs_muex[index].attr; + } + edev->attr_g_muex.name = muex_name; + edev->attr_g_muex.attrs = edev->attrs_muex; + + return 0; +} + +/** + * extcon_alloc_groups() - alloc the groups for extcon device + * @edev: extcon device + * + * Returns 0 if success or error number if fail. + */ +static int extcon_alloc_groups(struct extcon_dev *edev) +{ + int index; + + if (!edev) + return -EINVAL; + + if (!edev->max_supported) + return 0; + + edev->extcon_dev_type.groups = kcalloc(edev->max_supported + 2, + sizeof(*edev->extcon_dev_type.groups), + GFP_KERNEL); + if (!edev->extcon_dev_type.groups) + return -ENOMEM; + + edev->extcon_dev_type.name = dev_name(&edev->dev); + edev->extcon_dev_type.release = dummy_sysfs_dev_release; + + for (index = 0; index < edev->max_supported; index++) + edev->extcon_dev_type.groups[index] = &edev->cables[index].attr_g; + + if (edev->mutually_exclusive) + edev->extcon_dev_type.groups[index] = &edev->attr_g_muex; + + edev->dev.type = &edev->extcon_dev_type; + + return 0; +} + +/** * extcon_dev_register() - Register an new extcon device * @edev: the extcon device to be registered * @@ -1085,19 +1246,16 @@ EXPORT_SYMBOL_GPL(extcon_dev_free); */ int extcon_dev_register(struct extcon_dev *edev) { - int ret, index = 0; - static atomic_t edev_no = ATOMIC_INIT(-1); + int ret, index; - if (!extcon_class) { - ret = create_extcon_class(); - if (ret < 0) - return ret; - } + ret = create_extcon_class(); + if (ret < 0) + return ret; if (!edev || !edev->supported_cable) return -EINVAL; - for (; edev->supported_cable[index] != EXTCON_NONE; index++); + for (index = 0; edev->supported_cable[index] != EXTCON_NONE; index++); edev->max_supported = index; if (index > SUPPORTED_CABLE_MAX) { @@ -1115,124 +1273,26 @@ int extcon_dev_register(struct extcon_dev *edev) "extcon device name is null\n"); return -EINVAL; } - dev_set_name(&edev->dev, "extcon%lu", - (unsigned long)atomic_inc_return(&edev_no)); - - if (edev->max_supported) { - char *str; - struct extcon_cable *cable; - - edev->cables = kcalloc(edev->max_supported, - sizeof(struct extcon_cable), - GFP_KERNEL); - if (!edev->cables) { - ret = -ENOMEM; - goto err_sysfs_alloc; - } - for (index = 0; index < edev->max_supported; index++) { - cable = &edev->cables[index]; - - str = kasprintf(GFP_KERNEL, "cable.%d", index); - if (!str) { - for (index--; index >= 0; index--) { - cable = &edev->cables[index]; - kfree(cable->attr_g.name); - } - ret = -ENOMEM; - - goto err_alloc_cables; - } - - cable->edev = edev; - cable->cable_index = index; - cable->attrs[0] = &cable->attr_name.attr; - cable->attrs[1] = &cable->attr_state.attr; - cable->attrs[2] = NULL; - cable->attr_g.name = str; - cable->attr_g.attrs = cable->attrs; - - sysfs_attr_init(&cable->attr_name.attr); - cable->attr_name.attr.name = "name"; - cable->attr_name.attr.mode = 0444; - cable->attr_name.show = cable_name_show; - - sysfs_attr_init(&cable->attr_state.attr); - cable->attr_state.attr.name = "state"; - cable->attr_state.attr.mode = 0444; - cable->attr_state.show = cable_state_show; - } - } - if (edev->max_supported && edev->mutually_exclusive) { - char *name; - - /* Count the size of mutually_exclusive array */ - for (index = 0; edev->mutually_exclusive[index]; index++) - ; - - edev->attrs_muex = kcalloc(index + 1, - sizeof(struct attribute *), - GFP_KERNEL); - if (!edev->attrs_muex) { - ret = -ENOMEM; - goto err_muex; - } - - edev->d_attrs_muex = kcalloc(index, - sizeof(struct device_attribute), - GFP_KERNEL); - if (!edev->d_attrs_muex) { - ret = -ENOMEM; - kfree(edev->attrs_muex); - goto err_muex; - } - - for (index = 0; edev->mutually_exclusive[index]; index++) { - name = kasprintf(GFP_KERNEL, "0x%x", - edev->mutually_exclusive[index]); - if (!name) { - for (index--; index >= 0; index--) { - kfree(edev->d_attrs_muex[index].attr. - name); - } - kfree(edev->d_attrs_muex); - kfree(edev->attrs_muex); - ret = -ENOMEM; - goto err_muex; - } - sysfs_attr_init(&edev->d_attrs_muex[index].attr); - edev->d_attrs_muex[index].attr.name = name; - edev->d_attrs_muex[index].attr.mode = 0000; - edev->attrs_muex[index] = &edev->d_attrs_muex[index] - .attr; - } - edev->attr_g_muex.name = muex_name; - edev->attr_g_muex.attrs = edev->attrs_muex; + ret = ida_alloc(&extcon_dev_ids, GFP_KERNEL); + if (ret < 0) + return ret; - } + edev->id = ret; - if (edev->max_supported) { - edev->extcon_dev_type.groups = - kcalloc(edev->max_supported + 2, - sizeof(struct attribute_group *), - GFP_KERNEL); - if (!edev->extcon_dev_type.groups) { - ret = -ENOMEM; - goto err_alloc_groups; - } + dev_set_name(&edev->dev, "extcon%d", edev->id); - edev->extcon_dev_type.name = dev_name(&edev->dev); - edev->extcon_dev_type.release = dummy_sysfs_dev_release; + ret = extcon_alloc_cables(edev); + if (ret < 0) + goto err_alloc_cables; - for (index = 0; index < edev->max_supported; index++) - edev->extcon_dev_type.groups[index] = - &edev->cables[index].attr_g; - if (edev->mutually_exclusive) - edev->extcon_dev_type.groups[index] = - &edev->attr_g_muex; + ret = extcon_alloc_muex(edev); + if (ret < 0) + goto err_alloc_muex; - edev->dev.type = &edev->extcon_dev_type; - } + ret = extcon_alloc_groups(edev); + if (ret < 0) + goto err_alloc_groups; spin_lock_init(&edev->lock); if (edev->max_supported) { @@ -1277,13 +1337,14 @@ err_alloc_groups: kfree(edev->d_attrs_muex); kfree(edev->attrs_muex); } -err_muex: +err_alloc_muex: for (index = 0; index < edev->max_supported; index++) kfree(edev->cables[index].attr_g.name); -err_alloc_cables: if (edev->max_supported) kfree(edev->cables); -err_sysfs_alloc: +err_alloc_cables: + ida_free(&extcon_dev_ids, edev->id); + return ret; } EXPORT_SYMBOL_GPL(extcon_dev_register); @@ -1306,12 +1367,13 @@ void extcon_dev_unregister(struct extcon_dev *edev) list_del(&edev->entry); mutex_unlock(&extcon_dev_list_lock); - if (IS_ERR_OR_NULL(get_device(&edev->dev))) { - dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n", - dev_name(&edev->dev)); + if (!get_device(&edev->dev)) { + dev_err(&edev->dev, "Failed to unregister extcon_dev\n"); return; } + ida_free(&extcon_dev_ids, edev->id); + device_unregister(&edev->dev); if (edev->mutually_exclusive && edev->max_supported) { @@ -1349,7 +1411,7 @@ struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) mutex_lock(&extcon_dev_list_lock); list_for_each_entry(edev, &extcon_dev_list, entry) - if (edev->dev.parent && edev->dev.parent->of_node == node) + if (edev->dev.parent && device_match_of_node(edev->dev.parent, node)) goto out; edev = ERR_PTR(-EPROBE_DEFER); out: @@ -1367,21 +1429,17 @@ out: */ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) { - struct device_node *node; + struct device_node *node, *np = dev_of_node(dev); struct extcon_dev *edev; - if (!dev) - return ERR_PTR(-EINVAL); - - if (!dev->of_node) { + if (!np) { 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); + node = of_parse_phandle(np, "extcon", index); if (!node) { - dev_dbg(dev, "failed to get phandle in %pOF node\n", - dev->of_node); + dev_dbg(dev, "failed to get phandle in %pOF node\n", np); return ERR_PTR(-ENODEV); } diff --git a/drivers/extcon/extcon.h b/drivers/extcon/extcon.h index 93b5e0306966..946182687786 100644 --- a/drivers/extcon/extcon.h +++ b/drivers/extcon/extcon.h @@ -13,13 +13,14 @@ * are disabled. * @mutually_exclusive: Array of mutually exclusive set of cables that cannot * be attached simultaneously. The array should be - * ending with NULL or be NULL (no mutually exclusive - * cables). For example, if it is { 0x7, 0x30, 0}, then, + * ending with 0 or be NULL (no mutually exclusive cables). + * For example, if it is {0x7, 0x30, 0}, then, * {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot * be attached simulataneously. {0x7, 0} is equivalent to * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there * can be no simultaneous connections. * @dev: Device of this extcon. + * @id: Unique device ID of this extcon. * @state: Attach/detach state of this extcon. Do not provide at * register-time. * @nh_all: Notifier for the state change events for all supported @@ -27,7 +28,7 @@ * @nh: Notifier for the state change events from this extcon * @entry: To support list of extcon devices so that users can * search for extcon devices based on the extcon name. - * @lock: + * @lock: Protects device state and serialises device registration * @max_supported: Internal value to store the number of cables. * @extcon_dev_type: Device_type struct to provide attribute_groups * customized for each extcon device. @@ -46,6 +47,7 @@ struct extcon_dev { /* Internal data. Please do not set. */ struct device dev; + unsigned int id; struct raw_notifier_head nh_all; struct raw_notifier_head *nh; struct list_head entry; |