18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Texas Instruments AM35x "glue layer" 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2010, by Texas Instruments 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on the DA8xx "glue layer" code. 98c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009, MontaVista Software, Inc. <source@mvista.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is part of the Inventra Controller Driver for Linux. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/usb/usb_phy_generic.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_data/usb-omap.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "musb_core.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * AM35x specific definitions 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci/* USB 2.0 OTG module registers */ 298c2ecf20Sopenharmony_ci#define USB_REVISION_REG 0x00 308c2ecf20Sopenharmony_ci#define USB_CTRL_REG 0x04 318c2ecf20Sopenharmony_ci#define USB_STAT_REG 0x08 328c2ecf20Sopenharmony_ci#define USB_EMULATION_REG 0x0c 338c2ecf20Sopenharmony_ci/* 0x10 Reserved */ 348c2ecf20Sopenharmony_ci#define USB_AUTOREQ_REG 0x14 358c2ecf20Sopenharmony_ci#define USB_SRP_FIX_TIME_REG 0x18 368c2ecf20Sopenharmony_ci#define USB_TEARDOWN_REG 0x1c 378c2ecf20Sopenharmony_ci#define EP_INTR_SRC_REG 0x20 388c2ecf20Sopenharmony_ci#define EP_INTR_SRC_SET_REG 0x24 398c2ecf20Sopenharmony_ci#define EP_INTR_SRC_CLEAR_REG 0x28 408c2ecf20Sopenharmony_ci#define EP_INTR_MASK_REG 0x2c 418c2ecf20Sopenharmony_ci#define EP_INTR_MASK_SET_REG 0x30 428c2ecf20Sopenharmony_ci#define EP_INTR_MASK_CLEAR_REG 0x34 438c2ecf20Sopenharmony_ci#define EP_INTR_SRC_MASKED_REG 0x38 448c2ecf20Sopenharmony_ci#define CORE_INTR_SRC_REG 0x40 458c2ecf20Sopenharmony_ci#define CORE_INTR_SRC_SET_REG 0x44 468c2ecf20Sopenharmony_ci#define CORE_INTR_SRC_CLEAR_REG 0x48 478c2ecf20Sopenharmony_ci#define CORE_INTR_MASK_REG 0x4c 488c2ecf20Sopenharmony_ci#define CORE_INTR_MASK_SET_REG 0x50 498c2ecf20Sopenharmony_ci#define CORE_INTR_MASK_CLEAR_REG 0x54 508c2ecf20Sopenharmony_ci#define CORE_INTR_SRC_MASKED_REG 0x58 518c2ecf20Sopenharmony_ci/* 0x5c Reserved */ 528c2ecf20Sopenharmony_ci#define USB_END_OF_INTR_REG 0x60 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Control register bits */ 558c2ecf20Sopenharmony_ci#define AM35X_SOFT_RESET_MASK 1 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* USB interrupt register bits */ 588c2ecf20Sopenharmony_ci#define AM35X_INTR_USB_SHIFT 16 598c2ecf20Sopenharmony_ci#define AM35X_INTR_USB_MASK (0x1ff << AM35X_INTR_USB_SHIFT) 608c2ecf20Sopenharmony_ci#define AM35X_INTR_DRVVBUS 0x100 618c2ecf20Sopenharmony_ci#define AM35X_INTR_RX_SHIFT 16 628c2ecf20Sopenharmony_ci#define AM35X_INTR_TX_SHIFT 0 638c2ecf20Sopenharmony_ci#define AM35X_TX_EP_MASK 0xffff /* EP0 + 15 Tx EPs */ 648c2ecf20Sopenharmony_ci#define AM35X_RX_EP_MASK 0xfffe /* 15 Rx EPs */ 658c2ecf20Sopenharmony_ci#define AM35X_TX_INTR_MASK (AM35X_TX_EP_MASK << AM35X_INTR_TX_SHIFT) 668c2ecf20Sopenharmony_ci#define AM35X_RX_INTR_MASK (AM35X_RX_EP_MASK << AM35X_INTR_RX_SHIFT) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define USB_MENTOR_CORE_OFFSET 0x400 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct am35x_glue { 718c2ecf20Sopenharmony_ci struct device *dev; 728c2ecf20Sopenharmony_ci struct platform_device *musb; 738c2ecf20Sopenharmony_ci struct platform_device *phy; 748c2ecf20Sopenharmony_ci struct clk *phy_clk; 758c2ecf20Sopenharmony_ci struct clk *clk; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * am35x_musb_enable - enable interrupts 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic void am35x_musb_enable(struct musb *musb) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci void __iomem *reg_base = musb->ctrl_base; 848c2ecf20Sopenharmony_ci u32 epmask; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Workaround: setup IRQs through both register sets. */ 878c2ecf20Sopenharmony_ci epmask = ((musb->epmask & AM35X_TX_EP_MASK) << AM35X_INTR_TX_SHIFT) | 888c2ecf20Sopenharmony_ci ((musb->epmask & AM35X_RX_EP_MASK) << AM35X_INTR_RX_SHIFT); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci musb_writel(reg_base, EP_INTR_MASK_SET_REG, epmask); 918c2ecf20Sopenharmony_ci musb_writel(reg_base, CORE_INTR_MASK_SET_REG, AM35X_INTR_USB_MASK); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Force the DRVVBUS IRQ so we can start polling for ID change. */ 948c2ecf20Sopenharmony_ci musb_writel(reg_base, CORE_INTR_SRC_SET_REG, 958c2ecf20Sopenharmony_ci AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * am35x_musb_disable - disable HDRC and flush interrupts 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic void am35x_musb_disable(struct musb *musb) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci void __iomem *reg_base = musb->ctrl_base; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci musb_writel(reg_base, CORE_INTR_MASK_CLEAR_REG, AM35X_INTR_USB_MASK); 1068c2ecf20Sopenharmony_ci musb_writel(reg_base, EP_INTR_MASK_CLEAR_REG, 1078c2ecf20Sopenharmony_ci AM35X_TX_INTR_MASK | AM35X_RX_INTR_MASK); 1088c2ecf20Sopenharmony_ci musb_writel(reg_base, USB_END_OF_INTR_REG, 0); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define portstate(stmt) stmt 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void am35x_musb_set_vbus(struct musb *musb, int is_on) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci WARN_ON(is_on && is_peripheral_active(musb)); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define POLL_SECONDS 2 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void otg_timer(struct timer_list *t) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct musb *musb = from_timer(musb, t, dev_timer); 1238c2ecf20Sopenharmony_ci void __iomem *mregs = musb->mregs; 1248c2ecf20Sopenharmony_ci u8 devctl; 1258c2ecf20Sopenharmony_ci unsigned long flags; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * We poll because AM35x's won't expose several OTG-critical 1298c2ecf20Sopenharmony_ci * status change events (from the transceiver) otherwise. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci devctl = musb_readb(mregs, MUSB_DEVCTL); 1328c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, 1338c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state)); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 1368c2ecf20Sopenharmony_ci switch (musb->xceiv->otg->state) { 1378c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 1388c2ecf20Sopenharmony_ci devctl &= ~MUSB_DEVCTL_SESSION; 1398c2ecf20Sopenharmony_ci musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 1428c2ecf20Sopenharmony_ci if (devctl & MUSB_DEVCTL_BDEVICE) { 1438c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_B_IDLE; 1448c2ecf20Sopenharmony_ci MUSB_DEV_MODE(musb); 1458c2ecf20Sopenharmony_ci } else { 1468c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_IDLE; 1478c2ecf20Sopenharmony_ci MUSB_HST_MODE(musb); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_VFALL: 1518c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; 1528c2ecf20Sopenharmony_ci musb_writel(musb->ctrl_base, CORE_INTR_SRC_SET_REG, 1538c2ecf20Sopenharmony_ci MUSB_INTR_VBUSERROR << AM35X_INTR_USB_SHIFT); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci case OTG_STATE_B_IDLE: 1568c2ecf20Sopenharmony_ci devctl = musb_readb(mregs, MUSB_DEVCTL); 1578c2ecf20Sopenharmony_ci if (devctl & MUSB_DEVCTL_BDEVICE) 1588c2ecf20Sopenharmony_ci mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_IDLE; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci default: 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci static unsigned long last_timer; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (timeout == 0) 1738c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(3); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Never idle if active, or when VBUS timeout is not set as host */ 1768c2ecf20Sopenharmony_ci if (musb->is_active || (musb->a_wait_bcon == 0 && 1778c2ecf20Sopenharmony_ci musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) { 1788c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%s active, deleting timer\n", 1798c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state)); 1808c2ecf20Sopenharmony_ci del_timer(&musb->dev_timer); 1818c2ecf20Sopenharmony_ci last_timer = jiffies; 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) { 1868c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); 1878c2ecf20Sopenharmony_ci return; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci last_timer = timeout; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", 1928c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), 1938c2ecf20Sopenharmony_ci jiffies_to_msecs(timeout - jiffies)); 1948c2ecf20Sopenharmony_ci mod_timer(&musb->dev_timer, timeout); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic irqreturn_t am35x_musb_interrupt(int irq, void *hci) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct musb *musb = hci; 2008c2ecf20Sopenharmony_ci void __iomem *reg_base = musb->ctrl_base; 2018c2ecf20Sopenharmony_ci struct device *dev = musb->controller; 2028c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); 2038c2ecf20Sopenharmony_ci struct omap_musb_board_data *data = plat->board_data; 2048c2ecf20Sopenharmony_ci unsigned long flags; 2058c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 2068c2ecf20Sopenharmony_ci u32 epintr, usbintr; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Get endpoint interrupts */ 2118c2ecf20Sopenharmony_ci epintr = musb_readl(reg_base, EP_INTR_SRC_MASKED_REG); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (epintr) { 2148c2ecf20Sopenharmony_ci musb_writel(reg_base, EP_INTR_SRC_CLEAR_REG, epintr); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci musb->int_rx = 2178c2ecf20Sopenharmony_ci (epintr & AM35X_RX_INTR_MASK) >> AM35X_INTR_RX_SHIFT; 2188c2ecf20Sopenharmony_ci musb->int_tx = 2198c2ecf20Sopenharmony_ci (epintr & AM35X_TX_INTR_MASK) >> AM35X_INTR_TX_SHIFT; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Get usb core interrupts */ 2238c2ecf20Sopenharmony_ci usbintr = musb_readl(reg_base, CORE_INTR_SRC_MASKED_REG); 2248c2ecf20Sopenharmony_ci if (!usbintr && !epintr) 2258c2ecf20Sopenharmony_ci goto eoi; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (usbintr) { 2288c2ecf20Sopenharmony_ci musb_writel(reg_base, CORE_INTR_SRC_CLEAR_REG, usbintr); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci musb->int_usb = 2318c2ecf20Sopenharmony_ci (usbintr & AM35X_INTR_USB_MASK) >> AM35X_INTR_USB_SHIFT; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * DRVVBUS IRQs are the only proxy we have (a very poor one!) for 2358c2ecf20Sopenharmony_ci * AM35x's missing ID change IRQ. We need an ID change IRQ to 2368c2ecf20Sopenharmony_ci * switch appropriately between halves of the OTG state machine. 2378c2ecf20Sopenharmony_ci * Managing DEVCTL.SESSION per Mentor docs requires that we know its 2388c2ecf20Sopenharmony_ci * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. 2398c2ecf20Sopenharmony_ci * Also, DRVVBUS pulses for SRP (but not at 5V) ... 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci if (usbintr & (AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT)) { 2428c2ecf20Sopenharmony_ci int drvvbus = musb_readl(reg_base, USB_STAT_REG); 2438c2ecf20Sopenharmony_ci void __iomem *mregs = musb->mregs; 2448c2ecf20Sopenharmony_ci u8 devctl = musb_readb(mregs, MUSB_DEVCTL); 2458c2ecf20Sopenharmony_ci int err; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci err = musb->int_usb & MUSB_INTR_VBUSERROR; 2488c2ecf20Sopenharmony_ci if (err) { 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * The Mentor core doesn't debounce VBUS as needed 2518c2ecf20Sopenharmony_ci * to cope with device connect current spikes. This 2528c2ecf20Sopenharmony_ci * means it's not uncommon for bus-powered devices 2538c2ecf20Sopenharmony_ci * to get VBUS errors during enumeration. 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * This is a workaround, but newer RTL from Mentor 2568c2ecf20Sopenharmony_ci * seems to allow a better one: "re"-starting sessions 2578c2ecf20Sopenharmony_ci * without waiting for VBUS to stop registering in 2588c2ecf20Sopenharmony_ci * devctl. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci musb->int_usb &= ~MUSB_INTR_VBUSERROR; 2618c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; 2628c2ecf20Sopenharmony_ci mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 2638c2ecf20Sopenharmony_ci WARNING("VBUS error workaround (delay coming)\n"); 2648c2ecf20Sopenharmony_ci } else if (drvvbus) { 2658c2ecf20Sopenharmony_ci MUSB_HST_MODE(musb); 2668c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; 2678c2ecf20Sopenharmony_ci portstate(musb->port1_status |= USB_PORT_STAT_POWER); 2688c2ecf20Sopenharmony_ci del_timer(&musb->dev_timer); 2698c2ecf20Sopenharmony_ci } else { 2708c2ecf20Sopenharmony_ci musb->is_active = 0; 2718c2ecf20Sopenharmony_ci MUSB_DEV_MODE(musb); 2728c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_B_IDLE; 2738c2ecf20Sopenharmony_ci portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* NOTE: this must complete power-on within 100 ms. */ 2778c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", 2788c2ecf20Sopenharmony_ci drvvbus ? "on" : "off", 2798c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), 2808c2ecf20Sopenharmony_ci err ? " ERROR" : "", 2818c2ecf20Sopenharmony_ci devctl); 2828c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Drop spurious RX and TX if device is disconnected */ 2868c2ecf20Sopenharmony_ci if (musb->int_usb & MUSB_INTR_DISCONNECT) { 2878c2ecf20Sopenharmony_ci musb->int_tx = 0; 2888c2ecf20Sopenharmony_ci musb->int_rx = 0; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (musb->int_tx || musb->int_rx || musb->int_usb) 2928c2ecf20Sopenharmony_ci ret |= musb_interrupt(musb); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cieoi: 2958c2ecf20Sopenharmony_ci /* EOI needs to be written for the IRQ to be re-asserted. */ 2968c2ecf20Sopenharmony_ci if (ret == IRQ_HANDLED || epintr || usbintr) { 2978c2ecf20Sopenharmony_ci /* clear level interrupt */ 2988c2ecf20Sopenharmony_ci if (data->clear_irq) 2998c2ecf20Sopenharmony_ci data->clear_irq(); 3008c2ecf20Sopenharmony_ci /* write EOI */ 3018c2ecf20Sopenharmony_ci musb_writel(reg_base, USB_END_OF_INTR_REG, 0); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Poll for ID change */ 3058c2ecf20Sopenharmony_ci if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) 3068c2ecf20Sopenharmony_ci mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return ret; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int am35x_musb_set_mode(struct musb *musb, u8 musb_mode) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct device *dev = musb->controller; 3168c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); 3178c2ecf20Sopenharmony_ci struct omap_musb_board_data *data = plat->board_data; 3188c2ecf20Sopenharmony_ci int retval = 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (data->set_mode) 3218c2ecf20Sopenharmony_ci data->set_mode(musb_mode); 3228c2ecf20Sopenharmony_ci else 3238c2ecf20Sopenharmony_ci retval = -EIO; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return retval; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int am35x_musb_init(struct musb *musb) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct device *dev = musb->controller; 3318c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); 3328c2ecf20Sopenharmony_ci struct omap_musb_board_data *data = plat->board_data; 3338c2ecf20Sopenharmony_ci void __iomem *reg_base = musb->ctrl_base; 3348c2ecf20Sopenharmony_ci u32 rev; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci musb->mregs += USB_MENTOR_CORE_OFFSET; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Returns zero if e.g. not clocked */ 3398c2ecf20Sopenharmony_ci rev = musb_readl(reg_base, USB_REVISION_REG); 3408c2ecf20Sopenharmony_ci if (!rev) 3418c2ecf20Sopenharmony_ci return -ENODEV; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); 3448c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(musb->xceiv)) 3458c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci timer_setup(&musb->dev_timer, otg_timer, 0); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Reset the musb */ 3508c2ecf20Sopenharmony_ci if (data->reset) 3518c2ecf20Sopenharmony_ci data->reset(); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Reset the controller */ 3548c2ecf20Sopenharmony_ci musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Start the on-chip PHY and its PLL. */ 3578c2ecf20Sopenharmony_ci if (data->set_phy_power) 3588c2ecf20Sopenharmony_ci data->set_phy_power(1); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci msleep(5); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci musb->isr = am35x_musb_interrupt; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* clear level interrupt */ 3658c2ecf20Sopenharmony_ci if (data->clear_irq) 3668c2ecf20Sopenharmony_ci data->clear_irq(); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int am35x_musb_exit(struct musb *musb) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct device *dev = musb->controller; 3748c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); 3758c2ecf20Sopenharmony_ci struct omap_musb_board_data *data = plat->board_data; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci del_timer_sync(&musb->dev_timer); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Shutdown the on-chip PHY and its PLL. */ 3808c2ecf20Sopenharmony_ci if (data->set_phy_power) 3818c2ecf20Sopenharmony_ci data->set_phy_power(0); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci usb_put_phy(musb->xceiv); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/* AM35x supports only 32bit read operation */ 3898c2ecf20Sopenharmony_cistatic void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci void __iomem *fifo = hw_ep->fifo; 3928c2ecf20Sopenharmony_ci u32 val; 3938c2ecf20Sopenharmony_ci int i; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Read for 32bit-aligned destination address */ 3968c2ecf20Sopenharmony_ci if (likely((0x03 & (unsigned long) dst) == 0) && len >= 4) { 3978c2ecf20Sopenharmony_ci readsl(fifo, dst, len >> 2); 3988c2ecf20Sopenharmony_ci dst += len & ~0x03; 3998c2ecf20Sopenharmony_ci len &= 0x03; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * Now read the remaining 1 to 3 byte or complete length if 4038c2ecf20Sopenharmony_ci * unaligned address. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci if (len > 4) { 4068c2ecf20Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 4078c2ecf20Sopenharmony_ci *(u32 *) dst = musb_readl(fifo, 0); 4088c2ecf20Sopenharmony_ci dst += 4; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci len &= 0x03; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci if (len > 0) { 4138c2ecf20Sopenharmony_ci val = musb_readl(fifo, 0); 4148c2ecf20Sopenharmony_ci memcpy(dst, &val, len); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic const struct musb_platform_ops am35x_ops = { 4198c2ecf20Sopenharmony_ci .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, 4208c2ecf20Sopenharmony_ci .init = am35x_musb_init, 4218c2ecf20Sopenharmony_ci .exit = am35x_musb_exit, 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci .read_fifo = am35x_read_fifo, 4248c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_INVENTRA_DMA 4258c2ecf20Sopenharmony_ci .dma_init = musbhs_dma_controller_create, 4268c2ecf20Sopenharmony_ci .dma_exit = musbhs_dma_controller_destroy, 4278c2ecf20Sopenharmony_ci#endif 4288c2ecf20Sopenharmony_ci .enable = am35x_musb_enable, 4298c2ecf20Sopenharmony_ci .disable = am35x_musb_disable, 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci .set_mode = am35x_musb_set_mode, 4328c2ecf20Sopenharmony_ci .try_idle = am35x_musb_try_idle, 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci .set_vbus = am35x_musb_set_vbus, 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic const struct platform_device_info am35x_dev_info = { 4388c2ecf20Sopenharmony_ci .name = "musb-hdrc", 4398c2ecf20Sopenharmony_ci .id = PLATFORM_DEVID_AUTO, 4408c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int am35x_probe(struct platform_device *pdev) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); 4468c2ecf20Sopenharmony_ci struct platform_device *musb; 4478c2ecf20Sopenharmony_ci struct am35x_glue *glue; 4488c2ecf20Sopenharmony_ci struct platform_device_info pinfo; 4498c2ecf20Sopenharmony_ci struct clk *phy_clk; 4508c2ecf20Sopenharmony_ci struct clk *clk; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci int ret = -ENOMEM; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci glue = kzalloc(sizeof(*glue), GFP_KERNEL); 4558c2ecf20Sopenharmony_ci if (!glue) 4568c2ecf20Sopenharmony_ci goto err0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci phy_clk = clk_get(&pdev->dev, "fck"); 4598c2ecf20Sopenharmony_ci if (IS_ERR(phy_clk)) { 4608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get PHY clock\n"); 4618c2ecf20Sopenharmony_ci ret = PTR_ERR(phy_clk); 4628c2ecf20Sopenharmony_ci goto err3; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci clk = clk_get(&pdev->dev, "ick"); 4668c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 4678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock\n"); 4688c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 4698c2ecf20Sopenharmony_ci goto err4; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ret = clk_enable(phy_clk); 4738c2ecf20Sopenharmony_ci if (ret) { 4748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable PHY clock\n"); 4758c2ecf20Sopenharmony_ci goto err5; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ret = clk_enable(clk); 4798c2ecf20Sopenharmony_ci if (ret) { 4808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock\n"); 4818c2ecf20Sopenharmony_ci goto err6; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci glue->dev = &pdev->dev; 4858c2ecf20Sopenharmony_ci glue->phy_clk = phy_clk; 4868c2ecf20Sopenharmony_ci glue->clk = clk; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci pdata->platform_ops = &am35x_ops; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci glue->phy = usb_phy_generic_register(); 4918c2ecf20Sopenharmony_ci if (IS_ERR(glue->phy)) { 4928c2ecf20Sopenharmony_ci ret = PTR_ERR(glue->phy); 4938c2ecf20Sopenharmony_ci goto err7; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, glue); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci pinfo = am35x_dev_info; 4988c2ecf20Sopenharmony_ci pinfo.parent = &pdev->dev; 4998c2ecf20Sopenharmony_ci pinfo.res = pdev->resource; 5008c2ecf20Sopenharmony_ci pinfo.num_res = pdev->num_resources; 5018c2ecf20Sopenharmony_ci pinfo.data = pdata; 5028c2ecf20Sopenharmony_ci pinfo.size_data = sizeof(*pdata); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci glue->musb = musb = platform_device_register_full(&pinfo); 5058c2ecf20Sopenharmony_ci if (IS_ERR(musb)) { 5068c2ecf20Sopenharmony_ci ret = PTR_ERR(musb); 5078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); 5088c2ecf20Sopenharmony_ci goto err8; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cierr8: 5148c2ecf20Sopenharmony_ci usb_phy_generic_unregister(glue->phy); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cierr7: 5178c2ecf20Sopenharmony_ci clk_disable(clk); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cierr6: 5208c2ecf20Sopenharmony_ci clk_disable(phy_clk); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cierr5: 5238c2ecf20Sopenharmony_ci clk_put(clk); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cierr4: 5268c2ecf20Sopenharmony_ci clk_put(phy_clk); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cierr3: 5298c2ecf20Sopenharmony_ci kfree(glue); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cierr0: 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int am35x_remove(struct platform_device *pdev) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct am35x_glue *glue = platform_get_drvdata(pdev); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci platform_device_unregister(glue->musb); 5408c2ecf20Sopenharmony_ci usb_phy_generic_unregister(glue->phy); 5418c2ecf20Sopenharmony_ci clk_disable(glue->clk); 5428c2ecf20Sopenharmony_ci clk_disable(glue->phy_clk); 5438c2ecf20Sopenharmony_ci clk_put(glue->clk); 5448c2ecf20Sopenharmony_ci clk_put(glue->phy_clk); 5458c2ecf20Sopenharmony_ci kfree(glue); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5518c2ecf20Sopenharmony_cistatic int am35x_suspend(struct device *dev) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct am35x_glue *glue = dev_get_drvdata(dev); 5548c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); 5558c2ecf20Sopenharmony_ci struct omap_musb_board_data *data = plat->board_data; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Shutdown the on-chip PHY and its PLL. */ 5588c2ecf20Sopenharmony_ci if (data->set_phy_power) 5598c2ecf20Sopenharmony_ci data->set_phy_power(0); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci clk_disable(glue->phy_clk); 5628c2ecf20Sopenharmony_ci clk_disable(glue->clk); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int am35x_resume(struct device *dev) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct am35x_glue *glue = dev_get_drvdata(dev); 5708c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); 5718c2ecf20Sopenharmony_ci struct omap_musb_board_data *data = plat->board_data; 5728c2ecf20Sopenharmony_ci int ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Start the on-chip PHY and its PLL. */ 5758c2ecf20Sopenharmony_ci if (data->set_phy_power) 5768c2ecf20Sopenharmony_ci data->set_phy_power(1); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci ret = clk_enable(glue->phy_clk); 5798c2ecf20Sopenharmony_ci if (ret) { 5808c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable PHY clock\n"); 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ret = clk_enable(glue->clk); 5858c2ecf20Sopenharmony_ci if (ret) { 5868c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable clock\n"); 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci#endif 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(am35x_pm_ops, am35x_suspend, am35x_resume); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic struct platform_driver am35x_driver = { 5978c2ecf20Sopenharmony_ci .probe = am35x_probe, 5988c2ecf20Sopenharmony_ci .remove = am35x_remove, 5998c2ecf20Sopenharmony_ci .driver = { 6008c2ecf20Sopenharmony_ci .name = "musb-am35x", 6018c2ecf20Sopenharmony_ci .pm = &am35x_pm_ops, 6028c2ecf20Sopenharmony_ci }, 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AM35x MUSB Glue Layer"); 6068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>"); 6078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6088c2ecf20Sopenharmony_cimodule_platform_driver(am35x_driver); 609