From 948c49455e9d7d97d6c8d70cc96d3095d30f3537 Mon Sep 17 00:00:00 2001 From: Yue Wang Date: Thu, 1 Feb 2018 23:21:39 +0800 Subject: [PATCH] usb: add usb host & device driver support for g12a PD#156734: usb: add usb host & device driver support for g12a Change-Id: Ia12b63f85fb6d980a7c7906664ae2db7c4ddb86b Signed-off-by: Yue Wang --- MAINTAINERS | 5 + arch/arm64/boot/dts/amlogic/g12a_skt.dts | 63 ++++ drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c | 6 + drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h | 2 + drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.c | 22 +- drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h | 1 + drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c | 6 + .../amlogic/usb/dwc_otg/310/dwc_otg_pcd_linux.c | 11 +- drivers/amlogic/usb/phy/Makefile | 3 + drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c | 60 +++ drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h | 26 ++ drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c | 268 +++++++++++++ drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c | 420 +++++++++++++++++++++ drivers/amlogic/usb/phy/usbphy.c | 133 +++++++ drivers/usb/dwc3/core.c | 5 +- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/host.c | 5 + drivers/usb/host/xhci-hub.c | 15 +- drivers/usb/host/xhci-plat.c | 5 + drivers/usb/host/xhci.h | 4 +- include/linux/amlogic/usb-v2.h | 176 +++++++++ include/linux/amlogic/usbtype.h | 2 + 22 files changed, 1229 insertions(+), 10 deletions(-) create mode 100644 drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c create mode 100644 drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h create mode 100644 drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c create mode 100644 drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c create mode 100644 include/linux/amlogic/usb-v2.h diff --git a/MAINTAINERS b/MAINTAINERS index 7f0297b..7fa98f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13531,6 +13531,10 @@ F: drivers/usb/phy/phy-aml-new-usb.h F: drivers/usb/phy/phy-aml-new-usb.c F: drivers/usb/phy/phy-aml-new-usb2.c F: drivers/usb/phy/phy-aml-new-usb3.c +F: drivers/usb/phy/phy-aml-new-usb-v2.h +F: drivers/usb/phy/phy-aml-new-usb-v2.c +F: drivers/usb/phy/phy-aml-new-usb2-v2.c +F: drivers/usb/phy/phy-aml-new-usb3-v2.c F: drivers/usb/phy/phy-aml-usb.h F: drivers/usb/phy/phy-aml-usb.c F: drivers/usb/phy/phy-aml-usb2.c @@ -13541,6 +13545,7 @@ F: include/linux/amlogic/usb-gxbbtv.h F: include/linux/amlogic/usb-gxl.h F: include/linux/amlogic/usb-gxbb.h F: include/linux/amlogic/usbtype.h +F: include/linux/amlogic/usb-v2.h AMLOGIC scpi cpufreq M: jianxin.pan diff --git a/arch/arm64/boot/dts/amlogic/g12a_skt.dts b/arch/arm64/boot/dts/amlogic/g12a_skt.dts index 70e256c..c4106a1 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_skt.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_skt.dts @@ -311,6 +311,69 @@ pinctrl-0 = <&c_uart_pins>; }; + dwc3: dwc3@ff500000 { + compatible = "synopsys, dwc3"; + status = "okay"; + reg = <0x0 0xff500000 0x0 0x100000>; + interrupts = <0 30 4>; + usb-phy = <&usb2_phy_v2>, <&usb3_phy_v2>; + cpu-type = "gxl"; + clock-src = "usb3.0"; + clocks = <&clkc CLKID_USB_GENERAL>; + clock-names = "dwc_general"; + /*snps,super_speed_support;*/ + }; + + usb2_phy_v2: usb2phy@ffe09000 { + compatible = "amlogic, amlogic-new-usb2-v2"; + status = "okay"; + portnum = <2>; + reg = <0x0 0xffe09000 0x0 0x80 + 0x0 0xffd01008 0x0 0x4 + 0x0 0xff636000 0x0 0x2000 + 0x0 0xff63a000 0x0 0x2000>; + }; + + usb3_phy_v2: usb3phy@ffe09080 { + compatible = "amlogic, amlogic-new-usb3-v2"; + status = "okay"; + portnum = <1>; + reg = <0x0 0xffe09080 0x0 0x20>; + phy-reg = <0xff646000>; + phy-reg-size = <0x4>; + interrupts = <0 16 4>; + otg = <0>; + }; + + dwc2_a { + compatible = "amlogic, dwc2"; + device_name = "dwc2_a"; + reg = <0x0 0xff400000 0x0 0x40000>; + status = "okay"; + interrupts = <0 31 4>; + pl-periph-id = <0>; /** lm name */ + clock-src = "usb0"; /** clock src */ + port-id = <0>; /** ref to mach/usb.h */ + port-type = <2>; /** 0: otg, 1: host, 2: slave */ + port-speed = <0>; /** 0: default, high, 1: full */ + port-config = <0>; /** 0: default */ + /*0:default,1:single,2:incr,3:incr4,4:incr8,5:incr16,6:disable*/ + port-dma = <0>; + port-id-mode = <0>; /** 0: hardware, 1: sw_host, 2: sw_slave*/ + usb-fifo = <728>; + cpu-type = "v2"; + /** 0: normal, 1: otg+dwc3 host only, 2: otg+dwc3 device only*/ + controller-type = <1>; + phy-reg = <0xffe09000>; + phy-reg-size = <0xa0>; + /** phy-interface: 0x0: amlogic phy, 0x1: synopsys phy **/ + phy-interface = <0x0>; + clocks = <&clkc CLKID_USB_GENERAL + &clkc CLKID_USB1_TO_DDR>; + clock-names = "usb_general", + "usb1"; + }; + canvas{ compatible = "amlogic, meson, canvas"; dev_name = "amlogic-canvas"; diff --git a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c index 62cfdf1..126bbe5 100644 --- a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c +++ b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c @@ -1472,6 +1472,12 @@ void dwc_otg_core_init(dwc_otg_core_if_t *core_if) break; } + /* AMLOGIC USB PHY interface */ + if (core_if->phy_interface == 1) + usbcfg.b.usbtrdtim = 9; + else + usbcfg.b.usbtrdtim = 5; + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); #ifdef CONFIG_USB_DWC_OTG_LPM diff --git a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h index 890ec40..454dce8 100644 --- a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h +++ b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h @@ -1057,6 +1057,8 @@ struct dwc_otg_core_if { uint8_t stop_adpprb; int controller_type; + + uint32_t phy_interface; }; #ifdef DEBUG diff --git a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.c b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.c index b6dab6a..042b89f 100644 --- a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.c +++ b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.c @@ -611,8 +611,12 @@ static void amlogic_device_detect_work(struct work_struct *work) int ret; if (USB_OTG == dwc_otg_device->core_if->controller_type) { - ret = device_status((unsigned long)dwc_otg_device-> - core_if->usb_peri_reg); + if (dwc_otg_device->core_if->phy_interface == 1) + ret = device_status((unsigned long)dwc_otg_device-> + core_if->usb_peri_reg); + else + ret = device_status_v2((unsigned long)dwc_otg_device-> + core_if->usb_peri_reg); if (!ret) { DWC_PRINTF("usb device plug out, stop pcd!!!\n"); if (dwc_otg_device->pcd->core_if->pcd_cb->stop) @@ -952,6 +956,7 @@ static int dwc_otg_driver_probe(struct platform_device *pdev) unsigned int p_phy_reg_addr = 0; unsigned int p_ctrl_reg_addr = 0; unsigned int phy_reg_addr_size = 0; + unsigned int phy_interface = 1; const char *s_clock_name = NULL; const char *cpu_type = NULL; const char *gpio_name = NULL; @@ -1049,6 +1054,10 @@ static int dwc_otg_driver_probe(struct platform_device *pdev) if (retval < 0) return -EINVAL; + prop = of_get_property(of_node, "phy-interface", NULL); + if (prop) + phy_interface = of_read_ulong(prop, 1); + dwc_otg_module_params.host_rx_fifo_size = dwc_otg_module_params.data_fifo_size / 2; DWC_PRINTF("dwc_otg: %s: type: %d speed: %d, ", s_clock_name, port_type, port_speed); @@ -1143,6 +1152,7 @@ static int dwc_otg_driver_probe(struct platform_device *pdev) dwc_otg_device->core_if->usb_peri_reg = (usb_peri_reg_t *)phy_reg_addr; dwc_otg_device->core_if->controller_type = controller_type; + dwc_otg_device->core_if->phy_interface = phy_interface; /* * Attempt to ensure this device is really a DWC_otg Controller. * Read and verify the SNPSID register contents. The value should be @@ -1387,8 +1397,12 @@ static int dwc_otg_driver_probe(struct platform_device *pdev) #endif #ifdef CONFIG_AMLOGIC_USB3PHY - if (USB_OTG == dwc_otg_device->core_if->controller_type) - aml_new_usb_init(); + if (dwc_otg_device->core_if->controller_type == USB_OTG) { + if (dwc_otg_device->core_if->phy_interface == 1) + aml_new_usb_init(); + else + aml_new_usb_v2_init(); + } #endif return 0; diff --git a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h index 5c9b338..707b206 100644 --- a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h +++ b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h @@ -46,6 +46,7 @@ #ifdef CONFIG_AMLOGIC_USB3PHY extern void aml_new_usb_init(void); +extern void aml_new_usb_v2_init(void); #endif /* Type declarations */ diff --git a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c index 2c4f27f..d3b9808 100644 --- a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c +++ b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c @@ -1127,6 +1127,12 @@ int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t *pcd) /* Full or low speed */ gusbcfg.b.usbtrdtim = 9; } + + /* AMLOGIC USB PHY interface */ + if (GET_CORE_IF(pcd)->phy_interface == 1) + gusbcfg.b.usbtrdtim = 9; + else + gusbcfg.b.usbtrdtim = 5; DWC_WRITE_REG32(&global_regs->gusbcfg, gusbcfg.d32); /* Clear interrupt */ diff --git a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_linux.c b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_linux.c index 22a6a9b..f4cb084 100644 --- a/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_linux.c +++ b/drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_linux.c @@ -62,6 +62,7 @@ #include #include #include +#include #include static struct gadget_wrapper { @@ -1293,7 +1294,10 @@ int pcd_init(struct platform_device *pdev) } #ifdef CONFIG_AMLOGIC_USB3PHY - aml_new_usb_register_notifier(&otg_dev->nb); + if (otg_dev->core_if->phy_interface == 1) + aml_new_usb_register_notifier(&otg_dev->nb); + else + aml_new_usb_v2_register_notifier(&otg_dev->nb); otg_dev->nb.notifier_call = dwc_usb_change; #endif @@ -1361,7 +1365,10 @@ void pcd_remove(struct platform_device *pdev) free_wrapper(gadget_wrapper); dwc_otg_pcd_remove(otg_dev->pcd); #ifdef CONFIG_AMLOGIC_USB3PHY - aml_new_usb_unregister_notifier(&otg_dev->nb); + if (otg_dev->core_if->phy_interface == 1) + aml_new_usb_unregister_notifier(&otg_dev->nb); + else + aml_new_usb_v2_unregister_notifier(&otg_dev->nb); #endif otg_dev->pcd = 0; } diff --git a/drivers/amlogic/usb/phy/Makefile b/drivers/amlogic/usb/phy/Makefile index da8178d..22433e1 100644 --- a/drivers/amlogic/usb/phy/Makefile +++ b/drivers/amlogic/usb/phy/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_AMLOGIC_USB3PHY) += phy-aml-usb3.o obj-$(CONFIG_AMLOGIC_USBPHY) += phy-aml-new-usb.o obj-$(CONFIG_AMLOGIC_USB2PHY) += phy-aml-new-usb2.o obj-$(CONFIG_AMLOGIC_USB3PHY) += phy-aml-new-usb3.o +obj-$(CONFIG_AMLOGIC_USBPHY) += phy-aml-new-usb-v2.o +obj-$(CONFIG_AMLOGIC_USB2PHY) += phy-aml-new-usb2-v2.o +obj-$(CONFIG_AMLOGIC_USB3PHY) += phy-aml-new-usb3-v2.o diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c new file mode 100644 index 0000000..b16a483 --- /dev/null +++ b/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c @@ -0,0 +1,60 @@ +/* + * drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phy-aml-new-usb-v2.h" + +int amlogic_new_usbphy_reset_v2(struct amlogic_usb_v2 *phy) +{ + static int init_count; + int i = 0; + + if (!init_count) { + init_count++; + if (!phy->reset_regs) + aml_cbus_update_bits(0x1102, 0x1<<2, 0x1<<2); + else + writel((readl(phy->reset_regs) | (0x1 << 2)), + phy->reset_regs); + for (i = 0; i < 1000; i++) + udelay(500); + } + + return 0; +} +EXPORT_SYMBOL_GPL(amlogic_new_usbphy_reset_v2); + +int amlogic_new_usbphy_reset_phycfg_v2(struct amlogic_usb_v2 *phy, int cnt) +{ + int i = 0; + + if (phy->reset_regs) + writel((readl(phy->reset_regs) | (1 << (16 + i))), + phy->reset_regs); + + return 0; +} +EXPORT_SYMBOL_GPL(amlogic_new_usbphy_reset_phycfg_v2); diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h b/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h new file mode 100644 index 0000000..a9c3ed1 --- /dev/null +++ b/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h @@ -0,0 +1,26 @@ +/* + * drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include + +#define phy_to_amlusb(x) container_of((x), struct amlogic_usb_v2, phy) + +extern int amlogic_new_usbphy_reset_v2(struct amlogic_usb_v2 *phy); +extern int amlogic_new_usbphy_reset_phycfg_v2 + (struct amlogic_usb_v2 *phy, int cnt); + diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c new file mode 100644 index 0000000..77dae47 --- /dev/null +++ b/drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c @@ -0,0 +1,268 @@ +/* + * drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phy-aml-new-usb-v2.h" + +void set_usb_pll(void __iomem *reg) +{ + /* TO DO set usb PLL */ + writel(0x39400414, reg + 0x40); + writel(0x927E0000, reg + 0x44); + writel(0xAD5B29E9, reg + 0x48); + udelay(100); + writel(0x19400414, reg + 0x40); +} + +static int amlogic_new_usb2_init(struct usb_phy *x) +{ + int i, j, cnt; + + struct amlogic_usb_v2 *phy = phy_to_amlusb(x); + struct u2p_aml_regs_v2 u2p_aml_regs; + union u2p_r0_v2 reg0; + union u2p_r1_v2 reg1; + //u32 val; + + if (phy->suspend_flag) { + phy->suspend_flag = 0; + for (i = 0; i < phy->portnum; i++) { + for (j = 0; j < 2; j++) { + u2p_aml_regs.u2p_r_v2[j] = (void __iomem *) + ((unsigned long)phy->regs + i*PHY_REGISTER_SIZE + + 4 * j); + } + } + /*TO DO: set usb phy to low power mode*/ + return 0; + } + + amlogic_new_usbphy_reset_v2(phy); + + for (i = 0; i < phy->portnum; i++) { + for (j = 0; j < 2; j++) { + u2p_aml_regs.u2p_r_v2[j] = (void __iomem *) + ((unsigned long)phy->regs + i*PHY_REGISTER_SIZE + + 4 * j); + } + + reg0.d32 = readl(u2p_aml_regs.u2p_r_v2[0]); + reg0.b.POR = 1; + reg0.b.host_device = 1; + if (i == 1) + reg0.b.IDPULLUP0 = 1; + + writel(reg0.d32, u2p_aml_regs.u2p_r_v2[0]); + + udelay(10); + amlogic_new_usbphy_reset_phycfg_v2(phy, i); + udelay(50); +#if 0 + /* ID DETECT: usb2_otg_aca_en set to 0 */ + /* usb2_otg_iddet_en set to 1 */ + writel(readl(phy->phy_cfg[i] + 0x54) & (~(1 << 2)), + (phy->phy_cfg[i] + 0x54)); + if (i == 1) { + writel((readl(phy->phy_cfg[i] + 0x50) | (1 << 0)), + (phy->phy_cfg[i] + 0x50)); + } +#endif + reg1.d32 = readl(u2p_aml_regs.u2p_r_v2[1]); + cnt = 0; + while (reg1.b.phy_rdy != 1) { + reg1.d32 = readl(u2p_aml_regs.u2p_r_v2[1]); + /*we wait phy ready max 1ms, common is 100us*/ + if (cnt > 200) + break; + + cnt++; + udelay(5); + } + + /* step 7: pll setting */ + set_usb_pll(phy->phy_cfg[i]); + } + + return 0; +} + +static int amlogic_new_usb2_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static void amlogic_new_usb2phy_shutdown(struct usb_phy *x) +{ + struct amlogic_usb_v2 *phy = phy_to_amlusb(x); + struct u2p_aml_regs_v2 u2p_aml_regs; + int i, j; + + phy->suspend_flag = 1; + for (i = phy->portnum - 1; i >= 0; i--) { + for (j = 0; j < 2; j++) { + u2p_aml_regs.u2p_r_v2[j] = (void __iomem *) + ((unsigned long)phy->regs + i*PHY_REGISTER_SIZE + + 4 * j); + } + + /*TO DO: set usb phy to low power mode*/ + } +} + +static int amlogic_new_usb2_probe(struct platform_device *pdev) +{ + struct amlogic_usb_v2 *phy; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + struct resource *reset_mem; + struct resource *phy_cfg_mem[4]; + void __iomem *phy_base; + void __iomem *reset_base = NULL; + void __iomem *phy_cfg_base[4]; + int portnum = 0; + const void *prop; + int i = 0; + + prop = of_get_property(dev->of_node, "portnum", NULL); + if (prop) + portnum = of_read_ulong(prop, 1); + + if (!portnum) { + dev_err(&pdev->dev, "This phy has no usb port\n"); + return -ENOMEM; + } + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy_base = devm_ioremap_resource(dev, phy_mem); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); + + reset_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (reset_mem) { + reset_base = ioremap(reset_mem->start, + resource_size(reset_mem)); + if (IS_ERR(reset_base)) + return PTR_ERR(reset_base); + } + + for (i = 0; i < portnum; i++) { + phy_cfg_mem[i] = platform_get_resource + (pdev, IORESOURCE_MEM, 2 + i); + if (phy_cfg_mem[i]) { + phy_cfg_base[i] = ioremap(phy_cfg_mem[i]->start, + resource_size(phy_cfg_mem[i])); + if (IS_ERR(phy_cfg_base[i])) + return PTR_ERR(phy_cfg_base[i]); + } + } + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + dev_info(&pdev->dev, "USB2 phy probe:phy_mem:0x%lx, iomap phy_base:0x%lx\n", + (unsigned long)phy_mem->start, (unsigned long)phy_base); + + phy->dev = dev; + phy->regs = phy_base; + phy->reset_regs = reset_base; + phy->portnum = portnum; + phy->suspend_flag = 0; + phy->phy.dev = phy->dev; + phy->phy.label = "amlogic-usbphy2"; + phy->phy.init = amlogic_new_usb2_init; + phy->phy.set_suspend = amlogic_new_usb2_suspend; + phy->phy.shutdown = amlogic_new_usb2phy_shutdown; + phy->phy.type = USB_PHY_TYPE_USB2; + for (i = 0; i < portnum; i++) + phy->phy_cfg[i] = phy_cfg_base[i]; + + usb_add_phy_dev(&phy->phy); + + platform_set_drvdata(pdev, phy); + + pm_runtime_enable(phy->dev); + + return 0; +} + +static int amlogic_new_usb2_remove(struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_PM_RUNTIME + +static int amlogic_new_usb2_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int amlogic_new_usb2_runtime_resume(struct device *dev) +{ + unsigned int ret = 0; + + return ret; +} + +static const struct dev_pm_ops amlogic_new_usb2_pm_ops = { + SET_RUNTIME_PM_OPS(amlogic_new_usb2_runtime_suspend, + amlogic_new_usb2_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&amlogic_new_usb2_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id amlogic_new_usb2_id_table[] = { + { .compatible = "amlogic, amlogic-new-usb2-v2" }, + {} +}; +MODULE_DEVICE_TABLE(of, amlogic_new_usb2_id_table); +#endif + +static struct platform_driver amlogic_new_usb2_v2_driver = { + .probe = amlogic_new_usb2_probe, + .remove = amlogic_new_usb2_remove, + .driver = { + .name = "amlogic-new-usb2-v2", + .owner = THIS_MODULE, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(amlogic_new_usb2_id_table), + }, +}; + +module_platform_driver(amlogic_new_usb2_v2_driver); + +MODULE_ALIAS("platform: amlogic_usb2_v2"); +MODULE_AUTHOR("Amlogic Inc."); +MODULE_DESCRIPTION("amlogic USB2 v2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c new file mode 100644 index 0000000..316f7f6 --- /dev/null +++ b/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c @@ -0,0 +1,420 @@ +/* + * drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phy-aml-new-usb-v2.h" + +#define HOST_MODE 0 +#define DEVICE_MODE 1 + +struct usb_aml_regs_v2 usb_new_aml_regs_v2; +struct amlogic_usb_v2 *g_phy_v2; + +static void set_mode(unsigned long reg_addr, int mode); +BLOCKING_NOTIFIER_HEAD(aml_new_usb_v2_notifier_list); + +int aml_new_usb_v2_register_notifier(struct notifier_block *nb) +{ + int ret; + + ret = blocking_notifier_chain_register + (&aml_new_usb_v2_notifier_list, nb); + + return ret; +} +EXPORT_SYMBOL(aml_new_usb_v2_register_notifier); + +int aml_new_usb_v2_unregister_notifier(struct notifier_block *nb) +{ + int ret; + + ret = blocking_notifier_chain_unregister + (&aml_new_usb_v2_notifier_list, nb); + + return ret; +} +EXPORT_SYMBOL(aml_new_usb_v2_unregister_notifier); + +static void aml_new_usb_notifier_call(unsigned long is_device_on) +{ + blocking_notifier_call_chain + (&aml_new_usb_v2_notifier_list, is_device_on, NULL); +} +//EXPORT_SYMBOL(aml_new_usb_notifier_call); + +static void set_usb_vbus_power + (struct gpio_desc *usb_gd, int pin, char is_power_on) +{ + if (is_power_on) + /*set vbus on by gpio*/ + gpiod_direction_output(usb_gd, is_power_on); + else + /*set vbus off by gpio first*/ + gpiod_direction_output(usb_gd, is_power_on); +} + +static void amlogic_new_set_vbus_power + (struct amlogic_usb_v2 *phy, char is_power_on) +{ + if (phy->vbus_power_pin != -1) + set_usb_vbus_power(phy->usb_gpio_desc, + phy->vbus_power_pin, is_power_on); +} + +static int amlogic_new_usb3_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static void amlogic_new_usb3phy_shutdown(struct usb_phy *x) +{ + struct amlogic_usb_v2 *phy = phy_to_amlusb(x); + + phy->suspend_flag = 1; +} + +void aml_new_usb_v2_init(void) +{ + union usb_r5_v2 r5 = {.d32 = 0}; + unsigned long reg_addr = ((unsigned long) + usb_new_aml_regs_v2.usb_r_v2[0] - 0x80); + + r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]); + if (r5.b.iddig_curr == 0) { + amlogic_new_set_vbus_power(g_phy_v2, 1); + aml_new_usb_notifier_call(0); + set_mode(reg_addr, HOST_MODE); + } +} +EXPORT_SYMBOL(aml_new_usb_v2_init); + +static int amlogic_new_usb3_init(struct usb_phy *x) +{ + struct amlogic_usb_v2 *phy = phy_to_amlusb(x); + union usb_r1_v2 r1 = {.d32 = 0}; + union usb_r2_v2 r2 = {.d32 = 0}; + union usb_r3_v2 r3 = {.d32 = 0}; + union usb_r5_v2 r5 = {.d32 = 0}; + int i = 0; + + if (phy->suspend_flag) { + phy->suspend_flag = 0; + return 0; + } + + /* set the phy from pcie to usb3 */ + if (phy->portnum > 0) + writel((readl(phy->phy3_cfg) | (3<<5)), phy->phy3_cfg); + + for (i = 0; i < 6; i++) { + usb_new_aml_regs_v2.usb_r_v2[i] = (void __iomem *) + ((unsigned long)phy->regs + 4*i); + } + + r1.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[1]); + r1.b.u3h_fladj_30mhz_reg = 0x20; + writel(r1.d32, usb_new_aml_regs_v2.usb_r_v2[1]); + + r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]); + r5.b.iddig_en0 = 1; + r5.b.iddig_en1 = 1; + r5.b.iddig_th = 255; + writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]); + + /* config usb3 phy */ + if (phy->portnum > 0) { + r3.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[3]); + r3.b.p30_ssc_en = 1; + r3.b.p30_ref_ssp_en = 1; + writel(r3.d32, usb_new_aml_regs_v2.usb_r_v2[3]); + udelay(2); + r2.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[2]); + r2.b.p30_pcs_tx_deemph_3p5db = 0x15; + r2.b.p30_pcs_tx_deemph_6db = 0x20; + writel(r2.d32, usb_new_aml_regs_v2.usb_r_v2[2]); + udelay(2); + r1.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[1]); + r1.b.u3h_host_port_power_control_present = 1; + r1.b.u3h_fladj_30mhz_reg = 32; + writel(r1.d32, usb_new_aml_regs_v2.usb_r_v2[1]); + udelay(2); + } + + return 0; +} + +static void set_mode(unsigned long reg_addr, int mode) +{ + struct u2p_aml_regs_v2 u2p_aml_regs; + struct usb_aml_regs_v2 usb_gxl_aml_regs; + union u2p_r0_v2 reg0; + union usb_r0_v2 r0 = {.d32 = 0}; + union usb_r4_v2 r4 = {.d32 = 0}; + + u2p_aml_regs.u2p_r_v2[0] = (void __iomem *) + ((unsigned long)reg_addr + PHY_REGISTER_SIZE); + + usb_gxl_aml_regs.usb_r_v2[0] = (void __iomem *) + ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE + + 4*0); + usb_gxl_aml_regs.usb_r_v2[1] = (void __iomem *) + ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE + + 4*1); + usb_gxl_aml_regs.usb_r_v2[4] = (void __iomem *) + ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE + + 4*4); + + r0.d32 = readl(usb_gxl_aml_regs.usb_r_v2[0]); + if (mode == DEVICE_MODE) { + r0.b.u2d_act = 1; + r0.b.u2d_ss_scaledown_mode = 0; + } else + r0.b.u2d_act = 0; + writel(r0.d32, usb_gxl_aml_regs.usb_r_v2[0]); + + r4.d32 = readl(usb_gxl_aml_regs.usb_r_v2[4]); + if (mode == DEVICE_MODE) + r4.b.p21_SLEEPM0 = 0x1; + else + r4.b.p21_SLEEPM0 = 0x0; + writel(r4.d32, usb_gxl_aml_regs.usb_r_v2[4]); + + reg0.d32 = readl(u2p_aml_regs.u2p_r_v2[0]); + if (mode == DEVICE_MODE) { + reg0.b.host_device = 0; + reg0.b.POR = 0; + } else { + reg0.b.host_device = 1; + reg0.b.POR = 0; + } + writel(reg0.d32, u2p_aml_regs.u2p_r_v2[0]); + + udelay(500); +} + +static void amlogic_gxl_work(struct work_struct *work) +{ + struct amlogic_usb_v2 *phy = + container_of(work, struct amlogic_usb_v2, work.work); + union usb_r5_v2 r5 = {.d32 = 0}; + unsigned long reg_addr = ((unsigned long)phy->regs - 0x80); + + r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]); + if (r5.b.iddig_curr == 0) { + amlogic_new_set_vbus_power(phy, 1); + aml_new_usb_notifier_call(0); + set_mode(reg_addr, HOST_MODE); + } else { + set_mode(reg_addr, DEVICE_MODE); + aml_new_usb_notifier_call(1); + amlogic_new_set_vbus_power(phy, 0); + } + r5.b.usb_iddig_irq = 0; + writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]); +} + +static irqreturn_t amlogic_botg_detect_irq(int irq, void *dev) +{ + struct amlogic_usb_v2 *phy = (struct amlogic_usb_v2 *)dev; + union usb_r5_v2 r5 = {.d32 = 0}; + + r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]); + r5.b.usb_iddig_irq = 0; + writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]); + + schedule_delayed_work(&phy->work, msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +static int amlogic_new_usb3_v2_probe(struct platform_device *pdev) +{ + struct amlogic_usb_v2 *phy; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + void __iomem *phy3_base; + unsigned int phy3_mem; + unsigned int phy3_mem_size = 0; + const char *gpio_name = NULL; + struct gpio_desc *usb_gd = NULL; + const void *prop; + int portnum = 0; + int irq; + int retval; + int gpio_vbus_power_pin = -1; + int otg = 0; + + gpio_name = of_get_property(dev->of_node, "gpio-vbus-power", NULL); + if (gpio_name) { + gpio_vbus_power_pin = 1; + usb_gd = gpiod_get_index(&pdev->dev, + NULL, 0, GPIOD_OUT_LOW); + if (IS_ERR(usb_gd)) + return -1; + } + + prop = of_get_property(dev->of_node, "portnum", NULL); + if (prop) + portnum = of_read_ulong(prop, 1); + + if (!portnum) + dev_err(&pdev->dev, "This phy has no usb port\n"); + + prop = of_get_property(dev->of_node, "otg", NULL); + if (prop) + otg = of_read_ulong(prop, 1); + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy_base = devm_ioremap_resource(dev, phy_mem); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); + + retval = of_property_read_u32(dev->of_node, "phy-reg", &phy3_mem); + if (retval < 0) + return -EINVAL; + + retval = of_property_read_u32 + (dev->of_node, "phy-reg-size", &phy3_mem_size); + if (retval < 0) + return -EINVAL; + + phy3_base = devm_ioremap_nocache + (&(pdev->dev), (resource_size_t)phy3_mem, + (unsigned long)phy3_mem_size); + if (!phy3_base) + return -ENOMEM; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + if (otg) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + retval = request_irq(irq, amlogic_botg_detect_irq, + IRQF_SHARED | IRQ_LEVEL, + "amlogic_botg_detect", phy); + + if (retval) { + dev_err(&pdev->dev, "request of irq%d failed\n", irq); + retval = -EBUSY; + return retval; + } + } + + dev_info(&pdev->dev, "USB3 phy probe:phy_mem:0x%lx, iomap phy_base:0x%lx\n", + (unsigned long)phy_mem->start, (unsigned long)phy_base); + + phy->dev = dev; + phy->regs = phy_base; + phy->phy3_cfg = phy3_base; + phy->portnum = portnum; + phy->suspend_flag = 0; + phy->phy.dev = phy->dev; + phy->phy.label = "amlogic-usbphy3"; + phy->phy.init = amlogic_new_usb3_init; + phy->phy.set_suspend = amlogic_new_usb3_suspend; + phy->phy.shutdown = amlogic_new_usb3phy_shutdown; + phy->phy.type = USB_PHY_TYPE_USB3; + phy->vbus_power_pin = gpio_vbus_power_pin; + phy->usb_gpio_desc = usb_gd; + + INIT_DELAYED_WORK(&phy->work, amlogic_gxl_work); + + usb_add_phy_dev(&phy->phy); + + platform_set_drvdata(pdev, phy); + + pm_runtime_enable(phy->dev); + + g_phy_v2 = phy; + + return 0; +} + +static int amlogic_new_usb3_remove(struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_PM_RUNTIME + +static int amlogic_new_usb3_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int amlogic_new_usb3_runtime_resume(struct device *dev) +{ + u32 ret = 0; + + return ret; +} + +static const struct dev_pm_ops amlogic_new_usb3_pm_ops = { + SET_RUNTIME_PM_OPS(amlogic_new_usb3_runtime_suspend, + amlogic_new_usb3_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&amlogic_new_usb3_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id amlogic_new_usb3_v2_id_table[] = { + { .compatible = "amlogic, amlogic-new-usb3-v2" }, + {} +}; +MODULE_DEVICE_TABLE(of, amlogic_new_usb3_v2_id_table); +#endif + +static struct platform_driver amlogic_new_usb3_v2_driver = { + .probe = amlogic_new_usb3_v2_probe, + .remove = amlogic_new_usb3_remove, + .driver = { + .name = "amlogic-new-usb3-v2", + .owner = THIS_MODULE, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(amlogic_new_usb3_v2_id_table), + }, +}; + +module_platform_driver(amlogic_new_usb3_v2_driver); + +MODULE_ALIAS("platform: amlogic_usb3_v2"); +MODULE_AUTHOR("Amlogic Inc."); +MODULE_DESCRIPTION("amlogic USB3 v2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/amlogic/usb/phy/usbphy.c b/drivers/amlogic/usb/phy/usbphy.c index 6f14c06..a6bea85 100644 --- a/drivers/amlogic/usb/phy/usbphy.c +++ b/drivers/amlogic/usb/phy/usbphy.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,26 @@ int device_status(unsigned long usb_peri_reg) } EXPORT_SYMBOL(device_status); +/* ret 1: device plug in */ +/* ret 0: device plug out */ +int device_status_v2(unsigned long usb_peri_reg) +{ + struct u2p_aml_regs_v2 u2p_aml_regs; + union u2p_r1_v2 reg1; + int ret = 1; + + u2p_aml_regs.u2p_r_v2[1] = (void __iomem *) + ((unsigned long)usb_peri_reg + + PHY_REGISTER_SIZE + 0x4); + reg1.d32 = readl(u2p_aml_regs.u2p_r_v2[1]); + if (!reg1.b.OTGSESSVLD0) + ret = 0; + + return ret; +} +EXPORT_SYMBOL(device_status_v2); + + int clk_enable_usb_meson8(struct platform_device *pdev, const char *s_clock_name, unsigned long usb_peri_reg) { @@ -368,6 +389,49 @@ static void set_device_mode(struct platform_device *pdev, } } +static void set_device_mode_v2(struct platform_device *pdev, + unsigned long reg_addr, int controller_type) +{ + struct u2p_aml_regs_v2 u2p_aml_regs; + struct usb_aml_regs_v2 usb_aml_regs; + union u2p_r0_v2 reg0; + union usb_r0_v2 r0 = {.d32 = 0}; + union usb_r1_v2 r1 = {.d32 = 0}; + union usb_r4_v2 r4 = {.d32 = 0}; + + u2p_aml_regs.u2p_r_v2[0] = (void __iomem *) + ((unsigned long)reg_addr + PHY_REGISTER_SIZE); + usb_aml_regs.usb_r_v2[0] = (void __iomem *) + ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE + + 4*0); + usb_aml_regs.usb_r_v2[1] = (void __iomem *) + ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE + + 4*1); + usb_aml_regs.usb_r_v2[4] = (void __iomem *) + ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE + + 4*4); + r0.d32 = readl(usb_aml_regs.usb_r_v2[0]); + r0.b.u2d_act = 1; + r0.b.u2d_ss_scaledown_mode = 0; + writel(r0.d32, usb_aml_regs.usb_r_v2[0]); + + r4.d32 = readl(usb_aml_regs.usb_r_v2[4]); + r4.b.p21_SLEEPM0 = 0x1; + writel(r4.d32, usb_aml_regs.usb_r_v2[4]); + + if (controller_type != USB_OTG) { + r1.d32 = readl(usb_aml_regs.usb_r_v2[1]); + r1.b.u3h_host_u2_port_disable = 0x2; + writel(r1.d32, usb_aml_regs.usb_r_v2[1]); + } + + reg0.d32 = readl(u2p_aml_regs.u2p_r_v2[0]); + reg0.b.host_device = 0; + reg0.b.POR = 0; + writel(reg0.d32, u2p_aml_regs.u2p_r_v2[0]); +} + + int clk_enable_usb_gxbabytv(struct platform_device *pdev, const char *s_clock_name, unsigned long usb_peri_reg, int controller_type) @@ -404,6 +468,23 @@ int clk_enable_usb_gxl(struct platform_device *pdev, } +int clk_enable_usb_v2(struct platform_device *pdev, + const char *s_clock_name, unsigned long usb_peri_reg, + int controller_type) +{ + struct clk *usb_reset; + + usb_reset = devm_clk_get(&pdev->dev, "usb_general"); + clk_prepare_enable(usb_reset); + p_clk_reset[pdev->id].usb_reset_usb_general = usb_reset; + usb_reset = devm_clk_get(&pdev->dev, "usb1"); + clk_prepare_enable(usb_reset); + p_clk_reset[pdev->id].usb_reset_usb_to_ddr = usb_reset; + set_device_mode_v2(pdev, usb_peri_reg, controller_type); + return 0; +} + + void clk_disable_usb_gxbabytv(struct platform_device *pdev, const char *s_clock_name, unsigned long usb_peri_reg) @@ -432,6 +513,19 @@ void clk_disable_usb_gxl(struct platform_device *pdev, return; } +void clk_disable_usb_v2(struct platform_device *pdev, + const char *s_clock_name, + unsigned long usb_peri_reg) +{ + struct clk *usb_reset; + + usb_reset = p_clk_reset[pdev->id].usb_reset_usb_general; + clk_disable_unprepare(usb_reset); + usb_reset = p_clk_reset[pdev->id].usb_reset_usb_to_ddr; + clk_disable_unprepare(usb_reset); +} + + int clk_resume_usb_gxbaby(struct platform_device *pdev, const char *s_clock_name, unsigned long usb_peri_reg) @@ -489,6 +583,33 @@ int clk_resume_usb_gxl(struct platform_device *pdev, } +int clk_resume_usb_v2(struct platform_device *pdev, + const char *s_clock_name, + unsigned long usb_peri_reg) +{ + struct clk *usb_reset; + + if (pdev->id == 0) { + usb_reset = p_clk_reset[pdev->id].usb_reset_usb_general; + clk_prepare_enable(usb_reset); + usb_reset = p_clk_reset[pdev->id].usb_reset_usb_to_ddr; + clk_prepare_enable(usb_reset); + } else if (pdev->id == 1) { + usb_reset = p_clk_reset[pdev->id].usb_reset_usb_general; + clk_prepare_enable(usb_reset); + usb_reset = p_clk_reset[pdev->id].usb_reset_usb_to_ddr; + clk_prepare_enable(usb_reset); + } else { + dev_err(&pdev->dev, "bad usb clk name.\n"); + return -1; + } + + dmb(4); + + return 0; +} + + int clk_enable_usb(struct platform_device *pdev, const char *s_clock_name, unsigned long usb_peri_reg, const char *cpu_type, int controller_type) @@ -510,6 +631,9 @@ int clk_enable_usb(struct platform_device *pdev, const char *s_clock_name, else if (!strcmp(cpu_type, GXL)) ret = clk_enable_usb_gxl(pdev, s_clock_name, usb_peri_reg, controller_type); + else if (!strcmp(cpu_type, V2)) + ret = clk_enable_usb_v2(pdev, + s_clock_name, usb_peri_reg, controller_type); /*add other cpu type's usb clock enable*/ @@ -536,6 +660,9 @@ int clk_disable_usb(struct platform_device *pdev, const char *s_clock_name, else if (!strcmp(cpu_type, GXL)) clk_disable_usb_gxl(pdev, s_clock_name, usb_peri_reg); + else if (!strcmp(cpu_type, V2)) + clk_disable_usb_v2(pdev, + s_clock_name, usb_peri_reg); dmb(4); return 0; @@ -560,6 +687,9 @@ int clk_resume_usb(struct platform_device *pdev, const char *s_clock_name, else if (!strcmp(cpu_type, GXL)) ret = clk_resume_usb_gxl(pdev, s_clock_name, usb_peri_reg); + else if (!strcmp(cpu_type, V2)) + ret = clk_resume_usb_v2(pdev, + s_clock_name, usb_peri_reg); /*add other cpu type's usb clock enable*/ @@ -583,6 +713,9 @@ int clk_suspend_usb(struct platform_device *pdev, const char *s_clock_name, else if (!strcmp(cpu_type, GXL)) clk_disable_usb_gxl(pdev, s_clock_name, usb_peri_reg); + else if (!strcmp(cpu_type, V2)) + clk_disable_usb_v2(pdev, + s_clock_name, usb_peri_reg); dmb(4); return 0; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8a9463c..a2926a6 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1141,7 +1141,10 @@ static int dwc3_probe(struct platform_device *pdev) &dwc->hsphy_interface); device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); - +#ifdef CONFIG_AMLOGIC_USB + dwc->super_speed_support = device_property_read_bool(dev, + "snps,super_speed_support"); +#endif dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4f2638f..bc851ca 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -982,6 +982,7 @@ struct dwc3 { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; #ifdef CONFIG_AMLOGIC_USB + unsigned super_speed_support:1; struct clk *general_clk; #endif }; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 626d87d..2ea098c 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -113,6 +113,11 @@ int dwc3_host_init(struct dwc3 *dwc) } } +#ifdef CONFIG_AMLOGIC_USB + if (dwc->super_speed_support) + props[prop_idx++].name = "usb3-support"; +#endif + phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy", dev_name(&xhci->dev)); phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy", diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 0722f75..6b7c7da 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -255,6 +255,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, unsigned int i; ports = xhci->num_usb3_ports; + xhci_common_hub_descriptor(xhci, desc, ports); desc->bDescriptorType = USB_DT_SS_HUB; desc->bDescLength = USB_DT_SS_HUB_SIZE; @@ -279,12 +280,22 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, struct usb_hub_descriptor *desc) { - +#ifdef CONFIG_AMLOGIC_USB + if (xhci->quirks & XHCI_AML_SUPER_SPEED_SUPPORT) { + if (hcd->speed >= HCD_USB3) + xhci_usb3_hub_descriptor(hcd, xhci, desc); + else + xhci_usb2_hub_descriptor(hcd, xhci, desc); + } else { + if (hcd->speed < HCD_USB3) + xhci_usb2_hub_descriptor(hcd, xhci, desc); + } +#else if (hcd->speed >= HCD_USB3) xhci_usb3_hub_descriptor(hcd, xhci, desc); else xhci_usb2_hub_descriptor(hcd, xhci, desc); - +#endif } static unsigned int xhci_port_speed(unsigned int port_status) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 53a7037..c2722b5 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -237,6 +237,11 @@ static int xhci_plat_probe(struct platform_device *pdev) if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped")) xhci->quirks |= XHCI_BROKEN_PORT_PED; +#ifdef CONFIG_AMLOGIC_USB + if (device_property_read_bool(&pdev->dev, "usb3-support")) + xhci->quirks |= XHCI_AML_SUPER_SPEED_SUPPORT; +#endif + hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 836398a..98e1616 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1662,7 +1662,9 @@ struct xhci_hcd { #define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26) /* Reserved. It was XHCI_U2_DISABLE_WAKE */ #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) - +#ifdef CONFIG_AMLOGIC_USB +#define XHCI_AML_SUPER_SPEED_SUPPORT (1 << 29) +#endif unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ diff --git a/include/linux/amlogic/usb-v2.h b/include/linux/amlogic/usb-v2.h new file mode 100644 index 0000000..464fd2e --- /dev/null +++ b/include/linux/amlogic/usb-v2.h @@ -0,0 +1,176 @@ +/* + * include/linux/amlogic/usb-v2.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#ifndef __USB_V2_HEADER_ +#define __USB_V2_HEADER_ + +#include +#include +#include +#include +#include + +#define PHY_REGISTER_SIZE 0x20 +/* Register definitions */ + +int aml_new_usb_v2_register_notifier(struct notifier_block *nb); +int aml_new_usb_v2_unregister_notifier(struct notifier_block *nb); + +struct u2p_aml_regs_v2 { + void __iomem *u2p_r_v2[2]; +}; + +union u2p_r0_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned host_device:1; + unsigned power_ok:1; + unsigned hast_mode:1; + unsigned POR:1; + unsigned IDPULLUP0:1; + unsigned DRVVBUS0:1; + unsigned reserved:26; + } b; +}; + +union u2p_r1_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned phy_rdy:1; + unsigned IDDIG0:1; + unsigned OTGSESSVLD0:1; + unsigned VBUSVALID0:1; + unsigned reserved:28; + } b; +}; + +struct usb_aml_regs_v2 { + void __iomem *usb_r_v2[6]; +}; + +union usb_r0_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned reserved:17; + unsigned p30_lane0_tx2rx_loopback:1; + unsigned p30_lane0_ext_pclk_reg:1; + unsigned p30_pcs_rx_los_mask_val:10; + unsigned u2d_ss_scaledown_mode:2; + unsigned u2d_act:1; + } b; +}; + +union usb_r1_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned u3h_bigendian_gs:1; + unsigned u3h_pme_en:1; + unsigned u3h_hub_port_overcurrent:3; + unsigned reserved_1:2; + unsigned u3h_hub_port_perm_attach:3; + unsigned reserved_2:2; + unsigned u3h_host_u2_port_disable:2; + unsigned reserved_3:2; + unsigned u3h_host_u3_port_disable:1; + unsigned u3h_host_port_power_control_present:1; + unsigned u3h_host_msi_enable:1; + unsigned u3h_fladj_30mhz_reg:6; + unsigned p30_pcs_tx_swing_full:7; + } b; +}; + +union usb_r2_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned reserved:20; + unsigned p30_pcs_tx_deemph_3p5db:6; + unsigned p30_pcs_tx_deemph_6db:6; + } b; +}; + +union usb_r3_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned p30_ssc_en:1; + unsigned p30_ssc_range:3; + unsigned p30_ssc_ref_clk_sel:9; + unsigned p30_ref_ssp_en:1; + unsigned reserved:18; + } b; +}; + +union usb_r4_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned p21_PORTRESET0:1; + unsigned p21_SLEEPM0:1; + unsigned mem_pd:2; + unsigned p21_only:1; + unsigned reserved:27; + } b; +}; + +union usb_r5_v2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned iddig_sync:1; + unsigned iddig_reg:1; + unsigned iddig_cfg:2; + unsigned iddig_en0:1; + unsigned iddig_en1:1; + unsigned iddig_curr:1; + unsigned usb_iddig_irq:1; + unsigned iddig_th:8; + unsigned iddig_cnt:8; + unsigned reserved:8; + } b; +}; + +struct amlogic_usb_v2 { + struct usb_phy phy; + struct device *dev; + void __iomem *regs; + void __iomem *reset_regs; + void __iomem *phy_cfg[4]; + void __iomem *phy3_cfg; + /* Set VBus Power though GPIO */ + int vbus_power_pin; + int vbus_power_pin_work_mask; + struct delayed_work work; + struct gpio_desc *usb_gpio_desc; + + int portnum; + int suspend_flag; +}; + +#endif diff --git a/include/linux/amlogic/usbtype.h b/include/linux/amlogic/usbtype.h index edb2f20..0f5db62 100644 --- a/include/linux/amlogic/usbtype.h +++ b/include/linux/amlogic/usbtype.h @@ -25,6 +25,7 @@ #define GXBABY "gxbaby" #define GXBABYTV "gxtvbaby" #define GXL "gxl" +#define V2 "v2" #define USB_NORMAL 0 #define USB_HOST_ONLY 1 @@ -92,6 +93,7 @@ int clk_suspend_usb(struct platform_device *pdev, const char *s_clock_name, unsigned long usb_peri_reg, const char *cpu_type); int device_status(unsigned long usb_peri_reg); +int device_status_v2(unsigned long usb_peri_reg); extern int dwc_otg_power_register_notifier(struct notifier_block *nb); extern int dwc_otg_power_unregister_notifier(struct notifier_block *nb); -- 2.7.4