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

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

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

Peter writes:

USB Chipidea updates for v4.4-rc1

- Use extcon framework for VBUS and ID detect
- Add imx6sx and imx7d support
- Other small changes
parents a4d8e93c 851ce932
Loading
Loading
Loading
Loading
+21 −6
Original line number Diff line number Diff line
@@ -27,10 +27,6 @@ Optional properties:
- vbus-supply: reference to the VBUS regulator
- maximum-speed: limit the maximum connection speed to "full-speed".
- tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts
- fsl,usbmisc: (FSL only) phandler of non-core register device, with one
  argument that indicate usb controller index
- disable-over-current: (FSL only) disable over current detect
- external-vbus-divider: (FSL only) enables off-chip resistor divider for Vbus
- itc-setting: interrupt threshold control register control, the setting
  should be aligned with ITC bits at register USBCMD.
- ahb-burst-config: it is vendor dependent, the required value should be
@@ -41,11 +37,28 @@ Optional properties:
- tx-burst-size-dword: it is vendor dependent, the tx burst size in dword
  (4 bytes), This register represents the maximum length of a the burst
  in 32-bit words while moving data from system memory to the USB
  bus, changing this value takes effect only the SBUSCFG.AHBBRST is 0.
  bus, the value of this property will only take effect if property
  "ahb-burst-config" is set to 0, if this property is missing the reset
  default of the hardware implementation will be used.
- rx-burst-size-dword: it is vendor dependent, the rx burst size in dword
  (4 bytes), This register represents the maximum length of a the burst
  in 32-bit words while moving data from the USB bus to system memory,
  changing this value takes effect only the SBUSCFG.AHBBRST is 0.
  the value of this property will only take effect if property
  "ahb-burst-config" is set to 0, if this property is missing the reset
  default of the hardware implementation will be used.
- extcon: phandles to external connector devices. First phandle should point to
  external connector, which provide "USB" cable events, the second should point
  to external connector device, which provide "USB-HOST" cable events. If one
  of the external connector devices is not required, empty <0> phandle should
  be specified.
- phy-clkgate-delay-us: the delay time (us) between putting the PHY into
  low power mode and gating the PHY clock.

i.mx specific properties
- fsl,usbmisc: phandler of non-core register device, with one
  argument that indicate usb controller index
- disable-over-current: disable over current detect
- external-vbus-divider: enables off-chip resistor divider for Vbus

Example:

@@ -62,4 +75,6 @@ Example:
		ahb-burst-config = <0x0>;
		tx-burst-size-dword = <0x10>; /* 64 bytes */
		rx-burst-size-dword = <0x10>;
		extcon = <0>, <&usb_id>;
		phy-clkgate-delay-us = <400>;
	};
+1 −0
Original line number Diff line number Diff line
config USB_CHIPIDEA
	tristate "ChipIdea Highspeed Dual Role Controller"
	depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
	select EXTCON
	help
	  Say Y here if your system has a dual role high speed USB
	  controller based on ChipIdea silicon IP. Currently, only the
+11 −0
Original line number Diff line number Diff line
@@ -56,12 +56,23 @@ static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
		CI_HDRC_DISABLE_HOST_STREAMING,
};

static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
		CI_HDRC_TURN_VBUS_EARLY_ON,
};

static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
};

static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
+3 −3
Original line number Diff line number Diff line
@@ -142,16 +142,16 @@ static const struct pci_device_id ci_hdrc_pci_id_table[] = {
		.driver_data = (kernel_ulong_t)&pci_platdata,
	},
	{
		PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811),
		PCI_VDEVICE(INTEL, 0x0811),
		.driver_data = (kernel_ulong_t)&langwell_pci_platdata,
	},
	{
		PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829),
		PCI_VDEVICE(INTEL, 0x0829),
		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
	},
	{
		/* Intel Clovertrail */
		PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006),
		PCI_VDEVICE(INTEL, 0xe006),
		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
	},
	{ 0 } /* end: all zeroes */
+132 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/extcon.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/module.h>
@@ -602,9 +603,45 @@ static irqreturn_t ci_irq(int irq, void *data)
	return ret;
}

static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event,
			    void *ptr)
{
	struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb);
	struct ci_hdrc *ci = vbus->ci;

	if (event)
		vbus->state = true;
	else
		vbus->state = false;

	vbus->changed = true;

	ci_irq(ci->irq, ci);
	return NOTIFY_DONE;
}

static int ci_id_notifier(struct notifier_block *nb, unsigned long event,
			  void *ptr)
{
	struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb);
	struct ci_hdrc *ci = id->ci;

	if (event)
		id->state = false;
	else
		id->state = true;

	id->changed = true;

	ci_irq(ci->irq, ci);
	return NOTIFY_DONE;
}

static int ci_get_platdata(struct device *dev,
		struct ci_hdrc_platform_data *platdata)
{
	struct extcon_dev *ext_vbus, *ext_id;
	struct ci_hdrc_cable *cable;
	int ret;

	if (!platdata->phy_mode)
@@ -651,6 +688,10 @@ static int ci_get_platdata(struct device *dev,
	if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
		platdata->flags |= CI_HDRC_FORCE_FULLSPEED;

	if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL))
		of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
				     &platdata->phy_clkgate_delay_us);

	platdata->itc_setting = 1;
	if (of_find_property(dev->of_node, "itc-setting", NULL)) {
		ret = of_property_read_u32(dev->of_node, "itc-setting",
@@ -695,9 +736,91 @@ static int ci_get_platdata(struct device *dev,
		platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
	}

	ext_id = ERR_PTR(-ENODEV);
	ext_vbus = ERR_PTR(-ENODEV);
	if (of_property_read_bool(dev->of_node, "extcon")) {
		/* Each one of them is not mandatory */
		ext_vbus = extcon_get_edev_by_phandle(dev, 0);
		if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
			return PTR_ERR(ext_vbus);

		ext_id = extcon_get_edev_by_phandle(dev, 1);
		if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
			return PTR_ERR(ext_id);
	}

	cable = &platdata->vbus_extcon;
	cable->nb.notifier_call = ci_vbus_notifier;
	cable->edev = ext_vbus;

	if (!IS_ERR(ext_vbus)) {
		ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
		if (ret)
			cable->state = true;
		else
			cable->state = false;
	}

	cable = &platdata->id_extcon;
	cable->nb.notifier_call = ci_id_notifier;
	cable->edev = ext_id;

	if (!IS_ERR(ext_id)) {
		ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
		if (ret)
			cable->state = false;
		else
			cable->state = true;
	}
	return 0;
}

static int ci_extcon_register(struct ci_hdrc *ci)
{
	struct ci_hdrc_cable *id, *vbus;
	int ret;

	id = &ci->platdata->id_extcon;
	id->ci = ci;
	if (!IS_ERR(id->edev)) {
		ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
					       &id->nb);
		if (ret < 0) {
			dev_err(ci->dev, "register ID failed\n");
			return ret;
		}
	}

	vbus = &ci->platdata->vbus_extcon;
	vbus->ci = ci;
	if (!IS_ERR(vbus->edev)) {
		ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
					       &vbus->nb);
		if (ret < 0) {
			extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
						   &id->nb);
			dev_err(ci->dev, "register VBUS failed\n");
			return ret;
		}
	}

	return 0;
}

static void ci_extcon_unregister(struct ci_hdrc *ci)
{
	struct ci_hdrc_cable *cable;

	cable = &ci->platdata->id_extcon;
	if (!IS_ERR(cable->edev))
		extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST,
					   &cable->nb);

	cable = &ci->platdata->vbus_extcon;
	if (!IS_ERR(cable->edev))
		extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb);
}

static DEFINE_IDA(ci_ida);

struct platform_device *ci_hdrc_add_device(struct device *dev,
@@ -921,6 +1044,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
	if (ret)
		goto stop;

	ret = ci_extcon_register(ci);
	if (ret)
		goto stop;

	if (ci->supports_runtime_pm) {
		pm_runtime_set_active(&pdev->dev);
		pm_runtime_enable(&pdev->dev);
@@ -938,6 +1065,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
	if (!ret)
		return 0;

	ci_extcon_unregister(ci);
stop:
	ci_role_destroy(ci);
deinit_phy:
@@ -957,6 +1085,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
	}

	dbg_remove_files(ci);
	ci_extcon_unregister(ci);
	ci_role_destroy(ci);
	ci_hdrc_enter_lpm(ci, true);
	ci_usb_phy_exit(ci);
@@ -996,6 +1125,9 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
{
	disable_irq(ci->irq);
	ci_hdrc_enter_lpm(ci, true);
	if (ci->platdata->phy_clkgate_delay_us)
		usleep_range(ci->platdata->phy_clkgate_delay_us,
			     ci->platdata->phy_clkgate_delay_us + 50);
	usb_phy_set_suspend(ci->usb_phy, 1);
	ci->in_lpm = true;
	enable_irq(ci->irq);
Loading