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

Chanwoo writes:

Update extcon next for v6.5

Detailed description for this pull request:
1. Clean-up extcon core without any behavior changes
- Add extcon_alloc_cables/muex/groups to improve the readability
  of extcon_dev_register.

- Fix kernel doc of property and property capability fields to aovid warnings
  and add missing description of struct extcon_dev.

- Use DECLARE_BITMAP macro and sysfs_emit instead of sprintf

- Use device_match_of_node helper instead of accessing the .of_node

- Use ida_alloc/free to get the unique id for extcon device

2. Update extcon-usbc-tusb320.c to support usb_role_switch and accessory detection
- Add usb_role_switch support on extcon-usbsc-tusb320.

- Add additional accessory detection for audio/debug accessory
  and then pass the deteced accessory information to typec subsystem
  on extcon-usbsc-tusb320.c.

- Add the support of unregistration of typec port on both error handling
  and driver removal step on

3. Update extcon provider drivers (apx288/qcom-spmi-misc/palmas)
- Replace put_device with acpi_dev_put on extcon-axp288.c

- Use platform_get_irq_byname_optional for getting irq of
  usb_id and usb_vbus on extcon-qcom-spmi-misc.c.

- Remove unused of_gpio.h on extcon-palmas.c.

4. Fix the devicetree binding document
- Rename misc node name to 'usb-dect@900' on pm8941-misc.yam

- Fix usb-id and usb_vbus defintion on pm8941-misc.yaml

- Drop unneeded quotes from extcon-arizona.c devicetree documentation

* tag 'extcon-next-for-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon: (26 commits)
  dt-bindings: extcon: wlf,arizona: drop unneeded quotes
  extcon: Switch i2c drivers back to use .probe()
  extcon: Drop unneeded assignments
  extcon: Use sizeof(*pointer) instead of sizeof(type)
  extcon: Use unique number for the extcon device ID
  extcon: Remove dup device name in the message and unneeded error check
  extcon: Use dev_of_node(dev) instead of dev->of_node
  extcon: Use device_match_of_node() helper
  extcon: Amend kernel documentation of struct extcon_dev
  extcon: Use sysfs_emit() to instead of sprintf()
  extcon: Use DECLARE_BITMAP() to declare bit arrays
  extcon: Fix kernel doc of property capability fields to avoid warnings
  extcon: Fix kernel doc of property fields to avoid warnings
  extcon: usbc-tusb320: add USB_ROLE_SWITCH dependency
  extcon: usbc-tusb320: add usb_role_switch support
  extcon: usbc-tusb320: add accessory detection support
  extcon: Add extcon_alloc_groups to simplify extcon register function
  extcon: Add extcon_alloc_muex to simplify extcon register function
  extcon: Add extcon_alloc_cables to simplify extcon register function
  extcon: Remove redundant null checking for class
  ...
This commit is contained in:
Greg Kroah-Hartman
2023-06-19 17:08:18 +02:00
13 changed files with 368 additions and 211 deletions
@@ -27,10 +27,14 @@ properties:
interrupt-names:
minItems: 1
items:
- const: usb_id
- const: usb_vbus
anyOf:
- items:
- const: usb_id
- const: usb_vbus
- items:
- const: usb_id
- items:
- const: usb_vbus
required:
- compatible
- reg
@@ -49,7 +53,7 @@ examples:
interrupt-controller;
#interrupt-cells = <4>;
usb_id: misc@900 {
usb_id: usb-detect@900 {
compatible = "qcom,pm8941-misc";
reg = <0x900>;
interrupts = <0x0 0x9 0 IRQ_TYPE_EDGE_BOTH>;
@@ -23,7 +23,7 @@ properties:
headphone detect mode to HPDETL, ARIZONA_ACCDET_MODE_HPR/2 sets it
to HPDETR. If this node is not included or if the value is unknown,
then headphone detection mode is set to HPDETL.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 2
@@ -51,7 +51,7 @@ properties:
description:
Additional software microphone detection debounce specified in
milliseconds.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
wlf,micd-pol-gpio:
description:
@@ -63,7 +63,7 @@ properties:
description:
Time allowed for MICBIAS to startup prior to performing microphone
detection, specified as per the ARIZONA_MICD_TIME_XXX defines.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 12
@@ -71,7 +71,7 @@ properties:
description:
Delay between successive microphone detection measurements, specified
as per the ARIZONA_MICD_TIME_XXX defines.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 12
@@ -79,7 +79,7 @@ properties:
description:
Microphone detection hardware debounces specified as the number of
measurements to take.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
enum: [2, 4]
wlf,micd-timeout-ms:
@@ -97,7 +97,7 @@ properties:
CTIA / OMTP headsets), the field can be of variable length but
should always be a multiple of 3 cells long, each three cell group
represents one polarity configuration.
$ref: "/schemas/types.yaml#/definitions/uint32-matrix"
$ref: /schemas/types.yaml#/definitions/uint32-matrix
items:
items:
- description:
@@ -119,7 +119,7 @@ properties:
description:
Settings for the general purpose switch, set as one of the
ARIZONA_GPSW_XXX defines.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 3
+1
View File
@@ -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.
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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,
};
-1
View File
@@ -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>
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -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,
};
+1 -1
View File
@@ -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,
};
+126 -33
View File
@@ -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;
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;
typec_set_vconn_role(port, role);
typec_set_pwr_role(port, role);
typec_set_data_role(port, role == TYPEC_SOURCE ?
TYPEC_HOST : TYPEC_DEVICE);
ret = regmap_read(priv->regmap, TUSB320_REG8, &reg8);
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 = 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;
}
dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n",
accessory);
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,
+213 -155
View File
@@ -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;
}
@@ -1069,6 +1080,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;
ret = ida_alloc(&extcon_dev_ids, GFP_KERNEL);
if (ret < 0)
return ret;
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];
edev->id = ret;
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;
dev_set_name(&edev->dev, "extcon%d", edev->id);
goto err_alloc_cables;
}
ret = extcon_alloc_cables(edev);
if (ret < 0)
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;
ret = extcon_alloc_muex(edev);
if (ret < 0)
goto err_alloc_muex;
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;
}
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;
}
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;
}
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);
}
+5 -3
View File
@@ -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;