}
}
+static void rk_ehci_phy_suspend(void* pdata, int suspend)
+{
+ struct rkehci_platform_data *usbpdata=pdata;
+
+ if(suspend){
+ // enable soft control
+ control_usb->grf_uoc1_base->CON2 = (0x01<<2)|((0x01<<2)<<16);
+ // enter suspend
+ control_usb->grf_uoc1_base->CON3 = 0x2A|(0x3F<<16);
+ usbpdata->phy_status = 1;
+ }else{
+ // exit suspend
+ control_usb->grf_uoc1_base->CON2 = ((0x01<<2)<<16);
+ usbpdata->phy_status = 0;
+ }
+}
+
static void rk_ehci_clock_init(void* pdata)
{
struct rkehci_platform_data *usbpdata=pdata;
mdelay(2);
}
+static int rk_ehci_get_status(int id)
+{
+ int ret = -1;
+
+ switch(id){
+ case USB_STATUS_DPDM:
+ // dpdm in grf
+ ret = control_usb->grf_soc_status2_rk3288->host0_linestate;
+ 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;
+}
+
struct rkehci_platform_data rkehci_pdata_rk3288 = {
.phyclk = NULL,
.ahbclk = NULL,
.clk_status = -1,
+ .phy_status = 0,
.hw_init = rk_ehci_hw_init,
+ .phy_suspend = rk_ehci_phy_suspend,
.clock_init = rk_ehci_clock_init,
.clock_enable = rk_ehci_clock_enable,
.soft_reset = rk_ehci_soft_reset,
+ .get_status = rk_ehci_get_status,
};
#endif
static int rkehci_status = 1;
static struct ehci_hcd *g_ehci;
+struct rk_ehci_hcd {
+ struct ehci_hcd *ehci;
+ uint8_t host_enabled;
+ uint8_t host_setenable;
+ struct rkehci_platform_data *pldata;
+ struct timer_list connect_detect_timer;
+ struct delayed_work host_enable_work;
+};
#define EHCI_PRINT(x...) printk( KERN_INFO "EHCI: " x )
static struct rkehci_pdata_id rkehci_pdata[] = {
msleep(20);
}
+static void rk_ehci_hcd_enable(struct work_struct *work)
+{
+ struct rk_ehci_hcd *rk_ehci;
+ struct usb_hcd *hcd;
+ struct rkehci_platform_data *pldata;
+ struct ehci_hcd *ehci;
+
+ rk_ehci = container_of(work, struct rk_ehci_hcd, host_enable_work.work);
+ pldata = rk_ehci->pldata;
+ ehci = rk_ehci->ehci;
+ hcd = ehci_to_hcd(ehci);
+
+ if(rk_ehci->host_enabled == rk_ehci->host_setenable){
+ printk("%s, enable flag %d\n", __func__, rk_ehci->host_setenable);
+ goto out;
+ }
+
+ if(rk_ehci->host_setenable == 2){// enable -> disable
+ if(pldata->get_status(USB_STATUS_DPDM)){// usb device connected
+ rk_ehci->host_setenable = 1;
+ goto out;
+ }
+
+ printk("%s, disable host controller\n", __func__);
+ ehci_port_power(ehci, 0);
+ usb_remove_hcd(hcd);
+
+ /* reset cru and reinitialize EHCI controller */
+ pldata->soft_reset();
+ usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED);
+ if(pldata->phy_suspend)
+ pldata->phy_suspend(pldata, USB_PHY_SUSPEND);
+ /* do not disable EHCI clk, otherwise RK3288
+ * host1(DWC_OTG) can't work normally.
+ */
+ //pldata->clock_enable(pldata, 0);
+ }else if(rk_ehci->host_setenable == 1){
+ //pldata->clock_enable(pldata, 1);
+ if(pldata->phy_suspend)
+ pldata->phy_suspend(pldata, USB_PHY_ENABLED);
+ mdelay(5);
+ ehci_port_power(ehci, 1);
+ printk("%s, enable host controller\n", __func__);
+ }
+ rk_ehci->host_enabled = rk_ehci->host_setenable;
+
+out:
+ return;
+}
+
+static void rk_ehci_hcd_connect_detect(unsigned long pdata)
+{
+ struct rk_ehci_hcd *rk_ehci= (struct rk_ehci_hcd*)pdata;
+ struct ehci_hcd *ehci = rk_ehci->ehci;
+ struct rkehci_platform_data *pldata;
+ uint32_t status;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ pldata = rk_ehci->pldata;
+
+ if(pldata->get_status(USB_STATUS_DPDM)){
+ // usb device connected
+ rk_ehci->host_setenable = 1;
+ }else{
+ // no device, suspend host
+ status = readl(&ehci->regs->port_status[0]);
+ if(!(status & PORT_CONNECT)){
+ rk_ehci->host_setenable = 2;
+ }
+ }
+
+ if((rk_ehci->host_enabled) && (rk_ehci->host_setenable != rk_ehci->host_enabled)){
+ schedule_delayed_work(&rk_ehci->host_enable_work, 1);
+ }
+
+ mod_timer(&rk_ehci->connect_detect_timer,jiffies + (HZ<<1));
+
+ local_irq_restore(flags);
+ return;
+}
+
static struct hc_driver rk_ehci_hc_driver = {
.description = hcd_name,
.product_desc = "Rockchip On-Chip EHCI Host Controller",
static u64 usb_dmamask = 0xffffffffUL;
struct device_node *node = pdev->dev.of_node;
struct rkehci_pdata_id *p;
+ struct rk_ehci_hcd *rk_ehci;
const struct of_device_id *match =
of_match_device(of_match_ptr( rk_ehci_of_match ), &pdev->dev);
pldata->clock_enable(pldata, 1);
}
+ if(pldata->phy_suspend)
+ pldata->phy_suspend(pldata, USB_PHY_ENABLED);
+
if(pldata->soft_reset)
pldata->soft_reset();
}
g_ehci = ehci;
- ehci_port_power(ehci, 1);
-// writel_relaxed(0x1d4d ,hcd->regs +0x90);
-// writel_relaxed(0x4 ,hcd->regs +0xa0);
-// dsb();
+
+ rk_ehci = devm_kzalloc(&pdev->dev, sizeof(struct rk_ehci_hcd),
+ GFP_KERNEL);
+ if(!rk_ehci){
+ ret = -ENOMEM;
+ goto put_hcd;
+ }
+
+ rk_ehci->ehci = ehci;
+ rk_ehci->pldata = pldata;
+ rk_ehci->host_enabled = 2;
+ rk_ehci->host_setenable = 2;
+ rk_ehci->connect_detect_timer.function = rk_ehci_hcd_connect_detect;
+ rk_ehci->connect_detect_timer.data = (unsigned long)(rk_ehci);
+ init_timer( &rk_ehci->connect_detect_timer );
+ mod_timer( &rk_ehci->connect_detect_timer, jiffies+(HZ<<3) );
+ INIT_DELAYED_WORK( &rk_ehci->host_enable_work, rk_ehci_hcd_enable );
+
+ ehci_port_power(ehci, 0);
+
+ if(pldata->phy_suspend){
+ if( pldata->phy_status == USB_PHY_ENABLED ){
+ pldata->phy_suspend(pldata, USB_PHY_SUSPEND);
+ /* do not disable EHCI clk, otherwise RK3288
+ * host1(DWC_OTG) can't work normally.
+ * udelay(3);
+ * pldata->clock_enable(pldata, 0);
+ */
+ }
+ }
printk("%s ok\n", __func__);