usb: support rk3368 OTG device side and EHCI

This commit is contained in:
lyz 2014-12-15 20:45:51 +08:00 committed by Huang, Tao
commit d2f97737dd
8 changed files with 641 additions and 32 deletions

View file

@ -186,16 +186,6 @@
};
};
usb_control {
compatible = "rockchip,rk3288-usb-control";
host_drv_gpio = <&gpio0 GPIO_A4 GPIO_ACTIVE_LOW>;
otg_drv_gpio = <&gpio0 GPIO_D1 GPIO_ACTIVE_LOW>;
rockchip,remote_wakeup;
rockchip,usb_irq_wakeup;
};
io-domains {
compatible = "rockchip,rk3368-io-voltage-domain";
rockchip,grf = <&grf>;
@ -748,6 +738,14 @@
status="disabled";
};
&dwc_control_usb {
host_drv_gpio = <&gpio0 GPIO_A4 GPIO_ACTIVE_LOW>;
otg_drv_gpio = <&gpio0 GPIO_D1 GPIO_ACTIVE_LOW>;
rockchip,remote_wakeup;
rockchip,usb_irq_wakeup;
};
/include/ "../../../arm/boot/dts/act8846.dtsi"
&act8846 {
gpios =<&gpio0 GPIO_A0 GPIO_ACTIVE_LOW>,<&gpio0 GPIO_A3 GPIO_ACTIVE_HIGH>;

View file

@ -1175,6 +1175,83 @@
clock-names = "clk_crypto", "sclk_crypto", "mclk_crypto";
status = "okay";
};
dwc_control_usb: dwc-control-usb {
compatible = "rockchip,rk3368-dwc-control-usb";
rockchip,grf = <&grf>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "otg_id", "otg_bvalid",
"otg_linestate", "host0_linestate";
clocks = <&clk_gates20 6>, <&usbphy_480m>;
clock-names = "hclk_usb_peri", "usbphy_480m";
//resets = <&reset RK3128_RST_USBPOR>;
//reset-names = "usbphy_por";
usb_bc{
compatible = "inno,phy";
regbase = &dwc_control_usb;
rk_usb,bvalid = <0x04b 23 1>;
rk_usb,iddig = <0x04b 26 1>;
rk_usb,vdmsrcen = <0x718 12 1>;
rk_usb,vdpsrcen = <0x718 11 1>;
rk_usb,rdmpden = <0x718 10 1>;
rk_usb,idpsrcen = <0x718 9 1>;
rk_usb,idmsinken = <0x718 8 1>;
rk_usb,idpsinken = <0x718 7 1>;
rk_usb,dpattach = <0x4b8 31 1>;
rk_usb,cpdet = <0x4b8 30 1>;
rk_usb,dcpattach = <0x4b8 29 1>;
};
};
usb0: usb@ff580000 {
compatible = "rockchip,rk3368_usb20_otg";
reg = <0x0 0xff580000 0x0 0x40000>;
interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_gates8 1>, <&clk_gates20 1>;
clock-names = "clk_usbphy0", "hclk_otg";
resets = <&reset RK3368_SRST_USBOTG0_H>, <&reset RK3368_SRST_USBOTGPHY0>,
<&reset RK3368_SRST_USBOTGC0>;
reset-names = "otg_ahb", "otg_phy", "otg_controller";
/*0 - Normal, 1 - Force Host, 2 - Force Device*/
rockchip,usb-mode = <0>;
};
usb_ehci: usb@ff500000 {
compatible = "generic-ehci";
reg = <0x0 0xff500000 0x0 0x20000>;
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_gates8 1>, <&clk_gates20 3>;
clock-names = "clk_usbphy0", "hclk_ehci";
//resets = <&reset RK3288_SOFT_RST_USBHOST0_H>, <&reset RK3288_SOFT_RST_USBHOST0PHY>,
// <&reset RK3288_SOFT_RST_USBHOST0C>, <&reset RK3288_SOFT_RST_USB_HOST0>;
//reset-names = "ehci_ahb", "ehci_phy", "ehci_controller", "ehci";
};
usb_ohci: usb@ff520000 {
compatible = "generic-ohci";
reg = <0x0 0xff520000 0x0 0x20000>;
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_gates8 1>, <&clk_gates20 3>;
clock-names = "clk_usbphy0", "hclk_ohci";
};
usb_hsic: usb@ff5c0000 {
compatible = "rockchip,rk3288_rk_hsic_host";
reg = <0x0 0xff5c0000 0x0 0x40000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
/*
clocks = <&hsicphy_480m>, <&clk_gates7 8>,
<&hsicphy_12m>, <&usbphy_480m>,
<&otgphy1_480m>, <&otgphy2_480m>;
clock-names = "hsicphy_480m", "hclk_hsic",
"hsicphy_12m", "usbphy_480m",
"hsic_usbphy1", "hsic_usbphy2";
resets = <&reset RK3288_SOFT_RST_HSIC>, <&reset RK3288_SOFT_RST_HSIC_AUX>,
<&reset RK3288_SOFT_RST_HSICPHY>;
reset-names = "hsic_ahb", "hsic_aux", "hsic_phy";
*/
status = "disabled";
};
pinctrl: pinctrl {
compatible = "rockchip,rk3368-pinctrl";

View file

@ -25,5 +25,5 @@ endif
dwc_otg-objs += common_port/dwc_common_linux.o
#objs relative to RK platform
dwc_otg-objs += usbdev_rk30.o usbdev_rk32.o usbdev_rk3036.o usbdev_rk3126.o usbdev_bc.o
dwc_otg-objs += usbdev_rk30.o usbdev_rk32.o usbdev_rk3036.o usbdev_rk3126.o usbdev_rk3368.o usbdev_bc.o
obj-$(CONFIG_DWC_OTG_310) := dwc_otg.o

View file

@ -959,15 +959,13 @@ static int host20_driver_probe(struct platform_device *_dev)
of_match_device(of_match_ptr(usb20_host_of_match), &_dev->dev);
if (match && match->data) {
dev->platform_data = (void *)match->data;
pldata = (void *)match->data;
pldata->dev = dev;
} else {
dev_err(dev, "usb20host match failed\n");
dev_err(dev, "usb20otg match failed\n");
return -EINVAL;
}
pldata = dev->platform_data;
pldata->dev = dev;
if (!node) {
dev_err(dev, "device node not found\n");
return -EINVAL;
@ -1012,8 +1010,7 @@ static int host20_driver_probe(struct platform_device *_dev)
retval = -ENOMEM;
goto clk_disable;
}
dev_dbg(&_dev->dev, "base=0x%08x\n",
(unsigned)dwc_otg_device->os_dep.base);
dev_dbg(&_dev->dev, "base=0x%p\n", dwc_otg_device->os_dep.base);
/* Set device flags indicating whether the HCD supports DMA. */
if (!_dev->dev.dma_mask)
@ -1156,18 +1153,18 @@ static int dwc_otg_driver_resume(struct platform_device *_dev)
static void dwc_otg_driver_shutdown(struct platform_device *_dev)
{
struct device *dev = &_dev->dev;
struct dwc_otg_platform_data *pldata = dev->platform_data;
dwc_otg_device_t *otg_dev = dev->platform_data;
dwc_otg_core_if_t *core_if = otg_dev->core_if;
struct dwc_otg_platform_data *pldata = otg_dev->pldata;
dctl_data_t dctl = {.d32 = 0 };
DWC_PRINTF("%s: disconnect USB %s mode\n", __func__,
dwc_otg_is_host_mode(core_if) ? "host" : "device");
if( pldata->dwc_otg_uart_mode != NULL)
pldata->dwc_otg_uart_mode( pldata, PHY_USB_MODE);
if(pldata->phy_suspend != NULL)
pldata->phy_suspend(pldata, USB_PHY_ENABLED);
if (pldata->dwc_otg_uart_mode != NULL)
pldata->dwc_otg_uart_mode(pldata, PHY_USB_MODE);
if (pldata->phy_suspend != NULL)
pldata->phy_suspend(pldata, USB_PHY_ENABLED);
if (dwc_otg_is_host_mode(core_if)) {
if (core_if->hcd_cb && core_if->hcd_cb->stop)
core_if->hcd_cb->stop(core_if->hcd_cb_p);
@ -1303,6 +1300,10 @@ static const struct of_device_id usb20_otg_of_match[] = {
.compatible = "rockchip,rk3126_usb20_otg",
.data = &usb20otg_pdata_rk3126,
},
{
.compatible = "rockchip,rk3368_usb20_otg",
.data = &usb20otg_pdata_rk3368,
},
{ },
};
@ -1332,21 +1333,19 @@ static int otg20_driver_probe(struct platform_device *_dev)
const struct of_device_id *match =
of_match_device(of_match_ptr(usb20_otg_of_match), &_dev->dev);
if (match) {
dev->platform_data = (void *)match->data;
if (match && match->data) {
pldata = (void *)match->data;
pldata->dev = dev;
} else {
dev_err(dev, "usb20otg match failed\n");
return -EINVAL;
}
pldata = dev->platform_data;
pldata->dev = dev;
if (!node) {
dev_err(dev, "device node not found\n");
return -EINVAL;
}
/*todo : move to usbdev_rk-XX.c */
if (pldata->hw_init)
pldata->hw_init();
@ -1395,8 +1394,7 @@ static int otg20_driver_probe(struct platform_device *_dev)
retval = -ENOMEM;
goto clk_disable;
}
dev_dbg(&_dev->dev, "base=0x%08x\n",
(unsigned)dwc_otg_device->os_dep.base);
dev_dbg(&_dev->dev, "base=0x%p\n", dwc_otg_device->os_dep.base);
/* Set device flags indicating whether the HCD supports DMA. */
if (!_dev->dev.dma_mask)

View file

@ -19,6 +19,8 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/rockchip/cru.h>
#include <linux/rockchip/grf.h>
#include <linux/rockchip/cpu.h>
@ -71,6 +73,10 @@ extern struct dwc_otg_platform_data usb20otg_pdata_rk3126;
extern struct dwc_otg_platform_data usb20host_pdata_rk3126;
extern struct dwc_otg_platform_data usb20ohci_pdata_rk3126;
extern struct rkehci_platform_data usb20ehci_pdata_rk3126;
/* rk3368 platform data */
extern struct rkehci_platform_data usb20ehci_pdata_rk3368;
extern struct dwc_otg_platform_data usb20ohci_pdata_rk3368;
extern struct dwc_otg_platform_data usb20otg_pdata_rk3368;
struct dwc_otg_platform_data {
void *privdata;
@ -120,6 +126,7 @@ struct dwc_otg_control_usb {
pGRF_SOC_STATUS19_RK3288 grf_soc_status19_rk3288;
pGRF_SOC_STATUS21_RK3288 grf_soc_status21_rk3288;
struct regmap *grf;
struct gpio *host_gpios;
struct gpio *otg_gpios;
struct clk *hclk_usb_peri;

View file

@ -0,0 +1,516 @@
#include "usbdev_rk.h"
#include "usbdev_grf_regs.h"
#include "dwc_otg_regs.h"
static struct dwc_otg_control_usb *control_usb;
static u32 uoc_read(u32 reg)
{
unsigned int val;
regmap_read(control_usb->grf, reg, &val);
return val;
}
static void uoc_write(u32 value, u32 reg)
{
regmap_write(control_usb->grf, reg, value);
}
#ifdef CONFIG_USB20_OTG
static void usb20otg_hw_init(void)
{
/* Turn off differential receiver in suspend mode */
uoc_write(UOC_HIWORD_UPDATE(0, 1, 2), 0x798);
/* other haredware init,include:
* DRV_VBUS GPIO init */
if (gpio_is_valid(control_usb->otg_gpios->gpio))
gpio_set_value(control_usb->otg_gpios->gpio, 0);
}
static void usb20otg_phy_suspend(void *pdata, int suspend)
{
struct dwc_otg_platform_data *usbpdata = pdata;
if (suspend) {
/* enable soft control */
uoc_write(UOC_HIWORD_UPDATE(0x55, 0x7f, 0), 0x700);
usbpdata->phy_status = 1;
} else {
/* exit suspend */
uoc_write(UOC_HIWORD_UPDATE(0x0, 0x1, 0), 0x700);
usbpdata->phy_status = 0;
}
}
static void usb20otg_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
{
struct dwc_otg_platform_data *usbpdata = pdata;
struct reset_control *rst_otg_h, *rst_otg_p, *rst_otg_c;
rst_otg_h = devm_reset_control_get(usbpdata->dev, "otg_ahb");
rst_otg_p = devm_reset_control_get(usbpdata->dev, "otg_phy");
rst_otg_c = devm_reset_control_get(usbpdata->dev, "otg_controller");
if (IS_ERR(rst_otg_h) || IS_ERR(rst_otg_p) || IS_ERR(rst_otg_c)) {
dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
return;
}
switch (rst_type) {
case RST_POR:
/* PHY reset */
uoc_write(UOC_HIWORD_UPDATE(0x1, 0x3, 0), 0x700);
reset_control_assert(rst_otg_p);
udelay(15);
uoc_write(UOC_HIWORD_UPDATE(0x2, 0x3, 0), 0x700);
udelay(1500);
reset_control_deassert(rst_otg_p);
udelay(2);
/* Controller reset */
reset_control_assert(rst_otg_c);
reset_control_assert(rst_otg_h);
udelay(2);
reset_control_deassert(rst_otg_c);
reset_control_deassert(rst_otg_h);
break;
default:
break;
}
}
static void usb20otg_clock_init(void *pdata)
{
struct dwc_otg_platform_data *usbpdata = pdata;
struct clk *ahbclk, *phyclk;
ahbclk = devm_clk_get(usbpdata->dev, "hclk_otg");
if (IS_ERR(ahbclk)) {
dev_err(usbpdata->dev, "Failed to get hclk_usb0\n");
return;
}
phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy0");
if (IS_ERR(phyclk)) {
dev_err(usbpdata->dev, "Failed to get clk_usbphy0\n");
return;
}
usbpdata->phyclk = phyclk;
usbpdata->ahbclk = ahbclk;
}
static void usb20otg_clock_enable(void *pdata, int enable)
{
struct dwc_otg_platform_data *usbpdata = pdata;
if (enable) {
clk_prepare_enable(usbpdata->ahbclk);
clk_prepare_enable(usbpdata->phyclk);
} else {
clk_disable_unprepare(usbpdata->ahbclk);
clk_disable_unprepare(usbpdata->phyclk);
}
}
static int usb20otg_get_status(int id)
{
int ret = -1;
u32 soc_status15 = uoc_read(0x4bc);
switch (id) {
case USB_STATUS_BVABLID:
/* bvalid in grf */
ret = soc_status15 & (0x1 << 23);
break;
case USB_STATUS_DPDM:
/* dpdm in grf */
ret = soc_status15 & (0x3 << 24);
break;
case USB_STATUS_ID:
/* id in grf */
ret = soc_status15 & (0x1 << 26);
break;
case USB_CHIP_ID:
ret = control_usb->chip_id;
break;
case USB_REMOTE_WAKEUP:
ret = control_usb->remote_wakeup;
break;
case USB_IRQ_WAKEUP:
ret = control_usb->usb_irq_wakeup;
break;
default:
break;
}
return ret;
}
#ifdef CONFIG_RK_USB_UART
/**
* dwc_otg_uart_enabled - check if a usb-uart bypass func is enabled in DT
*
* Returns true if the status property of node "usb_uart" is set to "okay"
* or "ok", if this property is absent it will use the default status "ok"
* 0 otherwise
*/
static bool dwc_otg_uart_enabled(void)
{
return false;
}
static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
{
}
#else
static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
{
}
#endif
static void usb20otg_power_enable(int enable)
{
if (0 == enable) {
/* disable otg_drv power */
if (gpio_is_valid(control_usb->otg_gpios->gpio))
gpio_set_value(control_usb->otg_gpios->gpio, 0);
} else if (1 == enable) {
/* enable otg_drv power */
if (gpio_is_valid(control_usb->otg_gpios->gpio))
gpio_set_value(control_usb->otg_gpios->gpio, 1);
}
}
struct dwc_otg_platform_data usb20otg_pdata_rk3368 = {
.phyclk = NULL,
.ahbclk = NULL,
.busclk = NULL,
.phy_status = 0,
.hw_init = usb20otg_hw_init,
.phy_suspend = usb20otg_phy_suspend,
.soft_reset = usb20otg_soft_reset,
.clock_init = usb20otg_clock_init,
.clock_enable = usb20otg_clock_enable,
.get_status = usb20otg_get_status,
.power_enable = usb20otg_power_enable,
.dwc_otg_uart_mode = dwc_otg_uart_mode,
/* .bc_detect_cb = rk_battery_charger_detect_cb, */
};
#endif
#ifdef CONFIG_USB_EHCI_RK
static void usb20ehci_hw_init(void)
{
/* Turn off differential receiver in suspend mode */
uoc_write(UOC_HIWORD_UPDATE(0, 1, 2), 0x7b8);
/* Set disconnect detection trigger point to 600mv */
uoc_write(UOC_HIWORD_UPDATE(1, 0xf, 11), 0x7bc);
/* other haredware init,include:
* DRV_VBUS GPIO init */
if (gpio_is_valid(control_usb->host_gpios->gpio)) {
if (!gpio_get_value(control_usb->host_gpios->gpio))
gpio_set_value(control_usb->host_gpios->gpio, 1);
}
}
static void usb20ehci_phy_suspend(void *pdata, int suspend)
{
struct rkehci_platform_data *usbpdata = pdata;
if (suspend) {
/* enable soft control */
uoc_write(UOC_HIWORD_UPDATE(0x1d5, 0x1ff, 0), 0x728);
usbpdata->phy_status = 1;
} else {
/* exit suspend */
uoc_write(UOC_HIWORD_UPDATE(0x0, 0x1, 0), 0x728);
usbpdata->phy_status = 0;
}
}
static void usb20ehci_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
{
struct rkehci_platform_data *usbpdata = pdata;
struct reset_control *rst_host_h, *rst_host_p, *rst_host_c;
rst_host_h = devm_reset_control_get(usbpdata->dev, "host_ahb");
rst_host_p = devm_reset_control_get(usbpdata->dev, "host_phy");
rst_host_c = devm_reset_control_get(usbpdata->dev, "host_controller");
if (IS_ERR(rst_host_h) || IS_ERR(rst_host_p) || IS_ERR(rst_host_c)) {
dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
return;
}
switch (rst_type) {
case RST_POR:
/* PHY reset */
uoc_write(UOC_HIWORD_UPDATE(0x1, 0x3, 0), 0x728);
reset_control_assert(rst_host_p);
udelay(15);
uoc_write(UOC_HIWORD_UPDATE(0x2, 0x3, 0), 0x728);
udelay(1500);
reset_control_deassert(rst_host_p);
/* Controller reset */
reset_control_assert(rst_host_c);
reset_control_assert(rst_host_h);
udelay(5);
reset_control_deassert(rst_host_c);
reset_control_deassert(rst_host_h);
break;
default:
break;
}
}
static void usb20ehci_clock_init(void *pdata)
{
}
static void usb20ehci_clock_enable(void *pdata, int enable)
{
}
static int usb20ehci_get_status(int id)
{
/* For HOST port in rk336x can not get any info from GRF */
return -ENOENT;
}
struct rkehci_platform_data usb20ehci_pdata_rk3368 = {
.phyclk = NULL,
.ahbclk = NULL,
.phy_status = 0,
.hw_init = usb20ehci_hw_init,
.phy_suspend = usb20ehci_phy_suspend,
.soft_reset = usb20ehci_soft_reset,
.clock_init = usb20ehci_clock_init,
.clock_enable = usb20ehci_clock_enable,
.get_status = usb20ehci_get_status,
};
#endif
struct dwc_otg_platform_data usb20ohci_pdata_rk3368;
#ifdef CONFIG_OF
static const struct of_device_id rk_usb_control_id_table[] = {
{
.compatible = "rockchip,rk3368-usb-control",
},
{},
};
#endif
/*********************************************************************
rk3126 usb detections
*********************************************************************/
#define WAKE_LOCK_TIMEOUT (HZ * 10)
static inline void do_wakeup(struct work_struct *work)
{
/* wake up the system */
rk_send_wakeup_key();
}
static void usb_battery_charger_detect_work(struct work_struct *work)
{
/* rk_battery_charger_detect_cb(usb_battery_charger_detect(1)); */
}
/********** handler for bvalid irq **********/
static irqreturn_t bvalid_irq_handler(int irq, void *dev_id)
{
/* clear irq */
uoc_write(UOC_HIWORD_UPDATE(0x1, 0x1, 3), 0x690);
#ifdef CONFIG_RK_USB_UART
/* usb otg dp/dm switch to usb phy */
dwc_otg_uart_mode(NULL, PHY_USB_MODE);
#endif
if (control_usb->usb_irq_wakeup) {
wake_lock_timeout(&control_usb->usb_wakelock,
WAKE_LOCK_TIMEOUT);
schedule_delayed_work(&control_usb->usb_det_wakeup_work,
HZ / 10);
}
schedule_delayed_work(&control_usb->usb_charger_det_work, HZ / 10);
return IRQ_HANDLED;
}
/************* register usb detection irqs **************/
static int otg_irq_detect_init(struct platform_device *pdev)
{
int ret = 0;
int irq = 0;
wake_lock_init(&control_usb->usb_wakelock, WAKE_LOCK_SUSPEND,
"usb_detect");
INIT_DELAYED_WORK(&control_usb->usb_det_wakeup_work, do_wakeup);
#if 0
/*register otg_bvalid irq */
irq = platform_get_irq_byname(pdev, "otg_bvalid");
if ((irq > 0) && control_usb->usb_irq_wakeup) {
ret = request_irq(irq, bvalid_irq_handler,
0, "otg_bvalid", NULL);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
} else {
/* enable bvalid irq */
uoc_write(UOC_HIWORD_UPDATE(0x1, 0x1, 3), 0x680);
}
}
#endif
return 0;
}
/********** end of usb detections **********/
#ifdef CONFIG_OF
static const struct of_device_id dwc_otg_control_usb_id_table[] = {
{
.compatible = "rockchip,rk3368-dwc-control-usb",
},
{},
};
#endif
static int dwc_otg_control_usb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct clk *hclk_usb_peri;
struct regmap *grf;
int gpio, ret = 0;
control_usb = devm_kzalloc(dev, sizeof(*control_usb), GFP_KERNEL);
if (!control_usb) {
dev_err(&pdev->dev, "Unable to alloc memory for control usb\n");
return -ENOMEM;
}
/* Init regmap GRF */
grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
if (IS_ERR(grf)) {
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
return PTR_ERR(grf);
}
control_usb->grf = grf;
/* Init Vbus-drv GPIOs */
control_usb->host_gpios =
devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL);
if (!control_usb->host_gpios) {
dev_err(&pdev->dev, "Unable to alloc memory for host_gpios\n");
return -ENOMEM;
}
gpio = of_get_named_gpio(np, "host_drv_gpio", 0);
control_usb->host_gpios->gpio = gpio;
if (gpio_is_valid(gpio)) {
if (devm_gpio_request(&pdev->dev, gpio, "usb_host_drv")) {
dev_err(&pdev->dev,
"Failed to request GPIO%d for host_drv\n",
gpio);
return -EINVAL;
}
gpio_direction_output(control_usb->host_gpios->gpio, 1);
}
control_usb->otg_gpios =
devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL);
if (!control_usb->otg_gpios) {
dev_err(&pdev->dev, "Unable to alloc memory for otg_gpios\n");
return -ENOMEM;
}
gpio = of_get_named_gpio(np, "otg_drv_gpio", 0);
control_usb->otg_gpios->gpio = gpio;
if (gpio_is_valid(gpio)) {
if (devm_gpio_request(&pdev->dev, gpio, "usb_otg_drv")) {
dev_err(&pdev->dev,
"failed to request GPIO%d for otg_drv\n", gpio);
return -EINVAL;
}
gpio_direction_output(control_usb->otg_gpios->gpio, 0);
}
control_usb->remote_wakeup = of_property_read_bool(np,
"rockchip,remote_wakeup");
control_usb->usb_irq_wakeup = of_property_read_bool(np,
"rockchip,usb_irq_wakeup");
/* Init hclk_usb_peri */
hclk_usb_peri = devm_clk_get(&pdev->dev, "hclk_usb_peri");
if (IS_ERR(hclk_usb_peri)) {
dev_err(&pdev->dev, "Failed to get hclk_usb_peri\n");
return PTR_ERR(hclk_usb_peri);
}
control_usb->hclk_usb_peri = hclk_usb_peri;
clk_prepare_enable(hclk_usb_peri);
#ifdef CONFIG_USB20_OTG
INIT_DELAYED_WORK(&control_usb->usb_charger_det_work,
usb_battery_charger_detect_work);
if (usb20otg_get_status(USB_STATUS_BVABLID))
schedule_delayed_work(&control_usb->usb_charger_det_work,
HZ / 10);
#endif
ret = otg_irq_detect_init(pdev);
if (ret < 0)
goto err;
return 0;
err:
clk_disable_unprepare(hclk_usb_peri);
return ret;
}
static int dwc_otg_control_usb_remove(struct platform_device *pdev)
{
clk_disable_unprepare(control_usb->hclk_usb_peri);
return 0;
}
static struct platform_driver dwc_otg_control_usb_driver = {
.probe = dwc_otg_control_usb_probe,
.remove = dwc_otg_control_usb_remove,
.driver = {
.name = "rk3368-dwc-control-usb",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(dwc_otg_control_usb_id_table),
},
};
static int __init dwc_otg_control_usb_init(void)
{
int retval = 0;
retval |= platform_driver_register(&dwc_otg_control_usb_driver);
return retval;
}
subsys_initcall(dwc_otg_control_usb_init);
static void __exit dwc_otg_control_usb_exit(void)
{
platform_driver_unregister(&dwc_otg_control_usb_driver);
}
module_exit(dwc_otg_control_usb_exit);
MODULE_ALIAS("platform: dwc_control_usb");
MODULE_AUTHOR("RockChip Inc.");
MODULE_DESCRIPTION("RockChip Control Module USB Driver");
MODULE_LICENSE("GPL v2");

View file

@ -253,6 +253,10 @@ static struct of_device_id rk_ehci_of_match[] = {
.compatible = "rockchip,rk3126_ehci",
.data = &usb20ehci_pdata_rk3126,
},
{
.compatible = "rockchip,rk3368_ehci",
.data = &usb20ehci_pdata_rk3368,
},
{},
};

View file

@ -93,6 +93,10 @@ static struct of_device_id rk_ohci_of_match[] = {
.compatible = "rockchip,rk3126_ohci",
.data = &usb20ohci_pdata_rk3126,
},
{
.compatible = "rockchip,rk3368_ohci",
.data = &usb20ohci_pdata_rk3368,
},
{},
};
@ -120,7 +124,7 @@ static int ohci_hcd_rk_probe(struct platform_device *pdev)
return -ENODEV;
match = of_match_device(of_match_ptr(rk_ohci_of_match), &pdev->dev);
if (match) {
if (match && match->data) {
pldata = (struct dwc_otg_platform_data *)match->data;
} else {
dev_err(dev, "ohci_rk match failed\n");
@ -145,6 +149,11 @@ static int ohci_hcd_rk_probe(struct platform_device *pdev)
goto clk_disable;
}
if (!pdev->dev.dma_mask)
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "UHH OHCI get resource failed\n");