S905X Le Potato Dev Add USB support

Add USB support to mainling kernel for Le Potato.
This commit is contained in:
Tonymac32
2017-08-15 02:09:37 -04:00
committed by GitHub
parent 1e4a43a89a
commit fdfc3c1922
6 changed files with 946 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
From 6006726ba262f50d61f3a001cc87fbc82dd8318a Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Sun, 20 Nov 2016 00:23:52 +0100
Subject: [PATCH 73/79] ARM64: dts: meson-gxl: add USB host support
This adds USB host support to the Meson GXL SoC. A dwc3 controller is
used for host-mode, while a dwc2 controller is used for device-mode only.
The dwc3 controller's internal roothub has two USB2 ports enabled but no
USB3 port. Each of the ports is supplied by a separate PHY. The USB pins
are connected to the SoC's USBHOST_A and USBOTG_B pins.
Due to the way the roothub works internally the USB PHYs are left
enabled. When the dwc3 controller is disabled the PHY is never powered on
so it does not draw any extra power. However, when the dwc3 host
controller is enabled then all PHYs also have to be enabled, otherwise
USB devices will not be detected (regardless of whether they are plugged
into an enabled port or not). This means that only the dwc3 controller
has to be enabled on boards with USB support (instead of requiring all
boards to enable the PHYs additionally with the chance of forgetting to
enable one and breaking all other ports with that as well).
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 50 ++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 1e65b0f..c33c29f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -49,6 +49,56 @@
/ {
compatible = "amlogic,meson-gxl";
+
+ soc {
+
+ usb0: usb@c9000000 {
+ compatible = "snps,dwc3";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0xc9000000 0x0 0x100000>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ dr_mode = "host";
+ maximum-speed = "high-speed";
+ snps,dis_u2_susphy_quirk;
+ status = "disabled";
+
+ dwc3_roothub: roothub@0 {
+ compatible = "usb1d6b,3", "usb1d6b,2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ port@1 {
+ reg = <1>;
+ phys = <&usb2_phy0>;
+ phy-names = "usb2-phy";
+ };
+
+ port@2 {
+ reg = <2>;
+ phys = <&usb2_phy1>;
+ phy-names = "usb2-phy";
+ };
+ };
+ };
+ };
+};
+
+&apb {
+ usb2_phy0: phy@78000 {
+ compatible = "amlogic,meson-gxl-usb2-phy";
+ #phy-cells = <0>;
+ reg = <0x0 0x78000 0x0 0x20>;
+ status = "okay";
+ };
+
+ usb2_phy1: phy@78020 {
+ compatible = "amlogic,meson-gxl-usb2-phy";
+ #phy-cells = <0>;
+ reg = <0x0 0x78020 0x0 0x20>;
+ status = "okay";
+ };
};
&ethmac {
--
1.9.1

View File

@@ -0,0 +1,293 @@
From acfe799c4e00a726cdb0bbd37aaaa123070ed59e Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Tue, 10 Jan 2017 18:59:43 +0100
Subject: [PATCH 75/79] usb: host: add a generic platform USB roothub driver
Many SoC platforms have separate devices for the USB PHY which are
registered through the generic PHY framework. These PHYs have to be
enabled to make the USB controller actually work. They also have to be
disabled again on shutdown/suspend.
Currently (at least) the following HCI platform drivers are using custom
code to obtain all PHYs via devicetree for the roothub/controller and
disable/enable them when required:
- ehci-platform.c has ehci_platform_power_{on,off}
- xhci-mtk.c has xhci_mtk_phy_{init,exit,power_on,power_off}
- ohci-platform.c has ohci_platform_power_{on,off}
These drivers are not using the generic devicetree USB device bindings
yet which were only introduced recently (documentation is available in
devicetree/bindings/usb/usb-device.txt).
With this new driver the usb2-phy and usb3-phy can be specified directly
in the child-node of the corresponding port of the roothub via
devicetree. This can be extended by not just parsing PHYs (some of the
other drivers listed above are for example also parsing a list of clocks
as well) when required.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../devicetree/bindings/usb/usb-roothub.txt | 46 +++++++
drivers/usb/host/Kconfig | 3 +
drivers/usb/host/Makefile | 2 +
drivers/usb/host/platform-roothub.c | 146 +++++++++++++++++++++
drivers/usb/host/platform-roothub.h | 14 ++
5 files changed, 211 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/usb-roothub.txt
create mode 100644 drivers/usb/host/platform-roothub.c
create mode 100644 drivers/usb/host/platform-roothub.h
diff --git a/Documentation/devicetree/bindings/usb/usb-roothub.txt b/Documentation/devicetree/bindings/usb/usb-roothub.txt
new file mode 100644
index 0000000..23b24b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usb-roothub.txt
@@ -0,0 +1,46 @@
+Generic USB root-hub Properties
+
+similar to the USB device bindings (documented in usb-device.txt from the
+current directory) this provides support for configuring the root-hub.
+
+Required properties:
+- compatible: should be at least one of "usb1d6b,3", "usb1d6b,2"
+- reg: must be 0.
+- address-cells: must be 1
+- size-cells: must be 0
+
+Required sub-nodes:
+a sub-node per actual USB port is required. each sub-node supports the
+following properties:
+ Required properties:
+ - reg: the port number on the root-hub (mandatory)
+ Optional properties:
+ - phys: optional, from the *Generic PHY* bindings (mandatory needed
+ when phy-names is given)
+ - phy-names: optional, from the *Generic PHY* bindings; supported names
+ are "usb2-phy" or "usb3-phy"
+
+Example:
+ &usb1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ roothub@0 {
+ compatible = "usb1d6b,3", "usb1d6b,2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ port@1 {
+ reg = <1>;
+ usb-phy = <&usb2_phy1>, <&usb3_phy1>;
+ phy-names = "usb2-phy", "usb3-phy";
+ };
+
+ port@2 {
+ reg = <2>;
+ usb-phy = <&usb2_phy2>, <&usb3_phy2>;
+ phy-names = "usb2-phy", "usb3-phy";
+ };
+ };
+ }
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ababb91..3f450e0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -797,6 +797,9 @@ config USB_HCD_SSB
If unsure, say N.
+config USB_PLATFORM_ROOTHUB
+ bool
+
config USB_HCD_TEST_MODE
bool "HCD test mode support"
---help---
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index c77b0a3..93f0dd2 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci/
obj-$(CONFIG_USB_PCI) += pci-quirks.o
+obj-$(CONFIG_USB_PLATFORM_ROOTHUB) += platform-roothub.o
+
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o
diff --git a/drivers/usb/host/platform-roothub.c b/drivers/usb/host/platform-roothub.c
new file mode 100644
index 0000000..84837e4
--- /dev/null
+++ b/drivers/usb/host/platform-roothub.c
@@ -0,0 +1,146 @@
+/*
+ * platform roothub driver - a virtual PHY device which passes all phy_*
+ * function calls to multiple (actual) PHY devices. This is comes handy when
+ * initializing all PHYs on a root-hub (to keep them all in the same state).
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+#include <linux/usb/of.h>
+
+#include "platform-roothub.h"
+
+#define ROOTHUB_PORTNUM 0
+
+struct platform_roothub {
+ struct phy *phy;
+ struct list_head list;
+};
+
+static struct platform_roothub *platform_roothub_alloc(struct device *dev)
+{
+ struct platform_roothub *roothub_entry;
+
+ roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL);
+ if (!roothub_entry)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&roothub_entry->list);
+
+ return roothub_entry;
+}
+
+static int platform_roothub_add_phy(struct device *dev,
+ struct device_node *port_np,
+ const char *con_id, struct list_head *list)
+{
+ struct platform_roothub *roothub_entry;
+ struct phy *phy = devm_of_phy_get(dev, port_np, con_id);
+
+ if (IS_ERR_OR_NULL(phy)) {
+ if (!phy || PTR_ERR(phy) == -ENODEV)
+ return 0;
+ else
+ return PTR_ERR(phy);
+ }
+
+ roothub_entry = platform_roothub_alloc(dev);
+ if (IS_ERR(roothub_entry))
+ return PTR_ERR(roothub_entry);
+
+ roothub_entry->phy = phy;
+
+ list_add_tail(&roothub_entry->list, list);
+
+ return 0;
+}
+
+struct platform_roothub *platform_roothub_init(struct device *dev)
+{
+ struct device_node *roothub_np, *port_np;
+ struct platform_roothub *plat_roothub;
+ int err;
+
+ roothub_np = usb_of_get_child_node(dev->of_node, ROOTHUB_PORTNUM);
+ if (!of_device_is_available(roothub_np))
+ return NULL;
+
+ plat_roothub = platform_roothub_alloc(dev);
+ if (IS_ERR(plat_roothub))
+ return plat_roothub;
+
+ for_each_available_child_of_node(roothub_np, port_np) {
+ err = platform_roothub_add_phy(dev, port_np, "usb2-phy",
+ &plat_roothub->list);
+ if (err)
+ return ERR_PTR(err);
+
+ err = platform_roothub_add_phy(dev, port_np, "usb3-phy",
+ &plat_roothub->list);
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ return plat_roothub;
+}
+EXPORT_SYMBOL_GPL(platform_roothub_init);
+
+int platform_roothub_power_on(struct platform_roothub *plat_roothub)
+{
+ struct platform_roothub *roothub_entry;
+ struct list_head *head;
+ int err;
+
+ if (!plat_roothub)
+ return 0;
+
+ head = &plat_roothub->list;
+
+ list_for_each_entry(roothub_entry, head, list) {
+ err = phy_init(roothub_entry->phy);
+ if (err)
+ goto err_out;
+
+ err = phy_power_on(roothub_entry->phy);
+ if (err) {
+ phy_exit(roothub_entry->phy);
+ goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out:
+ list_for_each_entry_continue_reverse(roothub_entry, head, list) {
+ phy_power_off(roothub_entry->phy);
+ phy_exit(roothub_entry->phy);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(platform_roothub_power_on);
+
+void platform_roothub_power_off(struct platform_roothub *plat_roothub)
+{
+ struct platform_roothub *roothub_entry;
+
+ if (!plat_roothub)
+ return;
+
+ list_for_each_entry_reverse(roothub_entry, &plat_roothub->list, list) {
+ phy_power_off(roothub_entry->phy);
+ phy_exit(roothub_entry->phy);
+ }
+}
+EXPORT_SYMBOL_GPL(platform_roothub_power_off);
diff --git a/drivers/usb/host/platform-roothub.h b/drivers/usb/host/platform-roothub.h
new file mode 100644
index 0000000..bde0bf2
--- /dev/null
+++ b/drivers/usb/host/platform-roothub.h
@@ -0,0 +1,14 @@
+#ifndef USB_HOST_PLATFORM_ROOTHUB_H
+#define USB_HOST_PLATFORM_ROOTHUB_H
+
+struct phy;
+struct device_node;
+
+struct platform_roothub;
+
+struct platform_roothub *platform_roothub_init(struct device *dev);
+
+int platform_roothub_power_on(struct platform_roothub *plat_roothub);
+void platform_roothub_power_off(struct platform_roothub *plat_roothub);
+
+#endif /* USB_HOST_PLATFORM_ROOTHUB_H */
--
1.9.1

View File

@@ -0,0 +1,146 @@
From 9dfd5d48fbcd31b12825e81d532aca76c2b769f8 Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Wed, 11 Jan 2017 11:34:59 +0100
Subject: [PATCH 76/79] usb: host: xhci: plat: integrate the platform-roothub
This enables the platform-roothub for the xhci-plat driver. This allows
specifying a PHY for each port via devicetree. All PHYs will then be
enabled/disabled by the platform-roothub driver.
One example where this is required is the Amlogic GXL and GXM SoCs:
They are using a dwc3 USB controller with up to three ports enabled on
the internal roothub. Using only the top-level "phy" properties does not
work here since one can only specify one "usb2-phy" and one "usb3-phy",
while actually at least two "usb2-phy" have to be specified.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
Documentation/devicetree/bindings/usb/usb-xhci.txt | 7 ++++++
drivers/usb/host/Kconfig | 1 +
drivers/usb/host/xhci-plat.c | 26 +++++++++++++++++++++-
drivers/usb/host/xhci.h | 3 +++
4 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 2d80b60..31b4f68 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -29,6 +29,13 @@ Optional properties:
- usb3-lpm-capable: determines if platform is USB3 LPM capable
- quirk-broken-port-ped: set if the controller has broken port disable mechanism
+sub-nodes:
+- optionally there can be a node for the root-hub, see usb-roothub.txt in the
+ current directory
+- one or more nodes with reg 1-31 for each port to which a device is connected.
+ See usb-device.txt in the current directory for more information.
+
+
Example:
usb@f0931000 {
compatible = "generic-xhci";
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 3f450e0..d6279d7 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -36,6 +36,7 @@ config USB_XHCI_PCI
config USB_XHCI_PLATFORM
tristate "Generic xHCI driver for a platform device"
select USB_XHCI_RCAR if ARCH_RENESAS
+ select USB_PLATFORM_ROOTHUB
---help---
Adds an xHCI host driver for a generic platform device, which
provides a memory space and an irq.
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index c04144b..4de20b5 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -285,10 +285,20 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}
- ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ xhci->platform_roothub = platform_roothub_init(sysdev);
+ if (IS_ERR(xhci->platform_roothub)) {
+ ret = PTR_ERR(xhci->platform_roothub);
+ goto disable_clk;
+ }
+
+ ret = platform_roothub_power_on(xhci->platform_roothub);
if (ret)
goto disable_usb_phy;
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto disable_platform_roothub;
+
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
@@ -311,6 +321,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
dealloc_usb2_hcd:
usb_remove_hcd(hcd);
+disable_platform_roothub:
+ platform_roothub_power_off(xhci->platform_roothub);
+
disable_usb_phy:
usb_phy_shutdown(hcd->usb_phy);
@@ -342,6 +355,8 @@ static int xhci_plat_remove(struct platform_device *dev)
usb_remove_hcd(xhci->shared_hcd);
usb_phy_shutdown(hcd->usb_phy);
+ platform_roothub_power_off(xhci->platform_roothub);
+
usb_remove_hcd(hcd);
usb_put_hcd(xhci->shared_hcd);
@@ -374,6 +389,11 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev)
if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
clk_disable_unprepare(xhci->clk);
+ if (ret)
+ return ret;
+
+ platform_roothub_power_off(xhci->platform_roothub);
+
return ret;
}
@@ -386,6 +406,10 @@ static int __maybe_unused xhci_plat_resume(struct device *dev)
if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
clk_prepare_enable(xhci->clk);
+ ret = platform_roothub_power_on(xhci->platform_roothub);
+ if (ret)
+ return ret;
+
ret = xhci_priv_resume_quirk(hcd);
if (ret)
return ret;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a9..7f3d15e 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -34,6 +34,8 @@
#include "xhci-ext-caps.h"
#include "pci-quirks.h"
+#include "platform-roothub.h"
+
/* xHCI PCI Configuration Registers */
#define XHCI_SBRN_OFFSET (0x60)
@@ -1725,6 +1727,7 @@ struct xhci_hcd {
int msix_count;
/* optional clock */
struct clk *clk;
+ struct platform_roothub *platform_roothub;
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
--
1.9.1

View File

@@ -0,0 +1,64 @@
From 7079f88717e54f9ea48c1cbfba09b53cc8b9aefd Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 13 Jul 2017 15:02:33 +0200
Subject: [PATCH 77/79] ARM64: dts: meson-gx: Enable USB on GXL and GXM boards
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 4 ++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts | 4 ++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi | 4 ++++
arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts | 4 ++++
4 files changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 54718ee..bf49f6f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -234,3 +234,7 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&usb0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
index 55ec11a..19bb39b 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
@@ -249,3 +249,7 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&usb0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
index f3eea8e..a07c34d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
@@ -171,3 +171,7 @@
pinctrl-0 = <&uart_ao_a_pins>;
pinctrl-names = "default";
};
+
+&usb0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
index 3a327dd..1b7038d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
@@ -215,3 +215,7 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&usb0 {
+ status = "okay";
+};
--
1.9.1

View File

@@ -0,0 +1,325 @@
From c7975e835ec6c8dea399b649597f6fe8461214cb Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Sat, 1 Apr 2017 11:18:30 +0200
Subject: [PATCH 79/79] phy: meson: add USB2 PHY support for Meson GXL and GXM
This adds a new driver for the USB2 PHYs found on Meson GXL and GXM SoCs
(both SoCs are using the same USB PHY register layout).
The USB2 PHY is a simple PHY which only has a few registers to configure
the mode (host/device) and a reset register (to enable/disable the PHY).
Unfortunately there are no datasheets available for this PHY. The driver
was written by reading the code from Amlogic's GPL kernel sources and
by analyzing the registers on an actual GXL and GXM device running the
kernel that was shipped on the boards I have.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/phy/Kconfig | 12 ++
drivers/phy/Makefile | 1 +
drivers/phy/phy-meson-gxl-usb2.c | 261 +++++++++++++++++++++++++++++++++++++++
3 files changed, 274 insertions(+)
create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index afaf7b6..27189e2 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -530,4 +530,16 @@ config PHY_MESON8B_USB2
and GXBB SoCs.
If unsure, say N.
+config PHY_MESON_GXL_USB
+ tristate "Meson GXL and GXM USB2 PHY drivers"
+ default ARCH_MESON
+ depends on OF && (ARCH_MESON || COMPILE_TEST)
+ depends on USB_SUPPORT
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Enable this to support the Meson USB2 PHYs found in Meson
+ GXL and GXM SoCs.
+ If unsure, say N.
+
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f8047b4..bea10c6 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB) += phy-meson-gxl-usb2.o
diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
new file mode 100644
index 0000000..f5fbd3c
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb2.c
@@ -0,0 +1,261 @@
+/*
+ * Meson GXL and GXM USB2 PHY driver
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/* bits [31:27] are read-only */
+#define U2P_R0 0x0
+ #define U2P_R0_BYPASS_SEL BIT(0)
+ #define U2P_R0_BYPASS_DM_EN BIT(1)
+ #define U2P_R0_BYPASS_DP_EN BIT(2)
+ #define U2P_R0_TXBITSTUFF_ENH BIT(3)
+ #define U2P_R0_TXBITSTUFF_EN BIT(4)
+ #define U2P_R0_DM_PULLDOWN BIT(5)
+ #define U2P_R0_DP_PULLDOWN BIT(6)
+ #define U2P_R0_DP_VBUS_VLD_EXT_SEL BIT(7)
+ #define U2P_R0_DP_VBUS_VLD_EXT BIT(8)
+ #define U2P_R0_ADP_PRB_EN BIT(9)
+ #define U2P_R0_ADP_DISCHARGE BIT(10)
+ #define U2P_R0_ADP_CHARGE BIT(11)
+ #define U2P_R0_DRV_VBUS BIT(12)
+ #define U2P_R0_ID_PULLUP BIT(13)
+ #define U2P_R0_LOOPBACK_EN_B BIT(14)
+ #define U2P_R0_OTG_DISABLE BIT(15)
+ #define U2P_R0_COMMON_ONN BIT(16)
+ #define U2P_R0_FSEL_MASK GENMASK(19, 17)
+ #define U2P_R0_REF_CLK_SEL_MASK GENMASK(21, 20)
+ #define U2P_R0_POWER_ON_RESET BIT(22)
+ #define U2P_R0_V_ATE_TEST_EN_B_MASK GENMASK(24, 23)
+ #define U2P_R0_ID_SET_ID_DQ BIT(25)
+ #define U2P_R0_ATE_RESET BIT(26)
+ #define U2P_R0_FSV_MINUS BIT(27)
+ #define U2P_R0_FSV_PLUS BIT(28)
+ #define U2P_R0_BYPASS_DM_DATA BIT(29)
+ #define U2P_R0_BYPASS_DP_DATA BIT(30)
+
+#define U2P_R1 0x4
+ #define U2P_R1_BURN_IN_TEST BIT(0)
+ #define U2P_R1_ACA_ENABLE BIT(1)
+ #define U2P_R1_DCD_ENABLE BIT(2)
+ #define U2P_R1_VDAT_SRC_EN_B BIT(3)
+ #define U2P_R1_VDAT_DET_EN_B BIT(4)
+ #define U2P_R1_CHARGES_SEL BIT(5)
+ #define U2P_R1_TX_PREEMP_PULSE_TUNE BIT(6)
+ #define U2P_R1_TX_PREEMP_AMP_TUNE_MASK GENMASK(8, 7)
+ #define U2P_R1_TX_RES_TUNE_MASK GENMASK(10, 9)
+ #define U2P_R1_TX_RISE_TUNE_MASK GENMASK(12, 11)
+ #define U2P_R1_TX_VREF_TUNE_MASK GENMASK(16, 13)
+ #define U2P_R1_TX_FSLS_TUNE_MASK GENMASK(20, 17)
+ #define U2P_R1_TX_HSXV_TUNE_MASK GENMASK(22, 21)
+ #define U2P_R1_OTG_TUNE_MASK GENMASK(25, 23)
+ #define U2P_R1_SQRX_TUNE_MASK GENMASK(28, 26)
+ #define U2P_R1_COMP_DIS_TUNE_MASK GENMASK(31, 29)
+
+/* bits [31:14] are read-only */
+#define U2P_R2 0x8
+ #define U2P_R2_DATA_IN_MASK GENMASK(3, 0)
+ #define U2P_R2_DATA_IN_EN_MASK GENMASK(7, 4)
+ #define U2P_R2_ADDR_MASK GENMASK(11, 8)
+ #define U2P_R2_DATA_OUT_SEL BIT(12)
+ #define U2P_R2_CLK BIT(13)
+ #define U2P_R2_DATA_OUT_MASK GENMASK(17, 14)
+ #define U2P_R2_ACA_PIN_RANGE_C BIT(18)
+ #define U2P_R2_ACA_PIN_RANGE_B BIT(19)
+ #define U2P_R2_ACA_PIN_RANGE_A BIT(20)
+ #define U2P_R2_ACA_PIN_GND BIT(21)
+ #define U2P_R2_ACA_PIN_FLOAT BIT(22)
+ #define U2P_R2_CHARGE_DETECT BIT(23)
+ #define U2P_R2_DEVICE_SESSION_VALID BIT(24)
+ #define U2P_R2_ADP_PROBE BIT(25)
+ #define U2P_R2_ADP_SENSE BIT(26)
+ #define U2P_R2_SESSION_END BIT(27)
+ #define U2P_R2_VBUS_VALID BIT(28)
+ #define U2P_R2_B_VALID BIT(29)
+ #define U2P_R2_A_VALID BIT(30)
+ #define U2P_R2_ID_DIG BIT(31)
+
+#define U2P_R3 0xc
+
+#define RESET_COMPLETE_TIME 500
+
+struct phy_meson_gxl_usb2_priv {
+ struct regmap *regmap;
+ enum phy_mode mode;
+ int is_enabled;
+};
+
+static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = U2P_R3,
+};
+
+static int phy_meson_gxl_usb2_reset(struct phy *phy)
+{
+ struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+ if (!priv->is_enabled)
+ return 0;
+
+ /* reset the PHY and wait until settings are stabilized */
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+ U2P_R0_POWER_ON_RESET);
+ udelay(RESET_COMPLETE_TIME);
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+ udelay(RESET_COMPLETE_TIME);
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
+{
+ struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+ switch (mode) {
+ case PHY_MODE_USB_HOST:
+ case PHY_MODE_USB_OTG:
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+ U2P_R0_DM_PULLDOWN);
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+ U2P_R0_DP_PULLDOWN);
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
+ break;
+
+ case PHY_MODE_USB_DEVICE:
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+ 0);
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+ 0);
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
+ U2P_R0_ID_PULLUP);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ phy_meson_gxl_usb2_reset(phy);
+
+ priv->mode = mode;
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb2_power_off(struct phy *phy)
+{
+ struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+ priv->is_enabled = 0;
+
+ /* power off the PHY by putting it into reset mode */
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+ U2P_R0_POWER_ON_RESET);
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb2_power_on(struct phy *phy)
+{
+ struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ priv->is_enabled = 1;
+
+ /* power on the PHY by taking it out of reset mode */
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+
+ ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
+ if (ret) {
+ phy_meson_gxl_usb2_power_off(phy);
+
+ dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
+ priv->mode);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb2_ops = {
+ .power_on = phy_meson_gxl_usb2_power_on,
+ .power_off = phy_meson_gxl_usb2_power_off,
+ .set_mode = phy_meson_gxl_usb2_set_mode,
+ .reset = phy_meson_gxl_usb2_reset,
+ .owner = THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct resource *res;
+ struct phy_meson_gxl_usb2_priv *priv;
+ struct phy *phy;
+ void __iomem *base;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->mode = PHY_MODE_USB_HOST;
+
+ priv->regmap = devm_regmap_init_mmio(dev, base,
+ &phy_meson_gxl_usb2_regmap_conf);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
+ { .compatible = "amlogic,meson-gxl-usb2-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
+
+static struct platform_driver phy_meson_gxl_usb2_driver = {
+ .probe = phy_meson_gxl_usb2_probe,
+ .driver = {
+ .name = "phy-meson-gxl-usb2",
+ .of_match_table = phy_meson_gxl_usb2_of_match,
+ },
+};
+module_platform_driver(phy_meson_gxl_usb2_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL and GXM USB2 PHY driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1

View File

@@ -0,0 +1,28 @@
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
index 3f0d83b..7590383 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
@@ -129,6 +129,10 @@
};
};
+&usb0 {
+ status = "okay";
+};
+
/* SD card */
&sd_emmc_b {
status = "okay";
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f252201..10b2165 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
-
+obj-$(CONFIG_PHY_MESON_GXL_USB) += phy-meson-gxl-usb2.o
obj-$(CONFIG_ARCH_SUNXI) += allwinner/
obj-$(CONFIG_ARCH_MESON) += amlogic/
obj-$(CONFIG_ARCH_RENESAS) += renesas/