| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * am35x.c - TI's AM35x platform specific usb wrapper functions. |
| * |
| * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> |
| * |
| * Based on drivers/usb/musb/da8xx.c |
| * |
| * Copyright (c) 2010 Texas Instruments Incorporated |
| */ |
| |
| #include <linux/delay.h> |
| |
| #include "am35x.h" |
| |
| /* MUSB platform configuration */ |
| struct musb_config musb_cfg = { |
| .regs = (struct musb_regs *)AM35X_USB_OTG_CORE_BASE, |
| .timeout = AM35X_USB_OTG_TIMEOUT, |
| .musb_speed = 0, |
| }; |
| |
| /* |
| * Enable the USB phy |
| */ |
| static u8 phy_on(void) |
| { |
| u32 devconf2; |
| u32 timeout; |
| |
| devconf2 = readl(&am35x_scm_general_regs->devconf2); |
| |
| devconf2 &= ~(DEVCONF2_RESET | DEVCONF2_PHYPWRDN | DEVCONF2_OTGPWRDN | |
| DEVCONF2_OTGMODE | DEVCONF2_REFFREQ | |
| DEVCONF2_PHY_GPIOMODE); |
| devconf2 |= DEVCONF2_SESENDEN | DEVCONF2_VBDTCTEN | DEVCONF2_PHY_PLLON | |
| DEVCONF2_REFFREQ_13MHZ | DEVCONF2_DATPOL; |
| |
| writel(devconf2, &am35x_scm_general_regs->devconf2); |
| |
| /* wait until the USB phy is turned on */ |
| timeout = musb_cfg.timeout; |
| while (timeout--) |
| if (readl(&am35x_scm_general_regs->devconf2) & DEVCONF2_PHYCKGD) |
| return 1; |
| |
| /* USB phy was not turned on */ |
| return 0; |
| } |
| |
| /* |
| * Disable the USB phy |
| */ |
| static void phy_off(void) |
| { |
| u32 devconf2; |
| |
| /* |
| * Power down the on-chip PHY. |
| */ |
| devconf2 = readl(&am35x_scm_general_regs->devconf2); |
| |
| devconf2 &= ~DEVCONF2_PHY_PLLON; |
| devconf2 |= DEVCONF2_PHYPWRDN | DEVCONF2_OTGPWRDN; |
| writel(devconf2, &am35x_scm_general_regs->devconf2); |
| } |
| |
| /* |
| * This function performs platform specific initialization for usb0. |
| */ |
| int musb_platform_init(void) |
| { |
| u32 revision; |
| u32 sw_reset; |
| |
| /* global usb reset */ |
| sw_reset = readl(&am35x_scm_general_regs->ip_sw_reset); |
| sw_reset |= (1 << 0); |
| writel(sw_reset, &am35x_scm_general_regs->ip_sw_reset); |
| sw_reset &= ~(1 << 0); |
| writel(sw_reset, &am35x_scm_general_regs->ip_sw_reset); |
| |
| /* reset the controller */ |
| writel(0x1, &am35x_usb_regs->control); |
| udelay(5000); |
| |
| /* start the on-chip usb phy and its pll */ |
| if (phy_on() == 0) |
| return -1; |
| |
| /* Returns zero if e.g. not clocked */ |
| revision = readl(&am35x_usb_regs->revision); |
| if (revision == 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* |
| * This function performs platform specific deinitialization for usb0. |
| */ |
| void musb_platform_deinit(void) |
| { |
| /* Turn off the phy */ |
| phy_off(); |
| } |
| |
| /* |
| * This function reads data from endpoint fifo for AM35x |
| * which supports only 32bit read operation. |
| * |
| * ep - endpoint number |
| * length - number of bytes to read from FIFO |
| * fifo_data - pointer to data buffer into which data is read |
| */ |
| __attribute__((weak)) |
| void read_fifo(u8 ep, u32 length, void *fifo_data) |
| { |
| u8 *data = (u8 *)fifo_data; |
| u32 val; |
| int i; |
| |
| /* select the endpoint index */ |
| writeb(ep, &musbr->index); |
| |
| if (length > 4) { |
| for (i = 0; i < (length >> 2); i++) { |
| val = readl(&musbr->fifox[ep]); |
| memcpy(data, &val, 4); |
| data += 4; |
| } |
| length %= 4; |
| } |
| if (length > 0) { |
| val = readl(&musbr->fifox[ep]); |
| memcpy(data, &val, length); |
| } |
| } |