summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@nxp.com>2017-03-27 10:54:27 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commit8bcbfab5025e9c335a7e1a0e1056e0df62135af0 (patch)
treee9e2a8dfeae2d91a533ad2b3b85dd00652756f97
parentc71da1df49119f603a7c0bed2d01c6c4c4571b1b (diff)
usb: chipidea: core: add sysfs group
Sometimes, the user needs to adjust some properties for controllers, eg the role for controller, we add sysfs group for them. The attribute 'role' is used to switch host/gadget role dynamically, the uewr can read the current role, and write the other role compare to current one to finish the switch. Signed-off-by: Peter Chen <peter.chen@kernel.org> Signed-off-by: Peter Chen <peter.chen@nxp.com>
-rw-r--r--Documentation/ABI/testing/sysfs-platform-chipidea-usb29
-rw-r--r--drivers/usb/chipidea/core.c63
-rw-r--r--drivers/usb/chipidea/udc.c2
3 files changed, 72 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-chipidea-usb2 b/Documentation/ABI/testing/sysfs-platform-chipidea-usb2
new file mode 100644
index 000000000000..b0f4684a83fe
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-chipidea-usb2
@@ -0,0 +1,9 @@
+What: /sys/bus/platform/devices/ci_hdrc.0/role
+Date: Mar 2017
+Contact: Peter Chen <peter.chen@nxp.com>
+Description:
+ It returns string "gadget" or "host" when read it, it indicates
+ current controller role.
+
+ It will do role switch when write "gadget" or "host" to it.
+ Only controller at dual-role configuration supports writing.
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index f39f78478f6d..a9b3795a150b 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -894,6 +894,56 @@ static void ci_power_lost_work(struct work_struct *work)
enable_irq(ci->irq);
}
+static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", ci_role(ci)->name);
+}
+
+static ssize_t ci_role_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ enum ci_role role;
+ int ret;
+
+ if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) {
+ dev_warn(dev, "Current configuration is not dual-role, quit\n");
+ return -EPERM;
+ }
+
+ for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
+ if (!strncmp(buf, ci->roles[role]->name,
+ strlen(ci->roles[role]->name)))
+ break;
+
+ if (role == CI_ROLE_END || role == ci->role)
+ return -EINVAL;
+
+ pm_runtime_get_sync(dev);
+ disable_irq(ci->irq);
+ ci_role_stop(ci);
+ ret = ci_role_start(ci, role);
+ if (!ret && ci->role == CI_ROLE_GADGET)
+ ci_handle_vbus_change(ci);
+ enable_irq(ci->irq);
+ pm_runtime_put_sync(dev);
+
+ return (ret == 0) ? n : ret;
+}
+static DEVICE_ATTR(role, 0644, ci_role_show, ci_role_store);
+
+static struct attribute *ci_attrs[] = {
+ &dev_attr_role.attr,
+ NULL,
+};
+
+static struct attribute_group ci_attr_group = {
+ .attrs = ci_attrs,
+};
+
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1048,9 +1098,17 @@ static int ci_hdrc_probe(struct platform_device *pdev)
mutex_init(&ci->mutex);
ret = dbg_create_files(ci);
- if (!ret)
- return 0;
+ if (ret)
+ goto stop;
+
+ ret = sysfs_create_group(&dev->kobj, &ci_attr_group);
+ if (ret)
+ goto remove_debug;
+ return 0;
+
+remove_debug:
+ dbg_remove_files(ci);
stop:
if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
ci_hdrc_otg_destroy(ci);
@@ -1075,6 +1133,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
+ sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 9e0fbb98d3ef..cf24527d24eb 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -2096,6 +2096,8 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
ci->fsm.otg->state == OTG_STATE_B_IDLE ||
ci->fsm.otg->state == OTG_STATE_UNDEFINED)
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
+
+ ci->vbus_active = 0;
}
static void udc_suspend_for_power_lost(struct ci_hdrc *ci)