mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
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 <yue.wang@amlogic.com>
This commit is contained in:
@@ -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 <jianxin.pan@amlogic.com>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1057,6 +1057,8 @@ struct dwc_otg_core_if {
|
||||
uint8_t stop_adpprb;
|
||||
|
||||
int controller_type;
|
||||
|
||||
uint32_t phy_interface;
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/amlogic/usb-gxl.h>
|
||||
#include <linux/amlogic/usb-v2.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
60
drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c
Normal file
60
drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c
Normal file
@@ -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 <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/amlogic/usb-v2.h>
|
||||
#include <linux/amlogic/iomap.h>
|
||||
#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);
|
||||
26
drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h
Normal file
26
drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h
Normal file
@@ -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 <linux/usb/phy.h>
|
||||
#include <linux/amlogic/usb-v2.h>
|
||||
|
||||
#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);
|
||||
|
||||
268
drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c
Normal file
268
drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c
Normal file
@@ -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 <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb/phy_companion.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/amlogic/usb-v2.h>
|
||||
#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");
|
||||
420
drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c
Normal file
420
drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c
Normal file
@@ -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 <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/amlogic/usb-v2.h>
|
||||
#include <linux/amlogic/aml_gpio_consumer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/notifier.h>
|
||||
#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");
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amlogic/usb-gxbb.h>
|
||||
#include <linux/amlogic/usb-gxbbtv.h>
|
||||
#include <linux/amlogic/usb-v2.h>
|
||||
#include <linux/amlogic/cpu_version.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
176
include/linux/amlogic/usb-v2.h
Normal file
176
include/linux/amlogic/usb-v2.h
Normal file
@@ -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 <linux/usb/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amlogic/aml_gpio_consumer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user