Merge tag 'phy-for-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Aug 2015 23:45:51 +0000 (16:45 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Aug 2015 23:45:51 +0000 (16:45 -0700)
Kishon writes:

phy: for 4.3

*) Add new NXP USB OTG PHY driver
*) Add vbus/id detection, extcon support and fixes in phy-sun4i-usb
   driver
*) Add support to use phy-sun4i-usb driver for sun8i-a23 and sun8i-a33
   SoCs
*) Other trivial code cleanups, dropping .owner assignment and constify
   phy_ops

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
37 files changed:
Documentation/devicetree/bindings/phy/phy-lpc18xx-usb-otg.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/phy-armada375-usb2.c
drivers/phy/phy-bcm-kona-usb2.c
drivers/phy/phy-berlin-sata.c
drivers/phy/phy-berlin-usb.c
drivers/phy/phy-brcmstb-sata.c
drivers/phy/phy-dm816x-usb.c
drivers/phy/phy-exynos-dp-video.c
drivers/phy/phy-exynos-mipi-video.c
drivers/phy/phy-exynos5-usbdrd.c
drivers/phy/phy-exynos5250-sata.c
drivers/phy/phy-hix5hd2-sata.c
drivers/phy/phy-lpc18xx-usb-otg.c [new file with mode: 0644]
drivers/phy/phy-miphy28lp.c
drivers/phy/phy-miphy365x.c
drivers/phy/phy-mvebu-sata.c
drivers/phy/phy-omap-usb2.c
drivers/phy/phy-qcom-apq8064-sata.c
drivers/phy/phy-qcom-ipq806x-sata.c
drivers/phy/phy-qcom-ufs-i.h
drivers/phy/phy-qcom-ufs-qmp-14nm.c
drivers/phy/phy-qcom-ufs-qmp-20nm.c
drivers/phy/phy-qcom-ufs.c
drivers/phy/phy-rcar-gen2.c
drivers/phy/phy-rockchip-usb.c
drivers/phy/phy-samsung-usb2.c
drivers/phy/phy-spear1310-miphy.c
drivers/phy/phy-spear1340-miphy.c
drivers/phy/phy-stih41x-usb.c
drivers/phy/phy-sun4i-usb.c
drivers/phy/phy-sun9i-usb.c
drivers/phy/phy-ti-pipe3.c
drivers/phy/phy-tusb1210.c
drivers/phy/ulpi_phy.h

diff --git a/Documentation/devicetree/bindings/phy/phy-lpc18xx-usb-otg.txt b/Documentation/devicetree/bindings/phy/phy-lpc18xx-usb-otg.txt
new file mode 100644 (file)
index 0000000..bd61b46
--- /dev/null
@@ -0,0 +1,26 @@
+NXP LPC18xx/43xx internal USB OTG PHY binding
+---------------------------------------------
+
+This file contains documentation for the internal USB OTG PHY found
+in NXP LPC18xx and LPC43xx SoCs.
+
+Required properties:
+- compatible   : must be "nxp,lpc1850-usb-otg-phy"
+- clocks       : must be exactly one entry
+See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+- #phy-cells   : must be 0 for this phy
+See: Documentation/devicetree/bindings/phy/phy-bindings.txt
+
+The phy node must be a child of the creg syscon node.
+
+Example:
+creg: syscon@40043000 {
+       compatible = "nxp,lpc1850-creg", "syscon", "simple-mfd";
+       reg = <0x40043000 0x1000>;
+
+       usb0_otg_phy: phy@004 {
+               compatible = "nxp,lpc1850-usb-otg-phy";
+               clocks = <&ccu1 CLK_USB0>;
+               #phy-cells = <0>;
+       };
+};
index 16528b9eb561eca291ed8ee9f32017ff162f2fb4..0cebf745451760d103097ee73e5160b285610d81 100644 (file)
@@ -7,6 +7,8 @@ Required properties:
   * allwinner,sun5i-a13-usb-phy
   * allwinner,sun6i-a31-usb-phy
   * allwinner,sun7i-a20-usb-phy
+  * allwinner,sun8i-a23-usb-phy
+  * allwinner,sun8i-a33-usb-phy
 - reg : a list of offset + length pairs
 - reg-names :
   * "phy_ctrl"
@@ -17,12 +19,21 @@ Required properties:
 - clock-names :
   * "usb_phy" for sun4i, sun5i or sun7i
   * "usb0_phy", "usb1_phy" and "usb2_phy" for sun6i
+  * "usb0_phy", "usb1_phy" for sun8i
 - resets : a list of phandle + reset specifier pairs
 - reset-names :
   * "usb0_reset"
   * "usb1_reset"
   * "usb2_reset" for sun4i, sun6i or sun7i
 
+Optional properties:
+- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
+- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
+- usb0_vbus_power-supply: power-supply phandle for usb0 vbus presence detect
+- usb0_vbus-supply : regulator phandle for controller usb0 vbus
+- usb1_vbus-supply : regulator phandle for controller usb1 vbus
+- usb2_vbus-supply : regulator phandle for controller usb2 vbus
+
 Example:
        usbphy: phy@0x01c13400 {
                #phy-cells = <1>;
@@ -32,6 +43,13 @@ Example:
                reg-names = "phy_ctrl", "pmu1", "pmu2";
                clocks = <&usb_clk 8>;
                clock-names = "usb_phy";
-               resets = <&usb_clk 1>, <&usb_clk 2>;
-               reset-names = "usb1_reset", "usb2_reset";
+               resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
+               reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
+               pinctrl-names = "default";
+               pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
+               usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
+               usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
+               usb0_vbus-supply = <&reg_usb0_vbus>;
+               usb1_vbus-supply = <&reg_usb1_vbus>;
+               usb2_vbus-supply = <&reg_usb2_vbus>;
        };
index 6b8dd162f644214ba24fd666b5aa6c94467d964d..47da573d0babd8bb9805c8cdb9a9d40323567bf8 100644 (file)
@@ -54,6 +54,17 @@ config PHY_EXYNOS_MIPI_VIDEO
          Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
          and EXYNOS SoCs.
 
+config PHY_LPC18XX_USB_OTG
+       tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver"
+       depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
+       depends on MFD_SYSCON
+       select GENERIC_PHY
+       help
+         Enable this to support NXP LPC18xx/43xx internal USB OTG PHY.
+
+         This driver is need for USB0 support on LPC18xx/43xx and takes
+         care of enabling and clock setup.
+
 config PHY_PXA_28NM_HSIC
        tristate "Marvell USB HSIC 28nm PHY Driver"
        depends on HAS_IOMEM
@@ -199,6 +210,8 @@ config PHY_SUN4I_USB
        tristate "Allwinner sunxi SoC USB PHY driver"
        depends on ARCH_SUNXI && HAS_IOMEM && OF
        depends on RESET_CONTROLLER
+       depends on EXTCON
+       depends on POWER_SUPPLY
        select GENERIC_PHY
        help
          Enable this to support the transceiver that is part of Allwinner
index f344e1b2e825c2fe5124270beb0561672a5c6ccf..a5b18c18fc12d78fce9c19021a0847f49ece504b 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY)        += phy-armada375-usb2.o
 obj-$(CONFIG_BCM_KONA_USB2_PHY)                += phy-bcm-kona-usb2.o
 obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)      += phy-exynos-dp-video.o
 obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)    += phy-exynos-mipi-video.o
+obj-$(CONFIG_PHY_LPC18XX_USB_OTG)      += phy-lpc18xx-usb-otg.o
 obj-$(CONFIG_PHY_PXA_28NM_USB2)                += phy-pxa-28nm-usb2.o
 obj-$(CONFIG_PHY_PXA_28NM_HSIC)                += phy-pxa-28nm-hsic.o
 obj-$(CONFIG_PHY_MVEBU_SATA)           += phy-mvebu-sata.o
index 8ccc3952c13dca6287b04a6b62fe50d1ceb076d6..1a3db288c0a9ac81ade8eadab50429931c1898af 100644 (file)
@@ -51,7 +51,7 @@ static int armada375_usb_phy_init(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops armada375_usb_phy_ops = {
+static const struct phy_ops armada375_usb_phy_ops = {
        .init = armada375_usb_phy_init,
        .owner = THIS_MODULE,
 };
@@ -149,7 +149,6 @@ static struct platform_driver armada375_usb_phy_driver = {
        .driver = {
                .of_match_table = of_usb_cluster_table,
                .name  = "armada-375-usb-cluster",
-               .owner = THIS_MODULE,
        }
 };
 module_platform_driver(armada375_usb_phy_driver);
index ef2dc1aab2b9b5ed03cf0511da89c6da1f78393d..7b67fe49e30bf7187cfc8ee24cad70a0dafedfd1 100644 (file)
@@ -91,7 +91,7 @@ static int bcm_kona_usb_phy_power_off(struct phy *gphy)
        return 0;
 }
 
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .init           = bcm_kona_usb_phy_init,
        .power_on       = bcm_kona_usb_phy_power_on,
        .power_off      = bcm_kona_usb_phy_power_off,
index 6f3e06d687de99260b0300e10d0864387d740177..0062027afb1ef90335ae46782dba06448949b989 100644 (file)
@@ -176,7 +176,7 @@ static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
        return priv->phys[i]->phy;
 }
 
-static struct phy_ops phy_berlin_sata_ops = {
+static const struct phy_ops phy_berlin_sata_ops = {
        .power_on       = phy_berlin_sata_power_on,
        .power_off      = phy_berlin_sata_power_off,
        .owner          = THIS_MODULE,
index 335e06d66ed9a5100cbdda511e6c206238651814..797ba17c404f74053c460c4c7e5a9724d4e8bb5b 100644 (file)
@@ -147,12 +147,12 @@ static int phy_berlin_usb_power_on(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops phy_berlin_usb_ops = {
+static const struct phy_ops phy_berlin_usb_ops = {
        .power_on       = phy_berlin_usb_power_on,
        .owner          = THIS_MODULE,
 };
 
-static const struct of_device_id phy_berlin_sata_of_match[] = {
+static const struct of_device_id phy_berlin_usb_of_match[] = {
        {
                .compatible = "marvell,berlin2-usb-phy",
                .data = &phy_berlin_pll_dividers[0],
@@ -163,12 +163,12 @@ static const struct of_device_id phy_berlin_sata_of_match[] = {
        },
        { },
 };
-MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
+MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match);
 
 static int phy_berlin_usb_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match =
-               of_match_device(phy_berlin_sata_of_match, &pdev->dev);
+               of_match_device(phy_berlin_usb_of_match, &pdev->dev);
        struct phy_berlin_usb_priv *priv;
        struct resource *res;
        struct phy *phy;
@@ -207,9 +207,8 @@ static struct platform_driver phy_berlin_usb_driver = {
        .probe  = phy_berlin_usb_probe,
        .driver = {
                .name           = "phy-berlin-usb",
-               .owner          = THIS_MODULE,
-               .of_match_table = phy_berlin_sata_of_match,
-        },
+               .of_match_table = phy_berlin_usb_of_match,
+       },
 };
 module_platform_driver(phy_berlin_usb_driver);
 
index b7e303d28caff3d5eeb4cceade89c6a66047fdfa..8a2cb16a1937d20e88b045868beec28db966be5c 100644 (file)
@@ -122,7 +122,7 @@ static int brcm_sata_phy_init(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops phy_ops_28nm = {
+static const struct phy_ops phy_ops_28nm = {
        .init           = brcm_sata_phy_init,
        .owner          = THIS_MODULE,
 };
index 7b42555ddd51aac0d44714ef2254278d3fe59215..b4bbef664d206b4a73cd665986bd12eb988de285 100644 (file)
@@ -113,7 +113,7 @@ static int dm816x_usb_phy_init(struct phy *x)
        return 0;
 }
 
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .init           = dm816x_usb_phy_init,
        .owner          = THIS_MODULE,
 };
index 179cbf9451aacf1aca440e46eb05493f41037e48..34b06154e5d959b8b2b0f429e62dd9ae0355151f 100644 (file)
@@ -48,7 +48,7 @@ static int exynos_dp_video_phy_power_off(struct phy *phy)
                                  EXYNOS5_PHY_ENABLE, 0);
 }
 
-static struct phy_ops exynos_dp_video_phy_ops = {
+static const struct phy_ops exynos_dp_video_phy_ops = {
        .power_on       = exynos_dp_video_phy_power_on,
        .power_off      = exynos_dp_video_phy_power_off,
        .owner          = THIS_MODULE,
index df7519a39ba0b15fd9181a3c3c9bcba17fcd34e9..2a54caba93b4021a5b48c44100ec42e9baf45da4 100644 (file)
@@ -124,7 +124,7 @@ static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
        return state->phys[args->args[0]].phy;
 }
 
-static struct phy_ops exynos_mipi_video_phy_ops = {
+static const struct phy_ops exynos_mipi_video_phy_ops = {
        .power_on       = exynos_mipi_video_phy_power_on,
        .power_off      = exynos_mipi_video_phy_power_off,
        .owner          = THIS_MODULE,
index d72ef15b0d68d575917fc6293e8e9bf6fc0e5342..20696f53303f0798059f45273cf817cb936ea415 100644 (file)
@@ -537,7 +537,7 @@ static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
        return phy_drd->phys[args->args[0]].phy;
 }
 
-static struct phy_ops exynos5_usbdrd_phy_ops = {
+static const struct phy_ops exynos5_usbdrd_phy_ops = {
        .init           = exynos5_usbdrd_phy_init,
        .exit           = exynos5_usbdrd_phy_exit,
        .power_on       = exynos5_usbdrd_phy_power_on,
index bc858cc800a12bbbedad8bf18586cd9d9ee1ef78..60e13afcd9b849c568c15c543a3564fa9b5b1571 100644 (file)
@@ -154,7 +154,7 @@ static int exynos_sata_phy_init(struct phy *phy)
        return ret;
 }
 
-static struct phy_ops exynos_sata_phy_ops = {
+static const struct phy_ops exynos_sata_phy_ops = {
        .init           = exynos_sata_phy_init,
        .power_on       = exynos_sata_phy_power_on,
        .power_off      = exynos_sata_phy_power_off,
index d6b22659cac11cfb4187cf9347ac86774f79de5e..e5ab3aa78b9d2420a3564f802630c33b1036ce53 100644 (file)
@@ -129,7 +129,7 @@ static int hix5hd2_sata_phy_init(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops hix5hd2_sata_phy_ops = {
+static const struct phy_ops hix5hd2_sata_phy_ops = {
        .init           = hix5hd2_sata_phy_init,
        .owner          = THIS_MODULE,
 };
diff --git a/drivers/phy/phy-lpc18xx-usb-otg.c b/drivers/phy/phy-lpc18xx-usb-otg.c
new file mode 100644 (file)
index 0000000..3b7a71e
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * PHY driver for NXP LPC18xx/43xx internal USB OTG PHY
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* USB OTG PHY register offset and bit in CREG */
+#define LPC18XX_CREG_CREG0             0x004
+#define LPC18XX_CREG_CREG0_USB0PHY     BIT(5)
+
+struct lpc18xx_usb_otg_phy {
+       struct phy *phy;
+       struct clk *clk;
+       struct regmap *reg;
+};
+
+static int lpc18xx_usb_otg_phy_init(struct phy *phy)
+{
+       struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
+       int ret;
+
+       /* The PHY must be clocked at 480 MHz */
+       ret = clk_set_rate(lpc->clk, 480000000);
+       if (ret)
+               return ret;
+
+       return clk_prepare(lpc->clk);
+}
+
+static int lpc18xx_usb_otg_phy_exit(struct phy *phy)
+{
+       struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
+
+       clk_unprepare(lpc->clk);
+
+       return 0;
+}
+
+static int lpc18xx_usb_otg_phy_power_on(struct phy *phy)
+{
+       struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
+       int ret;
+
+       ret = clk_enable(lpc->clk);
+       if (ret)
+               return ret;
+
+       /* The bit in CREG is cleared to enable the PHY */
+       return regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
+                                 LPC18XX_CREG_CREG0_USB0PHY, 0);
+}
+
+static int lpc18xx_usb_otg_phy_power_off(struct phy *phy)
+{
+       struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
+       int ret;
+
+       ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
+                                LPC18XX_CREG_CREG0_USB0PHY,
+                                LPC18XX_CREG_CREG0_USB0PHY);
+       if (ret)
+               return ret;
+
+       clk_disable(lpc->clk);
+
+       return 0;
+}
+
+static const struct phy_ops lpc18xx_usb_otg_phy_ops = {
+       .init           = lpc18xx_usb_otg_phy_init,
+       .exit           = lpc18xx_usb_otg_phy_exit,
+       .power_on       = lpc18xx_usb_otg_phy_power_on,
+       .power_off      = lpc18xx_usb_otg_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int lpc18xx_usb_otg_phy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct lpc18xx_usb_otg_phy *lpc;
+
+       lpc = devm_kzalloc(&pdev->dev, sizeof(*lpc), GFP_KERNEL);
+       if (!lpc)
+               return -ENOMEM;
+
+       lpc->reg = syscon_node_to_regmap(pdev->dev.of_node->parent);
+       if (IS_ERR(lpc->reg)) {
+               dev_err(&pdev->dev, "failed to get syscon\n");
+               return PTR_ERR(lpc->reg);
+       }
+
+       lpc->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(lpc->clk)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               return PTR_ERR(lpc->clk);
+       }
+
+       lpc->phy = devm_phy_create(&pdev->dev, NULL, &lpc18xx_usb_otg_phy_ops);
+       if (IS_ERR(lpc->phy)) {
+               dev_err(&pdev->dev, "failed to create PHY\n");
+               return PTR_ERR(lpc->phy);
+       }
+
+       phy_set_drvdata(lpc->phy, lpc);
+
+       phy_provider = devm_of_phy_provider_register(&pdev->dev,
+                                                    of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id lpc18xx_usb_otg_phy_match[] = {
+       { .compatible = "nxp,lpc1850-usb-otg-phy" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_usb_otg_phy_match);
+
+static struct platform_driver lpc18xx_usb_otg_phy_driver = {
+       .probe          = lpc18xx_usb_otg_phy_probe,
+       .driver         = {
+               .name   = "lpc18xx-usb-otg-phy",
+               .of_match_table = lpc18xx_usb_otg_phy_match,
+       },
+};
+module_platform_driver(lpc18xx_usb_otg_phy_driver);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_DESCRIPTION("NXP LPC18xx/43xx USB OTG PHY driver");
+MODULE_LICENSE("GPL v2");
index 5e257ef7ac050a55bc09d6ce853aa6d2a37cf52e..c47b56b4a2b8892810f00179a53e9eceba7c612c 100644 (file)
@@ -1132,7 +1132,7 @@ static struct phy *miphy28lp_xlate(struct device *dev,
        return miphy_phy->phy;
 }
 
-static struct phy_ops miphy28lp_ops = {
+static const struct phy_ops miphy28lp_ops = {
        .init = miphy28lp_init,
        .owner = THIS_MODULE,
 };
@@ -1268,7 +1268,6 @@ static struct platform_driver miphy28lp_driver = {
        .probe = miphy28lp_probe,
        .driver = {
                .name = "miphy28lp-phy",
-               .owner = THIS_MODULE,
                .of_match_table = miphy28lp_of_match,
        }
 };
index 0ff354d6e1831b3f9a970ae8d6f30a4c0fa466da..00a686a073ed6ee804ea96ebcdb0ef3813482a85 100644 (file)
@@ -510,7 +510,7 @@ static struct phy *miphy365x_xlate(struct device *dev,
        return miphy_phy->phy;
 }
 
-static struct phy_ops miphy365x_ops = {
+static const struct phy_ops miphy365x_ops = {
        .init           = miphy365x_init,
        .owner          = THIS_MODULE,
 };
index 03b94f92e6f17b319c68a58f03ac8cf91ef0be75..768ce92e81ce7634f767612a99f37025185422db 100644 (file)
@@ -75,7 +75,7 @@ static int phy_mvebu_sata_power_off(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops phy_mvebu_sata_ops = {
+static const struct phy_ops phy_mvebu_sata_ops = {
        .power_on       = phy_mvebu_sata_power_on,
        .power_off      = phy_mvebu_sata_power_off,
        .owner          = THIS_MODULE,
index c1a468686bdc72433b7596512cb70852f3ef2420..0fe80589ffbea0cef4c00e85d242934c1dcb3f69 100644 (file)
@@ -137,7 +137,7 @@ static int omap_usb_init(struct phy *x)
        return 0;
 }
 
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .init           = omap_usb_init,
        .power_on       = omap_usb_power_on,
        .power_off      = omap_usb_power_off,
index 4b243f7a10e4a9bb3e9e237e1a25dcb5f80b7b52..69ce2afac0150fb580630921ed4f9892d58dab77 100644 (file)
@@ -204,7 +204,7 @@ static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)
        return 0;
 }
 
-static struct phy_ops qcom_apq8064_sata_phy_ops = {
+static const struct phy_ops qcom_apq8064_sata_phy_ops = {
        .init           = qcom_apq8064_sata_phy_init,
        .exit           = qcom_apq8064_sata_phy_exit,
        .owner          = THIS_MODULE,
index 6f2fe26279165ff1b8e26821cfd243d0ab79a214..0ad127cc9298b10cb99988b7f40c7aa264bc1f47 100644 (file)
@@ -126,7 +126,7 @@ static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
        return 0;
 }
 
-static struct phy_ops qcom_ipq806x_sata_phy_ops = {
+static const struct phy_ops qcom_ipq806x_sata_phy_ops = {
        .init           = qcom_ipq806x_sata_phy_init,
        .exit           = qcom_ipq806x_sata_phy_exit,
        .owner          = THIS_MODULE,
index 591a39175e8a2357cd0f138cda151850a91963ea..2bd5ce43a7240d66429ed781b4780c33210897a0 100644 (file)
@@ -150,7 +150,7 @@ int ufs_qcom_phy_remove(struct phy *generic_phy,
                       struct ufs_qcom_phy *ufs_qcom_phy);
 struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
                        struct ufs_qcom_phy *common_cfg,
-                       struct phy_ops *ufs_qcom_phy_gen_ops,
+                       const struct phy_ops *ufs_qcom_phy_gen_ops,
                        struct ufs_qcom_phy_specific_ops *phy_spec_ops);
 int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
                        struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A,
index f5fc50a9fce782036357bbd8f536ea08c209ee10..56631e77c11d7fb2ebd5e978869797d910b5daaf 100644 (file)
@@ -115,7 +115,7 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
        return err;
 }
 
-static struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
+static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
        .init           = ufs_qcom_phy_qmp_14nm_init,
        .exit           = ufs_qcom_phy_exit,
        .power_on       = ufs_qcom_phy_power_on,
@@ -191,7 +191,6 @@ static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = {
        .driver = {
                .of_match_table = ufs_qcom_phy_qmp_14nm_of_match,
                .name = "ufs_qcom_phy_qmp_14nm",
-               .owner = THIS_MODULE,
        },
 };
 
index 8332f96b2c4a7c7496e9ae3f987f6f404fd7faea..b16ea77d07b923c1a29f57def315bcdc032a9d40 100644 (file)
@@ -171,7 +171,7 @@ static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
        return err;
 }
 
-static struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
+static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
        .init           = ufs_qcom_phy_qmp_20nm_init,
        .exit           = ufs_qcom_phy_exit,
        .power_on       = ufs_qcom_phy_power_on,
@@ -247,7 +247,6 @@ static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = {
        .driver = {
                .of_match_table = ufs_qcom_phy_qmp_20nm_of_match,
                .name = "ufs_qcom_phy_qmp_20nm",
-               .owner = THIS_MODULE,
        },
 };
 
index f9c618f0ab6e4fe120462616b2c84ba0d9fe30a6..49a1ed0cef56fe7cbf9aed102b47149415f021f0 100644 (file)
@@ -77,7 +77,7 @@ EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate);
 
 struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
                                struct ufs_qcom_phy *common_cfg,
-                               struct phy_ops *ufs_qcom_phy_gen_ops,
+                               const struct phy_ops *ufs_qcom_phy_gen_ops,
                                struct ufs_qcom_phy_specific_ops *phy_spec_ops)
 {
        int err;
index 39d9b299543572f01a3712b85e18c07a1bef605d..6e0d9fa8e1d13f8d1ad0eec73b1040f594637df7 100644 (file)
@@ -184,7 +184,7 @@ static int rcar_gen2_phy_power_off(struct phy *p)
        return 0;
 }
 
-static struct phy_ops rcar_gen2_phy_ops = {
+static const struct phy_ops rcar_gen2_phy_ops = {
        .init           = rcar_gen2_phy_init,
        .exit           = rcar_gen2_phy_exit,
        .power_on       = rcar_gen2_phy_power_on,
index 7d4c33643768808780f71455900bc7dd51329103..5a5c073e72fe1ee6115bea310890b646b6b40a69 100644 (file)
@@ -84,7 +84,7 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
        return 0;
 }
 
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .power_on       = rockchip_usb_phy_power_on,
        .power_off      = rockchip_usb_phy_power_off,
        .owner          = THIS_MODULE,
@@ -146,7 +146,6 @@ static struct platform_driver rockchip_usb_driver = {
        .probe          = rockchip_usb_phy_probe,
        .driver         = {
                .name   = "rockchip-usb-phy",
-               .owner  = THIS_MODULE,
                .of_match_table = rockchip_usb_phy_dt_ids,
        },
 };
index 55b6994932e37f0b3d5d14c72cb1ff7df4807f85..f278a9c547e199c6228081f3445133dcdd9cda46 100644 (file)
@@ -71,7 +71,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops samsung_usb2_phy_ops = {
+static const struct phy_ops samsung_usb2_phy_ops = {
        .power_on       = samsung_usb2_phy_power_on,
        .power_off      = samsung_usb2_phy_power_off,
        .owner          = THIS_MODULE,
index 45d0005b2203f14777fff62f8ba30cffe4d779fc..ed67e98e54ca6560e567d4b3944d674651538681 100644 (file)
@@ -179,7 +179,7 @@ static const struct of_device_id spear1310_miphy_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match);
 
-static struct phy_ops spear1310_miphy_ops = {
+static const struct phy_ops spear1310_miphy_ops = {
        .init = spear1310_miphy_init,
        .exit = spear1310_miphy_exit,
        .owner = THIS_MODULE,
index 494240da4a398e4554f60a3c960df6234dbb8d84..97280c0cf6128c2a0339a27e7f207702ffe6762e 100644 (file)
@@ -189,7 +189,7 @@ static const struct of_device_id spear1340_miphy_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match);
 
-static struct phy_ops spear1340_miphy_ops = {
+static const struct phy_ops spear1340_miphy_ops = {
        .init = spear1340_miphy_init,
        .exit = spear1340_miphy_exit,
        .owner = THIS_MODULE,
index c093b472b57dadee94b832a989f5495a07dc98f2..0ac74639ad0244aa81e9f4878c58c5d6aff39841 100644 (file)
@@ -112,7 +112,7 @@ static int stih41x_usb_phy_power_off(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops stih41x_usb_phy_ops = {
+static const struct phy_ops stih41x_usb_phy_ops = {
        .init           = stih41x_usb_phy_init,
        .power_on       = stih41x_usb_phy_power_on,
        .power_off      = stih41x_usb_phy_power_off,
index 2dad7e820ff0b16b7447b708f0c147b2e0289f02..731b395d6e6a1621ee7fc36055b6c1a5f7367780 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Allwinner sun4i USB phy driver
  *
- * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
  *
  * Based on code from
  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/extcon.h>
 #include <linux/io.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_gpio.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/phy-sun4i-usb.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
+#include <linux/workqueue.h>
 
 #define REG_ISCR                       0x00
-#define REG_PHYCTL                     0x04
+#define REG_PHYCTL_A10                 0x04
 #define REG_PHYBIST                    0x08
 #define REG_PHYTUNE                    0x0c
+#define REG_PHYCTL_A33                 0x10
 
 #define PHYCTL_DATA                    BIT(7)
 
 #define SUNXI_AHB_INCRX_ALIGN_EN       BIT(8)
 #define SUNXI_ULPI_BYPASS_EN           BIT(0)
 
+/* ISCR, Interface Status and Control bits */
+#define ISCR_ID_PULLUP_EN              (1 << 17)
+#define ISCR_DPDM_PULLUP_EN    (1 << 16)
+/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
+#define ISCR_FORCE_ID_MASK     (3 << 14)
+#define ISCR_FORCE_ID_LOW              (2 << 14)
+#define ISCR_FORCE_ID_HIGH     (3 << 14)
+#define ISCR_FORCE_VBUS_MASK   (3 << 12)
+#define ISCR_FORCE_VBUS_LOW    (2 << 12)
+#define ISCR_FORCE_VBUS_HIGH   (3 << 12)
+
 /* Common Control Bits for Both PHYs */
 #define PHY_PLL_BW                     0x03
 #define PHY_RES45_CAL_EN               0x0c
 
 #define MAX_PHYS                       3
 
+/*
+ * Note do not raise the debounce time, we must report Vusb high within 100ms
+ * otherwise we get Vbus errors
+ */
+#define DEBOUNCE_TIME                  msecs_to_jiffies(50)
+#define POLL_TIME                      msecs_to_jiffies(250)
+
 struct sun4i_usb_phy_data {
        void __iomem *base;
        struct mutex mutex;
        int num_phys;
        u32 disc_thresh;
+       bool has_a33_phyctl;
        struct sun4i_usb_phy {
                struct phy *phy;
                void __iomem *pmu;
                struct regulator *vbus;
                struct reset_control *reset;
                struct clk *clk;
+               bool regulator_on;
                int index;
        } phys[MAX_PHYS];
+       /* phy0 / otg related variables */
+       struct extcon_dev *extcon;
+       bool phy0_init;
+       bool phy0_poll;
+       struct gpio_desc *id_det_gpio;
+       struct gpio_desc *vbus_det_gpio;
+       struct power_supply *vbus_power_supply;
+       struct notifier_block vbus_power_nb;
+       bool vbus_power_nb_registered;
+       int id_det_irq;
+       int vbus_det_irq;
+       int id_det;
+       int vbus_det;
+       struct delayed_work detect;
 };
 
 #define to_sun4i_usb_phy_data(phy) \
        container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
 
+static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+       u32 iscr;
+
+       iscr = readl(data->base + REG_ISCR);
+       iscr &= ~clr;
+       iscr |= set;
+       writel(iscr, data->base + REG_ISCR);
+}
+
+static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
+{
+       if (val)
+               val = ISCR_FORCE_ID_HIGH;
+       else
+               val = ISCR_FORCE_ID_LOW;
+
+       sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
+}
+
+static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
+{
+       if (val)
+               val = ISCR_FORCE_VBUS_HIGH;
+       else
+               val = ISCR_FORCE_VBUS_LOW;
+
+       sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
+}
+
 static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
                                int len)
 {
        struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
        u32 temp, usbc_bit = BIT(phy->index * 2);
+       void *phyctl;
        int i;
 
        mutex_lock(&phy_data->mutex);
 
+       if (phy_data->has_a33_phyctl) {
+               phyctl = phy_data->base + REG_PHYCTL_A33;
+               /* A33 needs us to set phyctl to 0 explicitly */
+               writel(0, phyctl);
+       } else {
+               phyctl = phy_data->base + REG_PHYCTL_A10;
+       }
+
        for (i = 0; i < len; i++) {
-               temp = readl(phy_data->base + REG_PHYCTL);
+               temp = readl(phyctl);
 
                /* clear the address portion */
                temp &= ~(0xff << 8);
 
                /* set the address */
                temp |= ((addr + i) << 8);
-               writel(temp, phy_data->base + REG_PHYCTL);
+               writel(temp, phyctl);
 
                /* set the data bit and clear usbc bit*/
-               temp = readb(phy_data->base + REG_PHYCTL);
+               temp = readb(phyctl);
                if (data & 0x1)
                        temp |= PHYCTL_DATA;
                else
                        temp &= ~PHYCTL_DATA;
                temp &= ~usbc_bit;
-               writeb(temp, phy_data->base + REG_PHYCTL);
+               writeb(temp, phyctl);
 
                /* pulse usbc_bit */
-               temp = readb(phy_data->base + REG_PHYCTL);
+               temp = readb(phyctl);
                temp |= usbc_bit;
-               writeb(temp, phy_data->base + REG_PHYCTL);
+               writeb(temp, phyctl);
 
-               temp = readb(phy_data->base + REG_PHYCTL);
+               temp = readb(phyctl);
                temp &= ~usbc_bit;
-               writeb(temp, phy_data->base + REG_PHYCTL);
+               writeb(temp, phyctl);
 
                data >>= 1;
        }
@@ -171,12 +253,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
 
        sun4i_usb_phy_passby(phy, 1);
 
+       if (phy->index == 0) {
+               data->phy0_init = true;
+
+               /* Enable pull-ups */
+               sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
+               sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
+
+               if (data->id_det_gpio) {
+                       /* OTG mode, force ISCR and cable state updates */
+                       data->id_det = -1;
+                       data->vbus_det = -1;
+                       queue_delayed_work(system_wq, &data->detect, 0);
+               } else {
+                       /* Host only mode */
+                       sun4i_usb_phy0_set_id_detect(_phy, 0);
+                       sun4i_usb_phy0_set_vbus_detect(_phy, 1);
+               }
+       }
+
        return 0;
 }
 
 static int sun4i_usb_phy_exit(struct phy *_phy)
 {
        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+       if (phy->index == 0) {
+               /* Disable pull-ups */
+               sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
+               sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
+               data->phy0_init = false;
+       }
 
        sun4i_usb_phy_passby(phy, 0);
        reset_control_assert(phy->reset);
@@ -185,23 +294,74 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
        return 0;
 }
 
+static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data)
+{
+       if (data->vbus_det_gpio)
+               return gpiod_get_value_cansleep(data->vbus_det_gpio);
+
+       if (data->vbus_power_supply) {
+               union power_supply_propval val;
+               int r;
+
+               r = power_supply_get_property(data->vbus_power_supply,
+                                             POWER_SUPPLY_PROP_PRESENT, &val);
+               if (r == 0)
+                       return val.intval;
+       }
+
+       /* Fallback: report vbus as high */
+       return 1;
+}
+
+static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data)
+{
+       return data->vbus_det_gpio || data->vbus_power_supply;
+}
+
 static int sun4i_usb_phy_power_on(struct phy *_phy)
 {
        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
-       int ret = 0;
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+       int ret;
+
+       if (!phy->vbus || phy->regulator_on)
+               return 0;
+
+       /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
+       if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) &&
+                               data->vbus_det)
+               return 0;
+
+       ret = regulator_enable(phy->vbus);
+       if (ret)
+               return ret;
+
+       phy->regulator_on = true;
 
-       if (phy->vbus)
-               ret = regulator_enable(phy->vbus);
+       /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
+       if (phy->index == 0 && data->vbus_det_gpio && data->phy0_poll)
+               mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
 
-       return ret;
+       return 0;
 }
 
 static int sun4i_usb_phy_power_off(struct phy *_phy)
 {
        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+       if (!phy->vbus || !phy->regulator_on)
+               return 0;
+
+       regulator_disable(phy->vbus);
+       phy->regulator_on = false;
 
-       if (phy->vbus)
-               regulator_disable(phy->vbus);
+       /*
+        * phy0 vbus typically slowly discharges, sometimes this causes the
+        * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
+        */
+       if (phy->index == 0 && data->vbus_det_gpio && !data->phy0_poll)
+               mod_delayed_work(system_wq, &data->detect, POLL_TIME);
 
        return 0;
 }
@@ -214,7 +374,7 @@ void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled)
 }
 EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect);
 
-static struct phy_ops sun4i_usb_phy_ops = {
+static const struct phy_ops sun4i_usb_phy_ops = {
        .init           = sun4i_usb_phy_init,
        .exit           = sun4i_usb_phy_exit,
        .power_on       = sun4i_usb_phy_power_on,
@@ -222,6 +382,95 @@ static struct phy_ops sun4i_usb_phy_ops = {
        .owner          = THIS_MODULE,
 };
 
+static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
+{
+       struct sun4i_usb_phy_data *data =
+               container_of(work, struct sun4i_usb_phy_data, detect.work);
+       struct phy *phy0 = data->phys[0].phy;
+       int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
+
+       id_det = gpiod_get_value_cansleep(data->id_det_gpio);
+       vbus_det = sun4i_usb_phy0_get_vbus_det(data);
+
+       mutex_lock(&phy0->mutex);
+
+       if (!data->phy0_init) {
+               mutex_unlock(&phy0->mutex);
+               return;
+       }
+
+       if (id_det != data->id_det) {
+               /*
+                * When a host cable (id == 0) gets plugged in on systems
+                * without vbus detection report vbus low for long enough for
+                * the musb-ip to end the current device session.
+                */
+               if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) {
+                       sun4i_usb_phy0_set_vbus_detect(phy0, 0);
+                       msleep(200);
+                       sun4i_usb_phy0_set_vbus_detect(phy0, 1);
+               }
+               sun4i_usb_phy0_set_id_detect(phy0, id_det);
+               data->id_det = id_det;
+               id_notify = 1;
+       }
+
+       if (vbus_det != data->vbus_det) {
+               sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
+               data->vbus_det = vbus_det;
+               vbus_notify = 1;
+       }
+
+       mutex_unlock(&phy0->mutex);
+
+       if (id_notify) {
+               extcon_set_cable_state_(data->extcon, EXTCON_USB_HOST,
+                                       !id_det);
+               /*
+                * When a host cable gets unplugged (id == 1) on systems
+                * without vbus detection report vbus low for long enough to
+                * the musb-ip to end the current host session.
+                */
+               if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) {
+                       mutex_lock(&phy0->mutex);
+                       sun4i_usb_phy0_set_vbus_detect(phy0, 0);
+                       msleep(1000);
+                       sun4i_usb_phy0_set_vbus_detect(phy0, 1);
+                       mutex_unlock(&phy0->mutex);
+               }
+       }
+
+       if (vbus_notify)
+               extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det);
+
+       if (data->phy0_poll)
+               queue_delayed_work(system_wq, &data->detect, POLL_TIME);
+}
+
+static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
+{
+       struct sun4i_usb_phy_data *data = dev_id;
+
+       /* vbus or id changed, let the pins settle and then scan them */
+       mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
+
+       return IRQ_HANDLED;
+}
+
+static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb,
+                                     unsigned long val, void *v)
+{
+       struct sun4i_usb_phy_data *data =
+               container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb);
+       struct power_supply *psy = v;
+
+       /* Properties on the vbus_power_supply changed, scan vbus_det */
+       if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply)
+               mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
+
+       return NOTIFY_OK;
+}
+
 static struct phy *sun4i_usb_phy_xlate(struct device *dev,
                                        struct of_phandle_args *args)
 {
@@ -233,6 +482,29 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
        return data->phys[args->args[0]].phy;
 }
 
+static int sun4i_usb_phy_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+       if (data->vbus_power_nb_registered)
+               power_supply_unreg_notifier(&data->vbus_power_nb);
+       if (data->id_det_irq >= 0)
+               devm_free_irq(dev, data->id_det_irq, data);
+       if (data->vbus_det_irq >= 0)
+               devm_free_irq(dev, data->vbus_det_irq, data);
+
+       cancel_delayed_work_sync(&data->detect);
+
+       return 0;
+}
+
+static const unsigned int sun4i_usb_phy0_cable[] = {
+       EXTCON_USB,
+       EXTCON_USB_HOST,
+       EXTCON_NONE,
+};
+
 static int sun4i_usb_phy_probe(struct platform_device *pdev)
 {
        struct sun4i_usb_phy_data *data;
@@ -241,35 +513,87 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
        struct phy_provider *phy_provider;
        bool dedicated_clocks;
        struct resource *res;
-       int i;
+       int i, ret;
 
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
        mutex_init(&data->mutex);
+       INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
+       dev_set_drvdata(dev, data);
 
-       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
+       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
+           of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
+           of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
                data->num_phys = 2;
        else
                data->num_phys = 3;
 
-       if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy") ||
-           of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy"))
-               data->disc_thresh = 3;
-       else
+       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
+           of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy"))
                data->disc_thresh = 2;
+       else
+               data->disc_thresh = 3;
 
-       if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy"))
+       if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") ||
+           of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
+           of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
                dedicated_clocks = true;
        else
                dedicated_clocks = false;
 
+       if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
+               data->has_a33_phyctl = true;
+
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
        data->base = devm_ioremap_resource(dev, res);
        if (IS_ERR(data->base))
                return PTR_ERR(data->base);
 
+       data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
+       if (IS_ERR(data->id_det_gpio)) {
+               if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               data->id_det_gpio = NULL;
+       }
+
+       data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
+       if (IS_ERR(data->vbus_det_gpio)) {
+               if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               data->vbus_det_gpio = NULL;
+       }
+
+       if (of_find_property(np, "usb0_vbus_power-supply", NULL)) {
+               data->vbus_power_supply = devm_power_supply_get_by_phandle(dev,
+                                                    "usb0_vbus_power-supply");
+               if (IS_ERR(data->vbus_power_supply))
+                       return PTR_ERR(data->vbus_power_supply);
+
+               if (!data->vbus_power_supply)
+                       return -EPROBE_DEFER;
+       }
+
+       /* vbus_det without id_det makes no sense, and is not supported */
+       if (sun4i_usb_phy0_have_vbus_det(data) && !data->id_det_gpio) {
+               dev_err(dev, "usb0_id_det missing or invalid\n");
+               return -ENODEV;
+       }
+
+       if (data->id_det_gpio) {
+               data->extcon = devm_extcon_dev_allocate(dev,
+                                                       sun4i_usb_phy0_cable);
+               if (IS_ERR(data->extcon))
+                       return PTR_ERR(data->extcon);
+
+               ret = devm_extcon_dev_register(dev, data->extcon);
+               if (ret) {
+                       dev_err(dev, "failed to register extcon: %d\n", ret);
+                       return ret;
+               }
+       }
+
        for (i = 0; i < data->num_phys; i++) {
                struct sun4i_usb_phy *phy = data->phys + i;
                char name[16];
@@ -319,10 +643,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
                phy_set_drvdata(phy->phy, &data->phys[i]);
        }
 
-       dev_set_drvdata(dev, data);
+       data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
+       data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
+       if ((data->id_det_gpio && data->id_det_irq < 0) ||
+           (data->vbus_det_gpio && data->vbus_det_irq < 0))
+               data->phy0_poll = true;
+
+       if (data->id_det_irq >= 0) {
+               ret = devm_request_irq(dev, data->id_det_irq,
+                               sun4i_usb_phy0_id_vbus_det_irq,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "usb0-id-det", data);
+               if (ret) {
+                       dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       if (data->vbus_det_irq >= 0) {
+               ret = devm_request_irq(dev, data->vbus_det_irq,
+                               sun4i_usb_phy0_id_vbus_det_irq,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "usb0-vbus-det", data);
+               if (ret) {
+                       dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
+                       data->vbus_det_irq = -1;
+                       sun4i_usb_phy_remove(pdev); /* Stop detect work */
+                       return ret;
+               }
+       }
+
+       if (data->vbus_power_supply) {
+               data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify;
+               data->vbus_power_nb.priority = 0;
+               ret = power_supply_reg_notifier(&data->vbus_power_nb);
+               if (ret) {
+                       sun4i_usb_phy_remove(pdev); /* Stop detect work */
+                       return ret;
+               }
+               data->vbus_power_nb_registered = true;
+       }
+
        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+       if (IS_ERR(phy_provider)) {
+               sun4i_usb_phy_remove(pdev); /* Stop detect work */
+               return PTR_ERR(phy_provider);
+       }
 
-       return PTR_ERR_OR_ZERO(phy_provider);
+       return 0;
 }
 
 static const struct of_device_id sun4i_usb_phy_of_match[] = {
@@ -330,12 +698,15 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
        { .compatible = "allwinner,sun5i-a13-usb-phy" },
        { .compatible = "allwinner,sun6i-a31-usb-phy" },
        { .compatible = "allwinner,sun7i-a20-usb-phy" },
+       { .compatible = "allwinner,sun8i-a23-usb-phy" },
+       { .compatible = "allwinner,sun8i-a33-usb-phy" },
        { },
 };
 MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
 
 static struct platform_driver sun4i_usb_phy_driver = {
        .probe  = sun4i_usb_phy_probe,
+       .remove = sun4i_usb_phy_remove,
        .driver = {
                .of_match_table = sun4i_usb_phy_of_match,
                .name  = "sun4i-usb-phy",
index 0095914a662c05916db38d1693ab1fa189a45a04..ac4f31abefe33e45a4b1cdf5636c7c1ba05f716d 100644 (file)
@@ -114,7 +114,7 @@ static int sun9i_usb_phy_exit(struct phy *_phy)
        return 0;
 }
 
-static struct phy_ops sun9i_usb_phy_ops = {
+static const struct phy_ops sun9i_usb_phy_ops = {
        .init           = sun9i_usb_phy_init,
        .exit           = sun9i_usb_phy_exit,
        .owner          = THIS_MODULE,
index 08020dc2c7c8c3496987589841fc0ddb0b1c9ff7..93bc1120af12844ca17cd53e7869dba0676b438b 100644 (file)
@@ -298,7 +298,7 @@ static int ti_pipe3_exit(struct phy *x)
 
        return 0;
 }
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .init           = ti_pipe3_init,
        .exit           = ti_pipe3_exit,
        .power_on       = ti_pipe3_power_on,
index 07efdd318bdc9215d4de464aa3a19f7cc22ec612..2535d792d57af69277460ed4bc51beb39065d1d5 100644 (file)
@@ -53,7 +53,7 @@ static int tusb1210_power_off(struct phy *phy)
        return 0;
 }
 
-static struct phy_ops phy_ops = {
+static const struct phy_ops phy_ops = {
        .power_on = tusb1210_power_on,
        .power_off = tusb1210_power_off,
        .owner = THIS_MODULE,
index ac49fb6285eeecfd4a6db6df0dd28965b55483a6..f2ebe490a4bc590aa381247c9bfbd1a12fecc11b 100644 (file)
@@ -5,7 +5,7 @@
  * and it's controller, which is always the parent.
  */
 static inline struct phy
-*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops)
+*ulpi_phy_create(struct ulpi *ulpi, const struct phy_ops *ops)
 {
        struct phy *phy;
        int ret;