]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'usb-ci-v4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 4 Feb 2017 08:12:37 +0000 (09:12 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 4 Feb 2017 08:12:37 +0000 (09:12 +0100)
Peter writes:

Hi Greg,

In this series, it adds qualcomm USB2 support. The review process takes
more than half of year, thanks for Stephen Boyd's great work.

Most of patches at linux-next more than ten days, and the last two small
chipidea patches at my tree about one day, no warning is reported from
autobuild robot.

Thanks.

15 files changed:
Documentation/devicetree/bindings/usb/ulpi.txt [new file with mode: 0644]
drivers/of/device.c
drivers/usb/chipidea/Kconfig
drivers/usb/chipidea/Makefile
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_msm.c
drivers/usb/chipidea/ci_hdrc_usb2.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/host.c
drivers/usb/chipidea/otg.c
drivers/usb/chipidea/udc.c
drivers/usb/chipidea/ulpi.c [new file with mode: 0644]
drivers/usb/common/ulpi.c
include/linux/of_device.h
include/linux/usb/chipidea.h

diff --git a/Documentation/devicetree/bindings/usb/ulpi.txt b/Documentation/devicetree/bindings/usb/ulpi.txt
new file mode 100644 (file)
index 0000000..ca179dc
--- /dev/null
@@ -0,0 +1,20 @@
+ULPI bus binding
+----------------
+
+Phys that are behind a ULPI connection can be described with the following
+binding. The host controller shall have a "ulpi" named node as a child, and
+that node shall have one enabled node underneath it representing the ulpi
+device on the bus.
+
+EXAMPLE
+-------
+
+usb {
+       compatible = "vendor,usb-controller";
+
+       ulpi {
+               phy {
+                       compatible = "vendor,phy";
+               };
+       };
+};
index fd5cfad7c4030e58bb817c4b2a56c49b674a1eba..b1e6bebda3f3a0ebee3b09085b6bb1a500f6249e 100644 (file)
@@ -225,6 +225,30 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
 
        return tsize;
 }
+EXPORT_SYMBOL_GPL(of_device_get_modalias);
+
+int of_device_request_module(struct device *dev)
+{
+       char *str;
+       ssize_t size;
+       int ret;
+
+       size = of_device_get_modalias(dev, NULL, 0);
+       if (size < 0)
+               return size;
+
+       str = kmalloc(size + 1, GFP_KERNEL);
+       if (!str)
+               return -ENOMEM;
+
+       of_device_get_modalias(dev, str, size);
+       str[size] = '\0';
+       ret = request_module(str);
+       kfree(str);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(of_device_request_module);
 
 /**
  * of_device_uevent - Display OF related uevent information
@@ -287,3 +311,4 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
index 5e5b9eb7ebf6daa2f8a1764c60fe3dced6bee13f..fc96f5cdcb5cc7744913f560f1c23f70fe6b8250 100644 (file)
@@ -2,6 +2,7 @@ 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
+       select RESET_CONTROLLER
        help
          Say Y here if your system has a dual role high speed USB
          controller based on ChipIdea silicon IP. It supports:
@@ -38,4 +39,11 @@ config USB_CHIPIDEA_HOST
          Say Y here to enable host controller functionality of the
          ChipIdea driver.
 
+config USB_CHIPIDEA_ULPI
+       bool "ChipIdea ULPI PHY support"
+       depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_CHIPIDEA
+       help
+         Say Y here if you have a ULPI PHY attached to your ChipIdea
+         controller.
+
 endif
index 518e445476c3f5c7fed0cb9930700310f04688b6..39fca5715ed335a3ddb59019187be45bd2805144 100644 (file)
@@ -4,6 +4,7 @@ ci_hdrc-y                               := core.o otg.o debug.o
 ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC)     += udc.o
 ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST)    += host.o
 ci_hdrc-$(CONFIG_USB_OTG_FSM)          += otg_fsm.o
+ci_hdrc-$(CONFIG_USB_CHIPIDEA_ULPI)    += ulpi.o
 
 # Glue/Bridge layers go here
 
index cd414559040f12224bda60a0dd0a9e12b597241c..59e22389c10b01c7a37c719c84c32b9b19b9a3e4 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/usb.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg-fsm.h>
+#include <linux/usb/otg.h>
+#include <linux/ulpi/interface.h>
 
 /******************************************************************************
  * DEFINE
@@ -52,6 +54,7 @@ enum ci_hw_regs {
        OP_ENDPTLISTADDR,
        OP_TTCTRL,
        OP_BURSTSIZE,
+       OP_ULPI_VIEWPORT,
        OP_PORTSC,
        OP_DEVLC,
        OP_OTGSC,
@@ -187,6 +190,8 @@ struct hw_bank {
  * @test_mode: the selected test mode
  * @platdata: platform specific information supplied by parent device
  * @vbus_active: is VBUS active
+ * @ulpi: pointer to ULPI device, if any
+ * @ulpi_ops: ULPI read/write ops for this device
  * @phy: pointer to PHY, if any
  * @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
  * @hcd: pointer to usb_hcd for ehci host driver
@@ -236,6 +241,10 @@ struct ci_hdrc {
 
        struct ci_hdrc_platform_data    *platdata;
        int                             vbus_active;
+#ifdef CONFIG_USB_CHIPIDEA_ULPI
+       struct ulpi                     *ulpi;
+       struct ulpi_ops                 ulpi_ops;
+#endif
        struct phy                      *phy;
        /* old usb_phy interface */
        struct usb_phy                  *usb_phy;
@@ -418,6 +427,16 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
 #endif
 }
 
+#if IS_ENABLED(CONFIG_USB_CHIPIDEA_ULPI)
+int ci_ulpi_init(struct ci_hdrc *ci);
+void ci_ulpi_exit(struct ci_hdrc *ci);
+int ci_ulpi_resume(struct ci_hdrc *ci);
+#else
+static inline int ci_ulpi_init(struct ci_hdrc *ci) { return 0; }
+static inline void ci_ulpi_exit(struct ci_hdrc *ci) { }
+static inline int ci_ulpi_resume(struct ci_hdrc *ci) { return 0; }
+#endif
+
 u32 hw_read_intr_enable(struct ci_hdrc *ci);
 
 u32 hw_read_intr_status(struct ci_hdrc *ci);
@@ -428,8 +447,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
 
 u8 hw_port_test_get(struct ci_hdrc *ci);
 
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
-                               u32 value, unsigned int timeout_ms);
+void hw_phymode_configure(struct ci_hdrc *ci);
 
 void ci_platform_configure(struct ci_hdrc *ci);
 
index 3889809fd0c49f61495857323ccb33ab9729ae0b..0bdfcdcbf7a5a4265ecb2be8570015c0e9d08e38 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/usb/msm_hsusb_hw.h>
-#include <linux/usb/ulpi.h>
-#include <linux/usb/gadget.h>
 #include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
+#include <linux/reset-controller.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
 
 #include "ci.h"
 
-#define MSM_USB_BASE   (ci->hw_bank.abs)
+#define HS_PHY_AHB_MODE                        0x0098
 
-static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+#define HS_PHY_GENCONFIG               0x009c
+#define HS_PHY_TXFIFO_IDLE_FORCE_DIS   BIT(4)
+
+#define HS_PHY_GENCONFIG_2             0x00a0
+#define HS_PHY_SESS_VLD_CTRL_EN                BIT(7)
+#define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX  BIT(19)
+
+#define HSPHY_SESS_VLD_CTRL            BIT(25)
+
+/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_CTRL                    0x0040
+#define HS_PHY_SEC_CTRL                        0x0078
+#define HS_PHY_DIG_CLAMP_N             BIT(16)
+#define HS_PHY_POR_ASSERT              BIT(0)
+
+struct ci_hdrc_msm {
+       struct platform_device *ci;
+       struct clk *core_clk;
+       struct clk *iface_clk;
+       struct clk *fs_clk;
+       struct ci_hdrc_platform_data pdata;
+       struct reset_controller_dev rcdev;
+       bool secondary_phy;
+       bool hsic;
+       void __iomem *base;
+};
+
+static int
+ci_hdrc_msm_por_reset(struct reset_controller_dev *r, unsigned long id)
 {
-       struct device *dev = ci->gadget.dev.parent;
+       struct ci_hdrc_msm *ci_msm = container_of(r, struct ci_hdrc_msm, rcdev);
+       void __iomem *addr = ci_msm->base;
+       u32 val;
+
+       if (id)
+               addr += HS_PHY_SEC_CTRL;
+       else
+               addr += HS_PHY_CTRL;
+
+       val = readl_relaxed(addr);
+       val |= HS_PHY_POR_ASSERT;
+       writel(val, addr);
+       /*
+        * wait for minimum 10 microseconds as suggested by manual.
+        * Use a slightly larger value since the exact value didn't
+        * work 100% of the time.
+        */
+       udelay(12);
+       val &= ~HS_PHY_POR_ASSERT;
+       writel(val, addr);
+
+       return 0;
+}
+
+static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
+       .reset = ci_hdrc_msm_por_reset,
+};
+
+static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+{
+       struct device *dev = ci->dev->parent;
+       struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
+       int ret;
 
        switch (event) {
        case CI_HDRC_CONTROLLER_RESET_EVENT:
                dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
-               writel(0, USB_AHBBURST);
+
+               hw_phymode_configure(ci);
+               if (msm_ci->secondary_phy) {
+                       u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
+                       val |= HS_PHY_DIG_CLAMP_N;
+                       writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
+               }
+
+               ret = phy_init(ci->phy);
+               if (ret)
+                       return ret;
+
+               ret = phy_power_on(ci->phy);
+               if (ret) {
+                       phy_exit(ci->phy);
+                       return ret;
+               }
+
                /* use AHB transactor, allow posted data writes */
-               writel(0x8, USB_AHBMODE);
-               usb_phy_init(ci->usb_phy);
+               hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
+
+               /* workaround for rx buffer collision issue */
+               hw_write_id_reg(ci, HS_PHY_GENCONFIG,
+                               HS_PHY_TXFIFO_IDLE_FORCE_DIS, 0);
+
+               if (!msm_ci->hsic)
+                       hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+                                       HS_PHY_ULPI_TX_PKT_EN_CLR_FIX, 0);
+
+               if (!IS_ERR(ci->platdata->vbus_extcon.edev)) {
+                       hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+                                       HS_PHY_SESS_VLD_CTRL_EN,
+                                       HS_PHY_SESS_VLD_CTRL_EN);
+                       hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
+                                HSPHY_SESS_VLD_CTRL);
+
+               }
                break;
        case CI_HDRC_CONTROLLER_STOPPED_EVENT:
                dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
-               /*
-                * Put the phy in non-driving mode. Otherwise host
-                * may not detect soft-disconnection.
-                */
-               usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
+               phy_power_off(ci->phy);
+               phy_exit(ci->phy);
                break;
        default:
                dev_dbg(dev, "unknown ci_hdrc event\n");
                break;
        }
+
+       return 0;
 }
 
-static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
-       .name                   = "ci_hdrc_msm",
-       .capoffset              = DEF_CAPOFFSET,
-       .flags                  = CI_HDRC_REGS_SHARED |
-                                 CI_HDRC_DISABLE_STREAMING,
+static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
+                              struct platform_device *pdev)
+{
+       struct regmap *regmap;
+       struct device *dev = &pdev->dev;
+       struct of_phandle_args args;
+       u32 val;
+       int ret;
 
-       .notify_event           = ci_hdrc_msm_notify_event,
-};
+       ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
+                                              &args);
+       if (ret)
+               return 0;
+
+       regmap = syscon_node_to_regmap(args.np);
+       of_node_put(args.np);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       ret = regmap_write(regmap, args.args[0], args.args[1]);
+       if (ret)
+               return ret;
+
+       ci->secondary_phy = !!args.args[1];
+       if (ci->secondary_phy) {
+               val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
+               val |= HS_PHY_DIG_CLAMP_N;
+               writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
+       }
+
+       return 0;
+}
 
 static int ci_hdrc_msm_probe(struct platform_device *pdev)
 {
+       struct ci_hdrc_msm *ci;
        struct platform_device *plat_ci;
-       struct usb_phy *phy;
+       struct clk *clk;
+       struct reset_control *reset;
+       struct resource *res;
+       int ret;
+       struct device_node *ulpi_node, *phy_node;
 
        dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
 
-       /*
-        * OTG(PHY) driver takes care of PHY initialization, clock management,
-        * powering up VBUS, mapping of registers address space and power
-        * management.
-        */
-       phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
-       if (IS_ERR(phy))
-               return PTR_ERR(phy);
+       ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+       if (!ci)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, ci);
+
+       ci->pdata.name = "ci_hdrc_msm";
+       ci->pdata.capoffset = DEF_CAPOFFSET;
+       ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
+                         CI_HDRC_OVERRIDE_AHB_BURST |
+                         CI_HDRC_OVERRIDE_PHY_CONTROL;
+       ci->pdata.notify_event = ci_hdrc_msm_notify_event;
+
+       reset = devm_reset_control_get(&pdev->dev, "core");
+       if (IS_ERR(reset))
+               return PTR_ERR(reset);
+
+       ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
 
-       ci_hdrc_msm_platdata.usb_phy = phy;
+       ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
 
-       plat_ci = ci_hdrc_add_device(&pdev->dev,
-                               pdev->resource, pdev->num_resources,
-                               &ci_hdrc_msm_platdata);
+       ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
+       if (IS_ERR(clk)) {
+               if (PTR_ERR(clk) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               ci->fs_clk = NULL;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       ci->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ci->base))
+               return PTR_ERR(ci->base);
+
+       ci->rcdev.owner = THIS_MODULE;
+       ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
+       ci->rcdev.of_node = pdev->dev.of_node;
+       ci->rcdev.nr_resets = 2;
+       ret = reset_controller_register(&ci->rcdev);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(ci->fs_clk);
+       if (ret)
+               goto err_fs;
+
+       reset_control_assert(reset);
+       usleep_range(10000, 12000);
+       reset_control_deassert(reset);
+
+       clk_disable_unprepare(ci->fs_clk);
+
+       ret = clk_prepare_enable(ci->core_clk);
+       if (ret)
+               goto err_fs;
+
+       ret = clk_prepare_enable(ci->iface_clk);
+       if (ret)
+               goto err_iface;
+
+       ret = ci_hdrc_msm_mux_phy(ci, pdev);
+       if (ret)
+               goto err_mux;
+
+       ulpi_node = of_find_node_by_name(pdev->dev.of_node, "ulpi");
+       if (ulpi_node) {
+               phy_node = of_get_next_available_child(ulpi_node, NULL);
+               ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy");
+               of_node_put(phy_node);
+       }
+       of_node_put(ulpi_node);
+
+       plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+                                    pdev->num_resources, &ci->pdata);
        if (IS_ERR(plat_ci)) {
-               dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
-               return PTR_ERR(plat_ci);
+               ret = PTR_ERR(plat_ci);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+               goto err_mux;
        }
 
-       platform_set_drvdata(pdev, plat_ci);
+       ci->ci = plat_ci;
 
+       pm_runtime_set_active(&pdev->dev);
        pm_runtime_no_callbacks(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
 
        return 0;
+
+err_mux:
+       clk_disable_unprepare(ci->iface_clk);
+err_iface:
+       clk_disable_unprepare(ci->core_clk);
+err_fs:
+       reset_controller_unregister(&ci->rcdev);
+       return ret;
 }
 
 static int ci_hdrc_msm_remove(struct platform_device *pdev)
 {
-       struct platform_device *plat_ci = platform_get_drvdata(pdev);
+       struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
 
        pm_runtime_disable(&pdev->dev);
-       ci_hdrc_remove_device(plat_ci);
+       ci_hdrc_remove_device(ci->ci);
+       clk_disable_unprepare(ci->iface_clk);
+       clk_disable_unprepare(ci->core_clk);
+       reset_controller_unregister(&ci->rcdev);
 
        return 0;
 }
index 4456d2cf80ffb6f844a94815d43b184de6b3b6f4..d162cc0bb8ceae29edfa1498ef766a8cafaae775 100644 (file)
@@ -74,10 +74,6 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
                }
        }
 
-       ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
-       if (ret)
-               goto clk_err;
-
        ci_pdata->name = dev_name(dev);
 
        priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
index 3dbb4a21ab44c8a6555f5e2ad2a321a61526e23b..79ad8e91632e6d29f3a67a328b559d2b2be7f793 100644 (file)
@@ -62,7 +62,6 @@
 #include <linux/usb/chipidea.h>
 #include <linux/usb/of.h>
 #include <linux/of.h>
-#include <linux/phy.h>
 #include <linux/regulator/consumer.h>
 #include <linux/usb/ehci_def.h>
 
@@ -86,6 +85,7 @@ static const u8 ci_regs_nolpm[] = {
        [OP_ENDPTLISTADDR]      = 0x18U,
        [OP_TTCTRL]             = 0x1CU,
        [OP_BURSTSIZE]          = 0x20U,
+       [OP_ULPI_VIEWPORT]      = 0x30U,
        [OP_PORTSC]             = 0x44U,
        [OP_DEVLC]              = 0x84U,
        [OP_OTGSC]              = 0x64U,
@@ -110,6 +110,7 @@ static const u8 ci_regs_lpm[] = {
        [OP_ENDPTLISTADDR]      = 0x18U,
        [OP_TTCTRL]             = 0x1CU,
        [OP_BURSTSIZE]          = 0x20U,
+       [OP_ULPI_VIEWPORT]      = 0x30U,
        [OP_PORTSC]             = 0x44U,
        [OP_DEVLC]              = 0x84U,
        [OP_OTGSC]              = 0xC4U,
@@ -285,7 +286,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
        return 0;
 }
 
-static void hw_phymode_configure(struct ci_hdrc *ci)
+void hw_phymode_configure(struct ci_hdrc *ci)
 {
        u32 portsc, lpm, sts = 0;
 
@@ -325,6 +326,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
                        hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
        }
 }
+EXPORT_SYMBOL_GPL(hw_phymode_configure);
 
 /**
  * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
@@ -361,6 +363,9 @@ static int _ci_usb_phy_init(struct ci_hdrc *ci)
  */
 static void ci_usb_phy_exit(struct ci_hdrc *ci)
 {
+       if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+               return;
+
        if (ci->phy) {
                phy_power_off(ci->phy);
                phy_exit(ci->phy);
@@ -379,6 +384,9 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
 {
        int ret;
 
+       if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+               return 0;
+
        switch (ci->platdata->phy_mode) {
        case USBPHY_INTERFACE_MODE_UTMI:
        case USBPHY_INTERFACE_MODE_UTMIW:
@@ -419,13 +427,21 @@ void ci_platform_configure(struct ci_hdrc *ci)
        is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC;
        is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
 
-       if (is_device_mode &&
-               (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING))
-               hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+       if (is_device_mode) {
+               phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
+
+               if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
+                       hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+                                USBMODE_CI_SDIS);
+       }
+
+       if (is_host_mode) {
+               phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
 
-       if (is_host_mode &&
-               (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING))
-               hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+               if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)
+                       hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+                                USBMODE_CI_SDIS);
+       }
 
        if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
                if (ci->hw_bank.lpm)
@@ -495,9 +511,12 @@ int hw_device_reset(struct ci_hdrc *ci)
                return ret;
        }
 
-       if (ci->platdata->notify_event)
-               ci->platdata->notify_event(ci,
+       if (ci->platdata->notify_event) {
+               ret = ci->platdata->notify_event(ci,
                        CI_HDRC_CONTROLLER_RESET_EVENT);
+               if (ret)
+                       return ret;
+       }
 
        /* USBMODE should be configured step by step */
        hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
@@ -516,38 +535,6 @@ int hw_device_reset(struct ci_hdrc *ci)
        return 0;
 }
 
-/**
- * hw_wait_reg: wait the register value
- *
- * Sometimes, it needs to wait register value before going on.
- * Eg, when switch to device mode, the vbus value should be lower
- * than OTGSC_BSV before connects to host.
- *
- * @ci: the controller
- * @reg: register index
- * @mask: mast bit
- * @value: the bit value to wait
- * @timeout_ms: timeout in millisecond
- *
- * This function returns an error code if timeout
- */
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
-                               u32 value, unsigned int timeout_ms)
-{
-       unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms);
-
-       while (hw_read(ci, reg, mask) != value) {
-               if (time_after(jiffies, elapse)) {
-                       dev_err(ci->dev, "timeout waiting for %08x in %d\n",
-                                       mask, reg);
-                       return -ETIMEDOUT;
-               }
-               msleep(20);
-       }
-
-       return 0;
-}
-
 static irqreturn_t ci_irq(int irq, void *data)
 {
        struct ci_hdrc *ci = data;
@@ -601,35 +588,14 @@ 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)
+static int ci_cable_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;
+       struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
+       struct ci_hdrc *ci = cbl->ci;
 
-       if (event)
-               id->state = false;
-       else
-               id->state = true;
-
-       id->changed = true;
+       cbl->connected = event;
+       cbl->changed = true;
 
        ci_irq(ci->irq, ci);
        return NOTIFY_DONE;
@@ -738,27 +704,27 @@ static int ci_get_platdata(struct device *dev,
        }
 
        cable = &platdata->vbus_extcon;
-       cable->nb.notifier_call = ci_vbus_notifier;
+       cable->nb.notifier_call = ci_cable_notifier;
        cable->edev = ext_vbus;
 
        if (!IS_ERR(ext_vbus)) {
-               ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
+               ret = extcon_get_state(cable->edev, EXTCON_USB);
                if (ret)
-                       cable->state = true;
+                       cable->connected = true;
                else
-                       cable->state = false;
+                       cable->connected = false;
        }
 
        cable = &platdata->id_extcon;
-       cable->nb.notifier_call = ci_id_notifier;
+       cable->nb.notifier_call = ci_cable_notifier;
        cable->edev = ext_id;
 
        if (!IS_ERR(ext_id)) {
-               ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
+               ret = extcon_get_state(cable->edev, EXTCON_USB_HOST);
                if (ret)
-                       cable->state = false;
+                       cable->connected = true;
                else
-                       cable->state = true;
+                       cable->connected = false;
        }
        return 0;
 }
@@ -771,8 +737,8 @@ static int ci_extcon_register(struct ci_hdrc *ci)
        id = &ci->platdata->id_extcon;
        id->ci = ci;
        if (!IS_ERR(id->edev)) {
-               ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
-                                              &id->nb);
+               ret = devm_extcon_register_notifier(ci->dev, id->edev,
+                                               EXTCON_USB_HOST, &id->nb);
                if (ret < 0) {
                        dev_err(ci->dev, "register ID failed\n");
                        return ret;
@@ -782,11 +748,9 @@ static int ci_extcon_register(struct ci_hdrc *ci)
        vbus = &ci->platdata->vbus_extcon;
        vbus->ci = ci;
        if (!IS_ERR(vbus->edev)) {
-               ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
-                                              &vbus->nb);
+               ret = devm_extcon_register_notifier(ci->dev, 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;
                }
@@ -795,20 +759,6 @@ static int ci_extcon_register(struct ci_hdrc *ci)
        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 +871,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                CI_HDRC_IMX28_WRITE_FIX);
        ci->supports_runtime_pm = !!(ci->platdata->flags &
                CI_HDRC_SUPPORTS_RUNTIME_PM);
+       platform_set_drvdata(pdev, ci);
 
        ret = hw_device_init(ci, base);
        if (ret < 0) {
@@ -928,6 +879,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
+       ret = ci_ulpi_init(ci);
+       if (ret)
+               return ret;
+
        if (ci->platdata->phy) {
                ci->phy = ci->platdata->phy;
        } else if (ci->platdata->usb_phy) {
@@ -938,11 +893,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
                /* if both generic PHY and USB PHY layers aren't enabled */
                if (PTR_ERR(ci->phy) == -ENOSYS &&
-                               PTR_ERR(ci->usb_phy) == -ENXIO)
-                       return -ENXIO;
+                               PTR_ERR(ci->usb_phy) == -ENXIO) {
+                       ret = -ENXIO;
+                       goto ulpi_exit;
+               }
 
-               if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
-                       return -EPROBE_DEFER;
+               if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+                       ret = -EPROBE_DEFER;
+                       goto ulpi_exit;
+               }
 
                if (IS_ERR(ci->phy))
                        ci->phy = NULL;
@@ -1027,7 +986,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                }
        }
 
-       platform_set_drvdata(pdev, ci);
        ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
                        ci->platdata->name, ci);
        if (ret)
@@ -1054,11 +1012,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        if (!ret)
                return 0;
 
-       ci_extcon_unregister(ci);
 stop:
        ci_role_destroy(ci);
 deinit_phy:
        ci_usb_phy_exit(ci);
+ulpi_exit:
+       ci_ulpi_exit(ci);
 
        return ret;
 }
@@ -1074,10 +1033,10 @@ 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);
+       ci_ulpi_exit(ci);
 
        return 0;
 }
@@ -1125,6 +1084,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
 static int ci_controller_resume(struct device *dev)
 {
        struct ci_hdrc *ci = dev_get_drvdata(dev);
+       int ret;
 
        dev_dbg(dev, "at %s\n", __func__);
 
@@ -1134,6 +1094,11 @@ static int ci_controller_resume(struct device *dev)
        }
 
        ci_hdrc_enter_lpm(ci, false);
+
+       ret = ci_ulpi_resume(ci);
+       if (ret)
+               return ret;
+
        if (ci->usb_phy) {
                usb_phy_set_suspend(ci->usb_phy, 0);
                usb_phy_set_wakeup(ci->usb_phy, false);
index 111b0e0b8698b76d0de983e856c89dadea92734e..915f3e91586e68adddce0367c3179753a90fd078 100644 (file)
@@ -90,6 +90,13 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
 
        ehci->need_io_watchdog = 0;
 
+       if (ci->platdata->notify_event) {
+               ret = ci->platdata->notify_event(ci,
+                               CI_HDRC_CONTROLLER_RESET_EVENT);
+               if (ret)
+                       return ret;
+       }
+
        ci_platform_configure(ci);
 
        return ret;
@@ -187,6 +194,9 @@ static void host_stop(struct ci_hdrc *ci)
        struct usb_hcd *hcd = ci->hcd;
 
        if (hcd) {
+               if (ci->platdata->notify_event)
+                       ci->platdata->notify_event(ci,
+                               CI_HDRC_CONTROLLER_STOPPED_EVENT);
                usb_remove_hcd(hcd);
                ci->role = CI_ROLE_END;
                synchronize_irq(ci->irq);
index 03b6743461d15de129a2d7908e851db450111d3c..10236fe7152286f5556c519d14f0b6c6b207c68a 100644 (file)
@@ -44,12 +44,15 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
                else
                        val &= ~OTGSC_BSVIS;
 
-               cable->changed = false;
-
-               if (cable->state)
+               if (cable->connected)
                        val |= OTGSC_BSV;
                else
                        val &= ~OTGSC_BSV;
+
+               if (cable->enabled)
+                       val |= OTGSC_BSVIE;
+               else
+                       val &= ~OTGSC_BSVIE;
        }
 
        cable = &ci->platdata->id_extcon;
@@ -59,15 +62,18 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
                else
                        val &= ~OTGSC_IDIS;
 
-               cable->changed = false;
+               if (cable->connected)
+                       val &= ~OTGSC_ID; /* host */
+               else
+                       val |= OTGSC_ID; /* device */
 
-               if (cable->state)
-                       val |= OTGSC_ID;
+               if (cable->enabled)
+                       val |= OTGSC_IDIE;
                else
-                       val &= ~OTGSC_ID;
+                       val &= ~OTGSC_IDIE;
        }
 
-       return val;
+       return val & mask;
 }
 
 /**
@@ -77,6 +83,36 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
  */
 void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
 {
+       struct ci_hdrc_cable *cable;
+
+       cable = &ci->platdata->vbus_extcon;
+       if (!IS_ERR(cable->edev)) {
+               if (data & mask & OTGSC_BSVIS)
+                       cable->changed = false;
+
+               /* Don't enable vbus interrupt if using external notifier */
+               if (data & mask & OTGSC_BSVIE) {
+                       cable->enabled = true;
+                       data &= ~OTGSC_BSVIE;
+               } else if (mask & OTGSC_BSVIE) {
+                       cable->enabled = false;
+               }
+       }
+
+       cable = &ci->platdata->id_extcon;
+       if (!IS_ERR(cable->edev)) {
+               if (data & mask & OTGSC_IDIS)
+                       cable->changed = false;
+
+               /* Don't enable id interrupt if using external notifier */
+               if (data & mask & OTGSC_IDIE) {
+                       cable->enabled = true;
+                       data &= ~OTGSC_IDIE;
+               } else if (mask & OTGSC_IDIE) {
+                       cable->enabled = false;
+               }
+       }
+
        hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
 }
 
@@ -98,13 +134,37 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
        if (!ci->is_otg)
                return;
 
-       if (hw_read_otgsc(ci, OTGSC_BSV))
+       if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
                usb_gadget_vbus_connect(&ci->gadget);
-       else
+       else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
                usb_gadget_vbus_disconnect(&ci->gadget);
 }
 
-#define CI_VBUS_STABLE_TIMEOUT_MS 5000
+/**
+ * When we switch to device mode, the vbus value should be lower
+ * than OTGSC_BSV before connecting to host.
+ *
+ * @ci: the controller
+ *
+ * This function returns an error code if timeout
+ */
+static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
+{
+       unsigned long elapse = jiffies + msecs_to_jiffies(5000);
+       u32 mask = OTGSC_BSV;
+
+       while (hw_read_otgsc(ci, mask)) {
+               if (time_after(jiffies, elapse)) {
+                       dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
+                                       mask);
+                       return -ETIMEDOUT;
+               }
+               msleep(20);
+       }
+
+       return 0;
+}
+
 static void ci_handle_id_switch(struct ci_hdrc *ci)
 {
        enum ci_role role = ci_otg_role(ci);
@@ -115,12 +175,21 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
 
                ci_role_stop(ci);
 
-               if (role == CI_ROLE_GADGET)
-                       /* wait vbus lower than OTGSC_BSV */
-                       hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
-                                       CI_VBUS_STABLE_TIMEOUT_MS);
+               if (role == CI_ROLE_GADGET &&
+                               IS_ERR(ci->platdata->vbus_extcon.edev))
+                       /*
+                        * Wait vbus lower than OTGSC_BSV before connecting
+                        * to host. If connecting status is from an external
+                        * connector instead of register, we don't need to
+                        * care vbus on the board, since it will not affect
+                        * external connector status.
+                        */
+                       hw_wait_vbus_lower_bsv(ci);
 
                ci_role_start(ci, role);
+               /* vbus change may have already occurred */
+               if (role == CI_ROLE_GADGET)
+                       ci_handle_vbus_change(ci);
        }
 }
 /**
index cf132f0571375c8dfcdc7681ef718516620d38cb..f88e9157fad07ea8443a8c72f036133418721cdf 100644 (file)
@@ -1725,7 +1725,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
                         struct usb_gadget_driver *driver)
 {
        struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
-       unsigned long flags;
        int retval = -ENOMEM;
 
        if (driver->disconnect == NULL)
@@ -1752,7 +1751,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
 
        pm_runtime_get_sync(&ci->gadget.dev);
        if (ci->vbus_active) {
-               spin_lock_irqsave(&ci->lock, flags);
                hw_device_reset(ci);
        } else {
                usb_udc_vbus_handler(&ci->gadget, false);
@@ -1761,7 +1759,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
        }
 
        retval = hw_device_state(ci, ci->ep0out->qh.dma);
-       spin_unlock_irqrestore(&ci->lock, flags);
        if (retval)
                pm_runtime_put_sync(&ci->gadget.dev);
 
@@ -1796,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget)
 
        if (ci->vbus_active) {
                hw_device_state(ci, 0);
+               spin_unlock_irqrestore(&ci->lock, flags);
                if (ci->platdata->notify_event)
                        ci->platdata->notify_event(ci,
                        CI_HDRC_CONTROLLER_STOPPED_EVENT);
-               spin_unlock_irqrestore(&ci->lock, flags);
                _gadget_stop_activity(&ci->gadget);
                spin_lock_irqsave(&ci->lock, flags);
                pm_runtime_put(&ci->gadget.dev);
diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c
new file mode 100644 (file)
index 0000000..1219583
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Linaro Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/ulpi/interface.h>
+
+#include "ci.h"
+
+#define ULPI_WAKEUP            BIT(31)
+#define ULPI_RUN               BIT(30)
+#define ULPI_WRITE             BIT(29)
+#define ULPI_SYNC_STATE                BIT(27)
+#define ULPI_ADDR(n)           ((n) << 16)
+#define ULPI_DATA(n)           (n)
+
+static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
+{
+       unsigned long usec = 10000;
+
+       while (usec--) {
+               if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
+                       return 0;
+
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int ci_ulpi_read(struct device *dev, u8 addr)
+{
+       struct ci_hdrc *ci = dev_get_drvdata(dev);
+       int ret;
+
+       hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+       ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+       if (ret)
+               return ret;
+
+       hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
+       ret = ci_ulpi_wait(ci, ULPI_RUN);
+       if (ret)
+               return ret;
+
+       return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
+}
+
+static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
+{
+       struct ci_hdrc *ci = dev_get_drvdata(dev);
+       int ret;
+
+       hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+       ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+       if (ret)
+               return ret;
+
+       hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
+                ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
+       return ci_ulpi_wait(ci, ULPI_RUN);
+}
+
+int ci_ulpi_init(struct ci_hdrc *ci)
+{
+       if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+               return 0;
+
+       /*
+        * Set PORTSC correctly so we can read/write ULPI registers for
+        * identification purposes
+        */
+       hw_phymode_configure(ci);
+
+       ci->ulpi_ops.read = ci_ulpi_read;
+       ci->ulpi_ops.write = ci_ulpi_write;
+       ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
+       if (IS_ERR(ci->ulpi))
+               dev_err(ci->dev, "failed to register ULPI interface");
+
+       return PTR_ERR_OR_ZERO(ci->ulpi);
+}
+
+void ci_ulpi_exit(struct ci_hdrc *ci)
+{
+       if (ci->ulpi) {
+               ulpi_unregister_interface(ci->ulpi);
+               ci->ulpi = NULL;
+       }
+}
+
+int ci_ulpi_resume(struct ci_hdrc *ci)
+{
+       int cnt = 100000;
+
+       while (cnt-- > 0) {
+               if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
index 8b317702d7610d3dae663df4f1fc0feff87c36f6..c9480d77810c9ff93db21c18ac262a3a046c62b2 100644 (file)
@@ -16,6 +16,9 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk/clk-conf.h>
 
 /* -------------------------------------------------------------------------- */
 
@@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
        struct ulpi *ulpi = to_ulpi_dev(dev);
        const struct ulpi_device_id *id;
 
+       /* Some ULPI devices don't have a vendor id so rely on OF match */
+       if (ulpi->id.vendor == 0)
+               return of_driver_match_device(dev, driver);
+
        for (id = drv->id_table; id->vendor; id++)
                if (id->vendor == ulpi->id.vendor &&
                    id->product == ulpi->id.product)
@@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
 static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct ulpi *ulpi = to_ulpi_dev(dev);
+       int ret;
+
+       ret = of_device_uevent_modalias(dev, env);
+       if (ret != -ENODEV)
+               return ret;
 
        if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
                           ulpi->id.vendor, ulpi->id.product))
@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
 static int ulpi_probe(struct device *dev)
 {
        struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
+       int ret;
+
+       ret = of_clk_set_defaults(dev->of_node, false);
+       if (ret < 0)
+               return ret;
 
        return drv->probe(to_ulpi_dev(dev));
 }
@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = {
 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
+       int len;
        struct ulpi *ulpi = to_ulpi_dev(dev);
 
+       len = of_device_get_modalias(dev, buf, PAGE_SIZE - 1);
+       if (len != -ENODEV)
+               return len;
+
        return sprintf(buf, "ulpi:v%04xp%04x\n",
                       ulpi->id.vendor, ulpi->id.product);
 }
@@ -153,23 +175,45 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
 
 /* -------------------------------------------------------------------------- */
 
-static int ulpi_register(struct device *dev, struct ulpi *ulpi)
+static int ulpi_of_register(struct ulpi *ulpi)
 {
-       int ret;
+       struct device_node *np = NULL, *child;
+       struct device *parent;
+
+       /* Find a ulpi bus underneath the parent or the grandparent */
+       parent = ulpi->dev.parent;
+       if (parent->of_node)
+               np = of_find_node_by_name(parent->of_node, "ulpi");
+       else if (parent->parent && parent->parent->of_node)
+               np = of_find_node_by_name(parent->parent->of_node, "ulpi");
+       if (!np)
+               return 0;
+
+       child = of_get_next_available_child(np, NULL);
+       of_node_put(np);
+       if (!child)
+               return -EINVAL;
 
-       ulpi->dev.parent = dev; /* needed early for ops */
+       ulpi->dev.of_node = child;
+
+       return 0;
+}
+
+static int ulpi_read_id(struct ulpi *ulpi)
+{
+       int ret;
 
        /* Test the interface */
        ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
        if (ret < 0)
-               return ret;
+               goto err;
 
        ret = ulpi_read(ulpi, ULPI_SCRATCH);
        if (ret < 0)
                return ret;
 
        if (ret != 0xaa)
-               return -ENODEV;
+               goto err;
 
        ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
        ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
@@ -177,13 +221,35 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
        ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
        ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
 
+       /* Some ULPI devices don't have a vendor id so rely on OF match */
+       if (ulpi->id.vendor == 0)
+               goto err;
+
+       request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
+       return 0;
+err:
+       of_device_request_module(&ulpi->dev);
+       return 0;
+}
+
+static int ulpi_register(struct device *dev, struct ulpi *ulpi)
+{
+       int ret;
+
+       ulpi->dev.parent = dev; /* needed early for ops */
        ulpi->dev.bus = &ulpi_bus;
        ulpi->dev.type = &ulpi_dev_type;
        dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
 
        ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
 
-       request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
+       ret = ulpi_of_register(ulpi);
+       if (ret)
+               return ret;
+
+       ret = ulpi_read_id(ulpi);
+       if (ret)
+               return ret;
 
        ret = device_register(&ulpi->dev);
        if (ret)
@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
  */
 void ulpi_unregister_interface(struct ulpi *ulpi)
 {
+       of_node_put(ulpi->dev.of_node);
        device_unregister(&ulpi->dev);
 }
 EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
index cc7dd687a89dd60699ebe28556138e293bcde9bc..e9afbcc8de120b2dadbbf193ab20095aef6043d3 100644 (file)
@@ -37,6 +37,7 @@ extern const void *of_device_get_match_data(const struct device *dev);
 
 extern ssize_t of_device_get_modalias(struct device *dev,
                                        char *str, ssize_t len);
+extern int of_device_request_module(struct device *dev);
 
 extern void of_device_uevent(struct device *dev, struct kobj_uevent_env *env);
 extern int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env);
@@ -78,6 +79,11 @@ static inline int of_device_get_modalias(struct device *dev,
        return -ENODEV;
 }
 
+static inline int of_device_request_module(struct device *dev)
+{
+       return -ENODEV;
+}
+
 static inline int of_device_uevent_modalias(struct device *dev,
                                   struct kobj_uevent_env *env)
 {
index 5dd75fa47dd823fbfd2c58ae19d65bb27ee120e1..c5fdfcf998285202cca159fef5b91a4c6d1a10b0 100644 (file)
@@ -12,16 +12,18 @@ struct ci_hdrc;
 
 /**
  * struct ci_hdrc_cable - structure for external connector cable state tracking
- * @state: current state of the line
+ * @connected: true if cable is connected, false otherwise
  * @changed: set to true when extcon event happen
+ * @enabled: set to true if we've enabled the vbus or id interrupt
  * @edev: device which generate events
  * @ci: driver state of the chipidea device
  * @nb: hold event notification callback
  * @conn: used for notification registration
  */
 struct ci_hdrc_cable {
-       bool                            state;
+       bool                            connected;
        bool                            changed;
+       bool                            enabled;
        struct extcon_dev               *edev;
        struct ci_hdrc                  *ci;
        struct notifier_block           nb;
@@ -55,10 +57,11 @@ struct ci_hdrc_platform_data {
 #define CI_HDRC_OVERRIDE_AHB_BURST     BIT(9)
 #define CI_HDRC_OVERRIDE_TX_BURST      BIT(10)
 #define CI_HDRC_OVERRIDE_RX_BURST      BIT(11)
+#define CI_HDRC_OVERRIDE_PHY_CONTROL   BIT(12) /* Glue layer manages phy */
        enum usb_dr_mode        dr_mode;
 #define CI_HDRC_CONTROLLER_RESET_EVENT         0
 #define CI_HDRC_CONTROLLER_STOPPED_EVENT       1
-       void    (*notify_event) (struct ci_hdrc *ci, unsigned event);
+       int     (*notify_event) (struct ci_hdrc *ci, unsigned event);
        struct regulator        *reg_vbus;
        struct usb_otg_caps     ci_otg_caps;
        bool                    tpl_support;