Commit fb9617ed authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'usb-ci-v5.4-rc1' of...

Merge tag 'usb-ci-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb into usb-next

Peter writes:

Add role switch class support for chipidea

* tag 'usb-ci-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb:
  usb: chipidea: msm: Use device-managed registration API
  usb: chipidea: add role switch class support
  dt-binding: usb: usbmisc-imx: add imx7ulp compatible
  dt-binding: usb: ci-hdrc-usb2: add imx7ulp compatible
parents 3dd550a2 ecd55e36
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ Required properties:
	"fsl,imx6sx-usb"
	"fsl,imx6ul-usb"
	"fsl,imx7d-usb"
	"fsl,imx7ulp-usb"
	"lsi,zevio-usb"
	"qcom,ci-hdrc"
	"chipidea,usb2"
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ Required properties:
	"fsl,vf610-usbmisc" for Vybrid vf610
	"fsl,imx6sx-usbmisc" for imx6sx
	"fsl,imx7d-usbmisc" for imx7d
	"fsl,imx7ulp-usbmisc" for imx7ulp
- reg: Should contain registers location and length

Examples:
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ config USB_CHIPIDEA
	select EXTCON
	select RESET_CONTROLLER
	select USB_ULPI_BUS
	select USB_ROLE_SWITCH
	help
	  Say Y here if your system has a dual role high speed USB
	  controller based on ChipIdea silicon IP. It supports:
+12 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>

/******************************************************************************
@@ -217,6 +218,7 @@ struct ci_hdrc {
	ktime_t				hr_timeouts[NUM_OTG_FSM_TIMERS];
	unsigned			enabled_otg_timer_bits;
	enum otg_fsm_timer		next_otg_timer;
	struct usb_role_switch		*role_switch;
	struct work_struct		work;
	struct workqueue_struct		*wq;

@@ -290,6 +292,16 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
	ci->roles[role]->stop(ci);
}

static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
{
	if (ci->role == CI_ROLE_HOST)
		return USB_ROLE_HOST;
	else if (ci->role == CI_ROLE_GADGET && ci->vbus_active)
		return USB_ROLE_DEVICE;
	else
		return USB_ROLE_NONE;
}

/**
 * hw_read_id_reg: reads from a identification register
 * @ci: the controller
+83 −0
Original line number Diff line number Diff line
@@ -600,6 +600,71 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
	return NOTIFY_DONE;
}

static enum usb_role ci_usb_role_switch_get(struct device *dev)
{
	struct ci_hdrc *ci = dev_get_drvdata(dev);
	enum usb_role role;
	unsigned long flags;

	spin_lock_irqsave(&ci->lock, flags);
	role = ci_role_to_usb_role(ci);
	spin_unlock_irqrestore(&ci->lock, flags);

	return role;
}

static int ci_usb_role_switch_set(struct device *dev, enum usb_role role)
{
	struct ci_hdrc *ci = dev_get_drvdata(dev);
	struct ci_hdrc_cable *cable = NULL;
	enum usb_role current_role = ci_role_to_usb_role(ci);
	unsigned long flags;

	if (current_role == role)
		return 0;

	pm_runtime_get_sync(ci->dev);
	/* Stop current role */
	spin_lock_irqsave(&ci->lock, flags);
	if (current_role == USB_ROLE_DEVICE)
		cable = &ci->platdata->vbus_extcon;
	else if (current_role == USB_ROLE_HOST)
		cable = &ci->platdata->id_extcon;

	if (cable) {
		cable->changed = true;
		cable->connected = false;
		ci_irq(ci->irq, ci);
		spin_unlock_irqrestore(&ci->lock, flags);
		if (ci->wq && role != USB_ROLE_NONE)
			flush_workqueue(ci->wq);
		spin_lock_irqsave(&ci->lock, flags);
	}

	cable = NULL;

	/* Start target role */
	if (role == USB_ROLE_DEVICE)
		cable = &ci->platdata->vbus_extcon;
	else if (role == USB_ROLE_HOST)
		cable = &ci->platdata->id_extcon;

	if (cable) {
		cable->changed = true;
		cable->connected = true;
		ci_irq(ci->irq, ci);
	}
	spin_unlock_irqrestore(&ci->lock, flags);
	pm_runtime_put_sync(ci->dev);

	return 0;
}

static struct usb_role_switch_desc ci_role_switch = {
	.set = ci_usb_role_switch_set,
	.get = ci_usb_role_switch_get,
};

static int ci_get_platdata(struct device *dev,
		struct ci_hdrc_platform_data *platdata)
{
@@ -726,6 +791,9 @@ static int ci_get_platdata(struct device *dev,
			cable->connected = false;
	}

	if (device_property_read_bool(dev, "usb-role-switch"))
		ci_role_switch.fwnode = dev->fwnode;

	platdata->pctl = devm_pinctrl_get(dev);
	if (!IS_ERR(platdata->pctl)) {
		struct pinctrl_state *p;
@@ -1047,6 +1115,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
		}
	}

	if (ci_role_switch.fwnode) {
		ci->role_switch = usb_role_switch_register(dev,
					&ci_role_switch);
		if (IS_ERR(ci->role_switch)) {
			ret = PTR_ERR(ci->role_switch);
			goto deinit_otg;
		}
	}

	if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
		if (ci->is_otg) {
			ci->role = ci_otg_role(ci);
@@ -1105,6 +1182,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
	return 0;

stop:
	if (ci->role_switch)
		usb_role_switch_unregister(ci->role_switch);
deinit_otg:
	if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
		ci_hdrc_otg_destroy(ci);
deinit_gadget:
@@ -1123,6 +1203,9 @@ static int ci_hdrc_remove(struct platform_device *pdev)
{
	struct ci_hdrc *ci = platform_get_drvdata(pdev);

	if (ci->role_switch)
		usb_role_switch_unregister(ci->role_switch);

	if (ci->supports_runtime_pm) {
		pm_runtime_get_sync(&pdev->dev);
		pm_runtime_disable(&pdev->dev);
Loading