18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TUSB6010 USB 2.0 OTG Dual Role controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Nokia Corporation
68c2ecf20Sopenharmony_ci * Tony Lindgren <tony@atomide.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Notes:
98c2ecf20Sopenharmony_ci * - Driver assumes that interface to external host (main CPU) is
108c2ecf20Sopenharmony_ci *   configured for NOR FLASH interface instead of VLYNQ serial
118c2ecf20Sopenharmony_ci *   interface.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/err.h>
188c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
198c2ecf20Sopenharmony_ci#include <linux/usb.h>
208c2ecf20Sopenharmony_ci#include <linux/irq.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/device.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
258c2ecf20Sopenharmony_ci#include <linux/usb/usb_phy_generic.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "musb_core.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct tusb6010_glue {
308c2ecf20Sopenharmony_ci	struct device		*dev;
318c2ecf20Sopenharmony_ci	struct platform_device	*musb;
328c2ecf20Sopenharmony_ci	struct platform_device	*phy;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define TUSB_REV_MAJOR(reg_val)		((reg_val >> 4) & 0xf)
388c2ecf20Sopenharmony_ci#define TUSB_REV_MINOR(reg_val)		(reg_val & 0xf)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Checks the revision. We need to use the DMA register as 3.0 does not
428c2ecf20Sopenharmony_ci * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_cistatic u8 tusb_get_revision(struct musb *musb)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
478c2ecf20Sopenharmony_ci	u32		die_id;
488c2ecf20Sopenharmony_ci	u8		rev;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	rev = musb_readl(tbase, TUSB_DMA_CTRL_REV) & 0xff;
518c2ecf20Sopenharmony_ci	if (TUSB_REV_MAJOR(rev) == 3) {
528c2ecf20Sopenharmony_ci		die_id = TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase,
538c2ecf20Sopenharmony_ci				TUSB_DIDR1_HI));
548c2ecf20Sopenharmony_ci		if (die_id >= TUSB_DIDR1_HI_REV_31)
558c2ecf20Sopenharmony_ci			rev |= 1;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return rev;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void tusb_print_revision(struct musb *musb)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
648c2ecf20Sopenharmony_ci	u8		rev;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	rev = musb->tusb_revision;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	pr_info("tusb: %s%i.%i %s%i.%i %s%i.%i %s%i.%i %s%i %s%i.%i\n",
698c2ecf20Sopenharmony_ci		"prcm",
708c2ecf20Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_PRCM_REV)),
718c2ecf20Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_PRCM_REV)),
728c2ecf20Sopenharmony_ci		"int",
738c2ecf20Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_INT_CTRL_REV)),
748c2ecf20Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_INT_CTRL_REV)),
758c2ecf20Sopenharmony_ci		"gpio",
768c2ecf20Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_GPIO_REV)),
778c2ecf20Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_GPIO_REV)),
788c2ecf20Sopenharmony_ci		"dma",
798c2ecf20Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)),
808c2ecf20Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)),
818c2ecf20Sopenharmony_ci		"dieid",
828c2ecf20Sopenharmony_ci		TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, TUSB_DIDR1_HI)),
838c2ecf20Sopenharmony_ci		"rev",
848c2ecf20Sopenharmony_ci		TUSB_REV_MAJOR(rev), TUSB_REV_MINOR(rev));
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define WBUS_QUIRK_MASK	(TUSB_PHY_OTG_CTRL_TESTM2 | TUSB_PHY_OTG_CTRL_TESTM1 \
888c2ecf20Sopenharmony_ci				| TUSB_PHY_OTG_CTRL_TESTM0)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/*
918c2ecf20Sopenharmony_ci * Workaround for spontaneous WBUS wake-up issue #2 for tusb3.0.
928c2ecf20Sopenharmony_ci * Disables power detection in PHY for the duration of idle.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistatic void tusb_wbus_quirk(struct musb *musb, int enabled)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
978c2ecf20Sopenharmony_ci	static u32	phy_otg_ctrl, phy_otg_ena;
988c2ecf20Sopenharmony_ci	u32		tmp;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (enabled) {
1018c2ecf20Sopenharmony_ci		phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
1028c2ecf20Sopenharmony_ci		phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
1038c2ecf20Sopenharmony_ci		tmp = TUSB_PHY_OTG_CTRL_WRPROTECT
1048c2ecf20Sopenharmony_ci				| phy_otg_ena | WBUS_QUIRK_MASK;
1058c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp);
1068c2ecf20Sopenharmony_ci		tmp = phy_otg_ena & ~WBUS_QUIRK_MASK;
1078c2ecf20Sopenharmony_ci		tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2;
1088c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp);
1098c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "Enabled tusb wbus quirk ctrl %08x ena %08x\n",
1108c2ecf20Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL),
1118c2ecf20Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE));
1128c2ecf20Sopenharmony_ci	} else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)
1138c2ecf20Sopenharmony_ci					& TUSB_PHY_OTG_CTRL_TESTM2) {
1148c2ecf20Sopenharmony_ci		tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl;
1158c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp);
1168c2ecf20Sopenharmony_ci		tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena;
1178c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp);
1188c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "Disabled tusb wbus quirk ctrl %08x ena %08x\n",
1198c2ecf20Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL),
1208c2ecf20Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE));
1218c2ecf20Sopenharmony_ci		phy_otg_ctrl = 0;
1228c2ecf20Sopenharmony_ci		phy_otg_ena = 0;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic u32 tusb_fifo_offset(u8 epnum)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	return 0x200 + (epnum * 0x20);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic u32 tusb_ep_offset(u8 epnum, u16 offset)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	return 0x10 + offset;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* TUSB mapping: "flat" plus ep0 special cases */
1378c2ecf20Sopenharmony_cistatic void tusb_ep_select(void __iomem *mbase, u8 epnum)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	musb_writeb(mbase, MUSB_INDEX, epnum);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/*
1438c2ecf20Sopenharmony_ci * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_cistatic u8 tusb_readb(void __iomem *addr, u32 offset)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	u16 tmp;
1488c2ecf20Sopenharmony_ci	u8 val;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	tmp = __raw_readw(addr + (offset & ~1));
1518c2ecf20Sopenharmony_ci	if (offset & 1)
1528c2ecf20Sopenharmony_ci		val = (tmp >> 8);
1538c2ecf20Sopenharmony_ci	else
1548c2ecf20Sopenharmony_ci		val = tmp & 0xff;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return val;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void tusb_writeb(void __iomem *addr, u32 offset, u8 data)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	u16 tmp;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	tmp = __raw_readw(addr + (offset & ~1));
1648c2ecf20Sopenharmony_ci	if (offset & 1)
1658c2ecf20Sopenharmony_ci		tmp = (data << 8) | (tmp & 0xff);
1668c2ecf20Sopenharmony_ci	else
1678c2ecf20Sopenharmony_ci		tmp = (tmp & 0xff00) | data;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	__raw_writew(tmp, addr + (offset & ~1));
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * TUSB 6010 may use a parallel bus that doesn't support byte ops;
1748c2ecf20Sopenharmony_ci * so both loading and unloading FIFOs need explicit byte counts.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic inline void
1788c2ecf20Sopenharmony_citusb_fifo_write_unaligned(void __iomem *fifo, const u8 *buf, u16 len)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	u32		val;
1818c2ecf20Sopenharmony_ci	int		i;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (len > 4) {
1848c2ecf20Sopenharmony_ci		for (i = 0; i < (len >> 2); i++) {
1858c2ecf20Sopenharmony_ci			memcpy(&val, buf, 4);
1868c2ecf20Sopenharmony_ci			musb_writel(fifo, 0, val);
1878c2ecf20Sopenharmony_ci			buf += 4;
1888c2ecf20Sopenharmony_ci		}
1898c2ecf20Sopenharmony_ci		len %= 4;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	if (len > 0) {
1928c2ecf20Sopenharmony_ci		/* Write the rest 1 - 3 bytes to FIFO */
1938c2ecf20Sopenharmony_ci		val = 0;
1948c2ecf20Sopenharmony_ci		memcpy(&val, buf, len);
1958c2ecf20Sopenharmony_ci		musb_writel(fifo, 0, val);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic inline void tusb_fifo_read_unaligned(void __iomem *fifo,
2008c2ecf20Sopenharmony_ci						void *buf, u16 len)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	u32		val;
2038c2ecf20Sopenharmony_ci	int		i;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (len > 4) {
2068c2ecf20Sopenharmony_ci		for (i = 0; i < (len >> 2); i++) {
2078c2ecf20Sopenharmony_ci			val = musb_readl(fifo, 0);
2088c2ecf20Sopenharmony_ci			memcpy(buf, &val, 4);
2098c2ecf20Sopenharmony_ci			buf += 4;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci		len %= 4;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	if (len > 0) {
2148c2ecf20Sopenharmony_ci		/* Read the rest 1 - 3 bytes from FIFO */
2158c2ecf20Sopenharmony_ci		val = musb_readl(fifo, 0);
2168c2ecf20Sopenharmony_ci		memcpy(buf, &val, len);
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void tusb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct musb *musb = hw_ep->musb;
2238c2ecf20Sopenharmony_ci	void __iomem	*ep_conf = hw_ep->conf;
2248c2ecf20Sopenharmony_ci	void __iomem	*fifo = hw_ep->fifo;
2258c2ecf20Sopenharmony_ci	u8		epnum = hw_ep->epnum;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	prefetch(buf);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
2308c2ecf20Sopenharmony_ci			'T', epnum, fifo, len, buf);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (epnum)
2338c2ecf20Sopenharmony_ci		musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
2348c2ecf20Sopenharmony_ci			TUSB_EP_CONFIG_XFR_SIZE(len));
2358c2ecf20Sopenharmony_ci	else
2368c2ecf20Sopenharmony_ci		musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_DIR_TX |
2378c2ecf20Sopenharmony_ci			TUSB_EP0_CONFIG_XFR_SIZE(len));
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (likely((0x01 & (unsigned long) buf) == 0)) {
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		/* Best case is 32bit-aligned destination address */
2428c2ecf20Sopenharmony_ci		if ((0x02 & (unsigned long) buf) == 0) {
2438c2ecf20Sopenharmony_ci			if (len >= 4) {
2448c2ecf20Sopenharmony_ci				iowrite32_rep(fifo, buf, len >> 2);
2458c2ecf20Sopenharmony_ci				buf += (len & ~0x03);
2468c2ecf20Sopenharmony_ci				len &= 0x03;
2478c2ecf20Sopenharmony_ci			}
2488c2ecf20Sopenharmony_ci		} else {
2498c2ecf20Sopenharmony_ci			if (len >= 2) {
2508c2ecf20Sopenharmony_ci				u32 val;
2518c2ecf20Sopenharmony_ci				int i;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci				/* Cannot use writesw, fifo is 32-bit */
2548c2ecf20Sopenharmony_ci				for (i = 0; i < (len >> 2); i++) {
2558c2ecf20Sopenharmony_ci					val = (u32)(*(u16 *)buf);
2568c2ecf20Sopenharmony_ci					buf += 2;
2578c2ecf20Sopenharmony_ci					val |= (*(u16 *)buf) << 16;
2588c2ecf20Sopenharmony_ci					buf += 2;
2598c2ecf20Sopenharmony_ci					musb_writel(fifo, 0, val);
2608c2ecf20Sopenharmony_ci				}
2618c2ecf20Sopenharmony_ci				len &= 0x03;
2628c2ecf20Sopenharmony_ci			}
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (len > 0)
2678c2ecf20Sopenharmony_ci		tusb_fifo_write_unaligned(fifo, buf, len);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic void tusb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct musb *musb = hw_ep->musb;
2738c2ecf20Sopenharmony_ci	void __iomem	*ep_conf = hw_ep->conf;
2748c2ecf20Sopenharmony_ci	void __iomem	*fifo = hw_ep->fifo;
2758c2ecf20Sopenharmony_ci	u8		epnum = hw_ep->epnum;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
2788c2ecf20Sopenharmony_ci			'R', epnum, fifo, len, buf);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (epnum)
2818c2ecf20Sopenharmony_ci		musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
2828c2ecf20Sopenharmony_ci			TUSB_EP_CONFIG_XFR_SIZE(len));
2838c2ecf20Sopenharmony_ci	else
2848c2ecf20Sopenharmony_ci		musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_XFR_SIZE(len));
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (likely((0x01 & (unsigned long) buf) == 0)) {
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		/* Best case is 32bit-aligned destination address */
2898c2ecf20Sopenharmony_ci		if ((0x02 & (unsigned long) buf) == 0) {
2908c2ecf20Sopenharmony_ci			if (len >= 4) {
2918c2ecf20Sopenharmony_ci				ioread32_rep(fifo, buf, len >> 2);
2928c2ecf20Sopenharmony_ci				buf += (len & ~0x03);
2938c2ecf20Sopenharmony_ci				len &= 0x03;
2948c2ecf20Sopenharmony_ci			}
2958c2ecf20Sopenharmony_ci		} else {
2968c2ecf20Sopenharmony_ci			if (len >= 2) {
2978c2ecf20Sopenharmony_ci				u32 val;
2988c2ecf20Sopenharmony_ci				int i;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci				/* Cannot use readsw, fifo is 32-bit */
3018c2ecf20Sopenharmony_ci				for (i = 0; i < (len >> 2); i++) {
3028c2ecf20Sopenharmony_ci					val = musb_readl(fifo, 0);
3038c2ecf20Sopenharmony_ci					*(u16 *)buf = (u16)(val & 0xffff);
3048c2ecf20Sopenharmony_ci					buf += 2;
3058c2ecf20Sopenharmony_ci					*(u16 *)buf = (u16)(val >> 16);
3068c2ecf20Sopenharmony_ci					buf += 2;
3078c2ecf20Sopenharmony_ci				}
3088c2ecf20Sopenharmony_ci				len &= 0x03;
3098c2ecf20Sopenharmony_ci			}
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (len > 0)
3148c2ecf20Sopenharmony_ci		tusb_fifo_read_unaligned(fifo, buf, len);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic struct musb *the_musb;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/* This is used by gadget drivers, and OTG transceiver logic, allowing
3208c2ecf20Sopenharmony_ci * at most mA current to be drawn from VBUS during a Default-B session
3218c2ecf20Sopenharmony_ci * (that is, while VBUS exceeds 4.4V).  In Default-A (including pure host
3228c2ecf20Sopenharmony_ci * mode), or low power Default-B sessions, something else supplies power.
3238c2ecf20Sopenharmony_ci * Caller must take care of locking.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic int tusb_draw_power(struct usb_phy *x, unsigned mA)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct musb	*musb = the_musb;
3288c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
3298c2ecf20Sopenharmony_ci	u32		reg;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* tps65030 seems to consume max 100mA, with maybe 60mA available
3328c2ecf20Sopenharmony_ci	 * (measured on one board) for things other than tps and tusb.
3338c2ecf20Sopenharmony_ci	 *
3348c2ecf20Sopenharmony_ci	 * Boards sharing the CPU clock with CLKIN will need to prevent
3358c2ecf20Sopenharmony_ci	 * certain idle sleep states while the USB link is active.
3368c2ecf20Sopenharmony_ci	 *
3378c2ecf20Sopenharmony_ci	 * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }.
3388c2ecf20Sopenharmony_ci	 * The actual current usage would be very board-specific.  For now,
3398c2ecf20Sopenharmony_ci	 * it's simpler to just use an aggregate (also board-specific).
3408c2ecf20Sopenharmony_ci	 */
3418c2ecf20Sopenharmony_ci	if (x->otg->default_a || mA < (musb->min_power << 1))
3428c2ecf20Sopenharmony_ci		mA = 0;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PRCM_MNGMT);
3458c2ecf20Sopenharmony_ci	if (mA) {
3468c2ecf20Sopenharmony_ci		musb->is_bus_powered = 1;
3478c2ecf20Sopenharmony_ci		reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN;
3488c2ecf20Sopenharmony_ci	} else {
3498c2ecf20Sopenharmony_ci		musb->is_bus_powered = 0;
3508c2ecf20Sopenharmony_ci		reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT, reg);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "draw max %d mA VBUS\n", mA);
3558c2ecf20Sopenharmony_ci	return 0;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/* workaround for issue 13:  change clock during chip idle
3598c2ecf20Sopenharmony_ci * (to be fixed in rev3 silicon) ... symptoms include disconnect
3608c2ecf20Sopenharmony_ci * or looping suspend/resume cycles
3618c2ecf20Sopenharmony_ci */
3628c2ecf20Sopenharmony_cistatic void tusb_set_clock_source(struct musb *musb, unsigned mode)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
3658c2ecf20Sopenharmony_ci	u32		reg;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PRCM_CONF);
3688c2ecf20Sopenharmony_ci	reg &= ~TUSB_PRCM_CONF_SYS_CLKSEL(0x3);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* 0 = refclk (clkin, XI)
3718c2ecf20Sopenharmony_ci	 * 1 = PHY 60 MHz (internal PLL)
3728c2ecf20Sopenharmony_ci	 * 2 = not supported
3738c2ecf20Sopenharmony_ci	 * 3 = what?
3748c2ecf20Sopenharmony_ci	 */
3758c2ecf20Sopenharmony_ci	if (mode > 0)
3768c2ecf20Sopenharmony_ci		reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_CONF, reg);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* FIXME tusb6010_platform_retime(mode == 0); */
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/*
3848c2ecf20Sopenharmony_ci * Idle TUSB6010 until next wake-up event; NOR access always wakes.
3858c2ecf20Sopenharmony_ci * Other code ensures that we idle unless we're connected _and_ the
3868c2ecf20Sopenharmony_ci * USB link is not suspended ... and tells us the relevant wakeup
3878c2ecf20Sopenharmony_ci * events.  SW_EN for voltage is handled separately.
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_cistatic void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
3928c2ecf20Sopenharmony_ci	u32		reg;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if ((wakeup_enables & TUSB_PRCM_WBUS)
3958c2ecf20Sopenharmony_ci			&& (musb->tusb_revision == TUSB_REV_30))
3968c2ecf20Sopenharmony_ci		tusb_wbus_quirk(musb, 1);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	tusb_set_clock_source(musb, 0);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	wakeup_enables |= TUSB_PRCM_WNORCS;
4018c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* REVISIT writeup of WID implies that if WID set and ID is grounded,
4048c2ecf20Sopenharmony_ci	 * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared.
4058c2ecf20Sopenharmony_ci	 * Presumably that's mostly to save power, hence WID is immaterial ...
4068c2ecf20Sopenharmony_ci	 */
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PRCM_MNGMT);
4098c2ecf20Sopenharmony_ci	/* issue 4: when driving vbus, use hipower (vbus_det) comparator */
4108c2ecf20Sopenharmony_ci	if (is_host_active(musb)) {
4118c2ecf20Sopenharmony_ci		reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
4128c2ecf20Sopenharmony_ci		reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
4138c2ecf20Sopenharmony_ci	} else {
4148c2ecf20Sopenharmony_ci		reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
4158c2ecf20Sopenharmony_ci		reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci	reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE;
4188c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT, reg);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci * Updates cable VBUS status. Caller must take care of locking.
4258c2ecf20Sopenharmony_ci */
4268c2ecf20Sopenharmony_cistatic int tusb_musb_vbus_status(struct musb *musb)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
4298c2ecf20Sopenharmony_ci	u32		otg_stat, prcm_mngmt;
4308c2ecf20Sopenharmony_ci	int		ret = 0;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
4338c2ecf20Sopenharmony_ci	prcm_mngmt = musb_readl(tbase, TUSB_PRCM_MNGMT);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Temporarily enable VBUS detection if it was disabled for
4368c2ecf20Sopenharmony_ci	 * suspend mode. Unless it's enabled otg_stat and devctl will
4378c2ecf20Sopenharmony_ci	 * not show correct VBUS state.
4388c2ecf20Sopenharmony_ci	 */
4398c2ecf20Sopenharmony_ci	if (!(prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) {
4408c2ecf20Sopenharmony_ci		u32 tmp = prcm_mngmt;
4418c2ecf20Sopenharmony_ci		tmp |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
4428c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PRCM_MNGMT, tmp);
4438c2ecf20Sopenharmony_ci		otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
4448c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PRCM_MNGMT, prcm_mngmt);
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID)
4488c2ecf20Sopenharmony_ci		ret = 1;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return ret;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic void musb_do_idle(struct timer_list *t)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct musb	*musb = from_timer(musb, t, dev_timer);
4568c2ecf20Sopenharmony_ci	unsigned long	flags;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	switch (musb->xceiv->otg->state) {
4618c2ecf20Sopenharmony_ci	case OTG_STATE_A_WAIT_BCON:
4628c2ecf20Sopenharmony_ci		if ((musb->a_wait_bcon != 0)
4638c2ecf20Sopenharmony_ci			&& (musb->idle_timeout == 0
4648c2ecf20Sopenharmony_ci				|| time_after(jiffies, musb->idle_timeout))) {
4658c2ecf20Sopenharmony_ci			dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n",
4668c2ecf20Sopenharmony_ci					usb_otg_state_string(musb->xceiv->otg->state));
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci		fallthrough;
4698c2ecf20Sopenharmony_ci	case OTG_STATE_A_IDLE:
4708c2ecf20Sopenharmony_ci		tusb_musb_set_vbus(musb, 0);
4718c2ecf20Sopenharmony_ci	default:
4728c2ecf20Sopenharmony_ci		break;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (!musb->is_active) {
4768c2ecf20Sopenharmony_ci		u32	wakeups;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		/* wait until hub_wq handles port change status */
4798c2ecf20Sopenharmony_ci		if (is_host_active(musb) && (musb->port1_status >> 16))
4808c2ecf20Sopenharmony_ci			goto done;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci		if (!musb->gadget_driver) {
4838c2ecf20Sopenharmony_ci			wakeups = 0;
4848c2ecf20Sopenharmony_ci		} else {
4858c2ecf20Sopenharmony_ci			wakeups = TUSB_PRCM_WHOSTDISCON
4868c2ecf20Sopenharmony_ci				| TUSB_PRCM_WBUS
4878c2ecf20Sopenharmony_ci					| TUSB_PRCM_WVBUS;
4888c2ecf20Sopenharmony_ci			wakeups |= TUSB_PRCM_WID;
4898c2ecf20Sopenharmony_ci		}
4908c2ecf20Sopenharmony_ci		tusb_allow_idle(musb, wakeups);
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_cidone:
4938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/*
4978c2ecf20Sopenharmony_ci * Maybe put TUSB6010 into idle mode mode depending on USB link status,
4988c2ecf20Sopenharmony_ci * like "disconnected" or "suspended".  We'll be woken out of it by
4998c2ecf20Sopenharmony_ci * connect, resume, or disconnect.
5008c2ecf20Sopenharmony_ci *
5018c2ecf20Sopenharmony_ci * Needs to be called as the last function everywhere where there is
5028c2ecf20Sopenharmony_ci * register access to TUSB6010 because of NOR flash wake-up.
5038c2ecf20Sopenharmony_ci * Caller should own controller spinlock.
5048c2ecf20Sopenharmony_ci *
5058c2ecf20Sopenharmony_ci * Delay because peripheral enables D+ pullup 3msec after SE0, and
5068c2ecf20Sopenharmony_ci * we don't want to treat that full speed J as a wakeup event.
5078c2ecf20Sopenharmony_ci * ... peripherals must draw only suspend current after 10 msec.
5088c2ecf20Sopenharmony_ci */
5098c2ecf20Sopenharmony_cistatic void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	unsigned long		default_timeout = jiffies + msecs_to_jiffies(3);
5128c2ecf20Sopenharmony_ci	static unsigned long	last_timer;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (timeout == 0)
5158c2ecf20Sopenharmony_ci		timeout = default_timeout;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* Never idle if active, or when VBUS timeout is not set as host */
5188c2ecf20Sopenharmony_ci	if (musb->is_active || ((musb->a_wait_bcon == 0)
5198c2ecf20Sopenharmony_ci			&& (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) {
5208c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "%s active, deleting timer\n",
5218c2ecf20Sopenharmony_ci			usb_otg_state_string(musb->xceiv->otg->state));
5228c2ecf20Sopenharmony_ci		del_timer(&musb->dev_timer);
5238c2ecf20Sopenharmony_ci		last_timer = jiffies;
5248c2ecf20Sopenharmony_ci		return;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (time_after(last_timer, timeout)) {
5288c2ecf20Sopenharmony_ci		if (!timer_pending(&musb->dev_timer))
5298c2ecf20Sopenharmony_ci			last_timer = timeout;
5308c2ecf20Sopenharmony_ci		else {
5318c2ecf20Sopenharmony_ci			dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n");
5328c2ecf20Sopenharmony_ci			return;
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci	last_timer = timeout;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
5388c2ecf20Sopenharmony_ci		usb_otg_state_string(musb->xceiv->otg->state),
5398c2ecf20Sopenharmony_ci		(unsigned long)jiffies_to_msecs(timeout - jiffies));
5408c2ecf20Sopenharmony_ci	mod_timer(&musb->dev_timer, timeout);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/* ticks of 60 MHz clock */
5448c2ecf20Sopenharmony_ci#define DEVCLOCK		60000000
5458c2ecf20Sopenharmony_ci#define OTG_TIMER_MS(msecs)	((msecs) \
5468c2ecf20Sopenharmony_ci		? (TUSB_DEV_OTG_TIMER_VAL((DEVCLOCK/1000)*(msecs)) \
5478c2ecf20Sopenharmony_ci				| TUSB_DEV_OTG_TIMER_ENABLE) \
5488c2ecf20Sopenharmony_ci		: 0)
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
5538c2ecf20Sopenharmony_ci	u32		conf, prcm, timer;
5548c2ecf20Sopenharmony_ci	u8		devctl;
5558c2ecf20Sopenharmony_ci	struct usb_otg	*otg = musb->xceiv->otg;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* HDRC controls CPEN, but beware current surges during device
5588c2ecf20Sopenharmony_ci	 * connect.  They can trigger transient overcurrent conditions
5598c2ecf20Sopenharmony_ci	 * that must be ignored.
5608c2ecf20Sopenharmony_ci	 */
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	prcm = musb_readl(tbase, TUSB_PRCM_MNGMT);
5638c2ecf20Sopenharmony_ci	conf = musb_readl(tbase, TUSB_DEV_CONF);
5648c2ecf20Sopenharmony_ci	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (is_on) {
5678c2ecf20Sopenharmony_ci		timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
5688c2ecf20Sopenharmony_ci		otg->default_a = 1;
5698c2ecf20Sopenharmony_ci		musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
5708c2ecf20Sopenharmony_ci		devctl |= MUSB_DEVCTL_SESSION;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		conf |= TUSB_DEV_CONF_USB_HOST_MODE;
5738c2ecf20Sopenharmony_ci		MUSB_HST_MODE(musb);
5748c2ecf20Sopenharmony_ci	} else {
5758c2ecf20Sopenharmony_ci		u32	otg_stat;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		timer = 0;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		/* If ID pin is grounded, we want to be a_idle */
5808c2ecf20Sopenharmony_ci		otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
5818c2ecf20Sopenharmony_ci		if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
5828c2ecf20Sopenharmony_ci			switch (musb->xceiv->otg->state) {
5838c2ecf20Sopenharmony_ci			case OTG_STATE_A_WAIT_VRISE:
5848c2ecf20Sopenharmony_ci			case OTG_STATE_A_WAIT_BCON:
5858c2ecf20Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
5868c2ecf20Sopenharmony_ci				break;
5878c2ecf20Sopenharmony_ci			case OTG_STATE_A_WAIT_VFALL:
5888c2ecf20Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_IDLE;
5898c2ecf20Sopenharmony_ci				break;
5908c2ecf20Sopenharmony_ci			default:
5918c2ecf20Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_IDLE;
5928c2ecf20Sopenharmony_ci			}
5938c2ecf20Sopenharmony_ci			musb->is_active = 0;
5948c2ecf20Sopenharmony_ci			otg->default_a = 1;
5958c2ecf20Sopenharmony_ci			MUSB_HST_MODE(musb);
5968c2ecf20Sopenharmony_ci		} else {
5978c2ecf20Sopenharmony_ci			musb->is_active = 0;
5988c2ecf20Sopenharmony_ci			otg->default_a = 0;
5998c2ecf20Sopenharmony_ci			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
6008c2ecf20Sopenharmony_ci			MUSB_DEV_MODE(musb);
6018c2ecf20Sopenharmony_ci		}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		devctl &= ~MUSB_DEVCTL_SESSION;
6048c2ecf20Sopenharmony_ci		conf &= ~TUSB_DEV_CONF_USB_HOST_MODE;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci	prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT, prcm);
6098c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DEV_OTG_TIMER, timer);
6108c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DEV_CONF, conf);
6118c2ecf20Sopenharmony_ci	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n",
6148c2ecf20Sopenharmony_ci		usb_otg_state_string(musb->xceiv->otg->state),
6158c2ecf20Sopenharmony_ci		musb_readb(musb->mregs, MUSB_DEVCTL),
6168c2ecf20Sopenharmony_ci		musb_readl(tbase, TUSB_DEV_OTG_STAT),
6178c2ecf20Sopenharmony_ci		conf, prcm);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci/*
6218c2ecf20Sopenharmony_ci * Sets the mode to OTG, peripheral or host by changing the ID detection.
6228c2ecf20Sopenharmony_ci * Caller must take care of locking.
6238c2ecf20Sopenharmony_ci *
6248c2ecf20Sopenharmony_ci * Note that if a mini-A cable is plugged in the ID line will stay down as
6258c2ecf20Sopenharmony_ci * the weak ID pull-up is not able to pull the ID up.
6268c2ecf20Sopenharmony_ci */
6278c2ecf20Sopenharmony_cistatic int tusb_musb_set_mode(struct musb *musb, u8 musb_mode)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
6308c2ecf20Sopenharmony_ci	u32		otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
6338c2ecf20Sopenharmony_ci	phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
6348c2ecf20Sopenharmony_ci	phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
6358c2ecf20Sopenharmony_ci	dev_conf = musb_readl(tbase, TUSB_DEV_CONF);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	switch (musb_mode) {
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	case MUSB_HOST:		/* Disable PHY ID detect, ground ID */
6408c2ecf20Sopenharmony_ci		phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
6418c2ecf20Sopenharmony_ci		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
6428c2ecf20Sopenharmony_ci		dev_conf |= TUSB_DEV_CONF_ID_SEL;
6438c2ecf20Sopenharmony_ci		dev_conf &= ~TUSB_DEV_CONF_SOFT_ID;
6448c2ecf20Sopenharmony_ci		break;
6458c2ecf20Sopenharmony_ci	case MUSB_PERIPHERAL:	/* Disable PHY ID detect, keep ID pull-up on */
6468c2ecf20Sopenharmony_ci		phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
6478c2ecf20Sopenharmony_ci		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
6488c2ecf20Sopenharmony_ci		dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
6498c2ecf20Sopenharmony_ci		break;
6508c2ecf20Sopenharmony_ci	case MUSB_OTG:		/* Use PHY ID detection */
6518c2ecf20Sopenharmony_ci		phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
6528c2ecf20Sopenharmony_ci		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
6538c2ecf20Sopenharmony_ci		dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
6548c2ecf20Sopenharmony_ci		break;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	default:
6578c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode);
6588c2ecf20Sopenharmony_ci		return -EINVAL;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL,
6628c2ecf20Sopenharmony_ci			TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl);
6638c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE,
6648c2ecf20Sopenharmony_ci			TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena);
6658c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DEV_CONF, dev_conf);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
6688c2ecf20Sopenharmony_ci	if ((musb_mode == MUSB_PERIPHERAL) &&
6698c2ecf20Sopenharmony_ci		!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS))
6708c2ecf20Sopenharmony_ci			INFO("Cannot be peripheral with mini-A cable "
6718c2ecf20Sopenharmony_ci			"otg_stat: %08x\n", otg_stat);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return 0;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic inline unsigned long
6778c2ecf20Sopenharmony_citusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	u32		otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
6808c2ecf20Sopenharmony_ci	unsigned long	idle_timeout = 0;
6818c2ecf20Sopenharmony_ci	struct usb_otg	*otg = musb->xceiv->otg;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/* ID pin */
6848c2ecf20Sopenharmony_ci	if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) {
6858c2ecf20Sopenharmony_ci		int	default_a;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci		default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
6888c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B');
6898c2ecf20Sopenharmony_ci		otg->default_a = default_a;
6908c2ecf20Sopenharmony_ci		tusb_musb_set_vbus(musb, default_a);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		/* Don't allow idling immediately */
6938c2ecf20Sopenharmony_ci		if (default_a)
6948c2ecf20Sopenharmony_ci			idle_timeout = jiffies + (HZ * 3);
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* VBUS state change */
6988c2ecf20Sopenharmony_ci	if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci		/* B-dev state machine:  no vbus ~= disconnect */
7018c2ecf20Sopenharmony_ci		if (!otg->default_a) {
7028c2ecf20Sopenharmony_ci			/* ? musb_root_disconnect(musb); */
7038c2ecf20Sopenharmony_ci			musb->port1_status &=
7048c2ecf20Sopenharmony_ci				~(USB_PORT_STAT_CONNECTION
7058c2ecf20Sopenharmony_ci				| USB_PORT_STAT_ENABLE
7068c2ecf20Sopenharmony_ci				| USB_PORT_STAT_LOW_SPEED
7078c2ecf20Sopenharmony_ci				| USB_PORT_STAT_HIGH_SPEED
7088c2ecf20Sopenharmony_ci				| USB_PORT_STAT_TEST
7098c2ecf20Sopenharmony_ci				);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci			if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
7128c2ecf20Sopenharmony_ci				dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n");
7138c2ecf20Sopenharmony_ci				if (musb->xceiv->otg->state != OTG_STATE_B_IDLE) {
7148c2ecf20Sopenharmony_ci					/* INTR_DISCONNECT can hide... */
7158c2ecf20Sopenharmony_ci					musb->xceiv->otg->state = OTG_STATE_B_IDLE;
7168c2ecf20Sopenharmony_ci					musb->int_usb |= MUSB_INTR_DISCONNECT;
7178c2ecf20Sopenharmony_ci				}
7188c2ecf20Sopenharmony_ci				musb->is_active = 0;
7198c2ecf20Sopenharmony_ci			}
7208c2ecf20Sopenharmony_ci			dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
7218c2ecf20Sopenharmony_ci				usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
7228c2ecf20Sopenharmony_ci			idle_timeout = jiffies + (1 * HZ);
7238c2ecf20Sopenharmony_ci			schedule_delayed_work(&musb->irq_work, 0);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci		} else /* A-dev state machine */ {
7268c2ecf20Sopenharmony_ci			dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
7278c2ecf20Sopenharmony_ci				usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci			switch (musb->xceiv->otg->state) {
7308c2ecf20Sopenharmony_ci			case OTG_STATE_A_IDLE:
7318c2ecf20Sopenharmony_ci				dev_dbg(musb->controller, "Got SRP, turning on VBUS\n");
7328c2ecf20Sopenharmony_ci				musb_platform_set_vbus(musb, 1);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci				/* CONNECT can wake if a_wait_bcon is set */
7358c2ecf20Sopenharmony_ci				if (musb->a_wait_bcon != 0)
7368c2ecf20Sopenharmony_ci					musb->is_active = 0;
7378c2ecf20Sopenharmony_ci				else
7388c2ecf20Sopenharmony_ci					musb->is_active = 1;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci				/*
7418c2ecf20Sopenharmony_ci				 * OPT FS A TD.4.6 needs few seconds for
7428c2ecf20Sopenharmony_ci				 * A_WAIT_VRISE
7438c2ecf20Sopenharmony_ci				 */
7448c2ecf20Sopenharmony_ci				idle_timeout = jiffies + (2 * HZ);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci				break;
7478c2ecf20Sopenharmony_ci			case OTG_STATE_A_WAIT_VRISE:
7488c2ecf20Sopenharmony_ci				/* ignore; A-session-valid < VBUS_VALID/2,
7498c2ecf20Sopenharmony_ci				 * we monitor this with the timer
7508c2ecf20Sopenharmony_ci				 */
7518c2ecf20Sopenharmony_ci				break;
7528c2ecf20Sopenharmony_ci			case OTG_STATE_A_WAIT_VFALL:
7538c2ecf20Sopenharmony_ci				/* REVISIT this irq triggers during short
7548c2ecf20Sopenharmony_ci				 * spikes caused by enumeration ...
7558c2ecf20Sopenharmony_ci				 */
7568c2ecf20Sopenharmony_ci				if (musb->vbuserr_retry) {
7578c2ecf20Sopenharmony_ci					musb->vbuserr_retry--;
7588c2ecf20Sopenharmony_ci					tusb_musb_set_vbus(musb, 1);
7598c2ecf20Sopenharmony_ci				} else {
7608c2ecf20Sopenharmony_ci					musb->vbuserr_retry
7618c2ecf20Sopenharmony_ci						= VBUSERR_RETRY_COUNT;
7628c2ecf20Sopenharmony_ci					tusb_musb_set_vbus(musb, 0);
7638c2ecf20Sopenharmony_ci				}
7648c2ecf20Sopenharmony_ci				break;
7658c2ecf20Sopenharmony_ci			default:
7668c2ecf20Sopenharmony_ci				break;
7678c2ecf20Sopenharmony_ci			}
7688c2ecf20Sopenharmony_ci		}
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* OTG timer expiration */
7728c2ecf20Sopenharmony_ci	if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) {
7738c2ecf20Sopenharmony_ci		u8	devctl;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "%s timer, %03x\n",
7768c2ecf20Sopenharmony_ci			usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		switch (musb->xceiv->otg->state) {
7798c2ecf20Sopenharmony_ci		case OTG_STATE_A_WAIT_VRISE:
7808c2ecf20Sopenharmony_ci			/* VBUS has probably been valid for a while now,
7818c2ecf20Sopenharmony_ci			 * but may well have bounced out of range a bit
7828c2ecf20Sopenharmony_ci			 */
7838c2ecf20Sopenharmony_ci			devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
7848c2ecf20Sopenharmony_ci			if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
7858c2ecf20Sopenharmony_ci				if ((devctl & MUSB_DEVCTL_VBUS)
7868c2ecf20Sopenharmony_ci						!= MUSB_DEVCTL_VBUS) {
7878c2ecf20Sopenharmony_ci					dev_dbg(musb->controller, "devctl %02x\n", devctl);
7888c2ecf20Sopenharmony_ci					break;
7898c2ecf20Sopenharmony_ci				}
7908c2ecf20Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
7918c2ecf20Sopenharmony_ci				musb->is_active = 0;
7928c2ecf20Sopenharmony_ci				idle_timeout = jiffies
7938c2ecf20Sopenharmony_ci					+ msecs_to_jiffies(musb->a_wait_bcon);
7948c2ecf20Sopenharmony_ci			} else {
7958c2ecf20Sopenharmony_ci				/* REVISIT report overcurrent to hub? */
7968c2ecf20Sopenharmony_ci				ERR("vbus too slow, devctl %02x\n", devctl);
7978c2ecf20Sopenharmony_ci				tusb_musb_set_vbus(musb, 0);
7988c2ecf20Sopenharmony_ci			}
7998c2ecf20Sopenharmony_ci			break;
8008c2ecf20Sopenharmony_ci		case OTG_STATE_A_WAIT_BCON:
8018c2ecf20Sopenharmony_ci			if (musb->a_wait_bcon != 0)
8028c2ecf20Sopenharmony_ci				idle_timeout = jiffies
8038c2ecf20Sopenharmony_ci					+ msecs_to_jiffies(musb->a_wait_bcon);
8048c2ecf20Sopenharmony_ci			break;
8058c2ecf20Sopenharmony_ci		case OTG_STATE_A_SUSPEND:
8068c2ecf20Sopenharmony_ci			break;
8078c2ecf20Sopenharmony_ci		case OTG_STATE_B_WAIT_ACON:
8088c2ecf20Sopenharmony_ci			break;
8098c2ecf20Sopenharmony_ci		default:
8108c2ecf20Sopenharmony_ci			break;
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci	schedule_delayed_work(&musb->irq_work, 0);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	return idle_timeout;
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct musb	*musb = __hci;
8218c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
8228c2ecf20Sopenharmony_ci	unsigned long	flags, idle_timeout = 0;
8238c2ecf20Sopenharmony_ci	u32		int_mask, int_src;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* Mask all interrupts to allow using both edge and level GPIO irq */
8288c2ecf20Sopenharmony_ci	int_mask = musb_readl(tbase, TUSB_INT_MASK);
8298c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS;
8328c2ecf20Sopenharmony_ci	dev_dbg(musb->controller, "TUSB IRQ %08x\n", int_src);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	musb->int_usb = (u8) int_src;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	/* Acknowledge wake-up source interrupts */
8378c2ecf20Sopenharmony_ci	if (int_src & TUSB_INT_SRC_DEV_WAKEUP) {
8388c2ecf20Sopenharmony_ci		u32	reg;
8398c2ecf20Sopenharmony_ci		u32	i;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		if (musb->tusb_revision == TUSB_REV_30)
8428c2ecf20Sopenharmony_ci			tusb_wbus_quirk(musb, 0);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		/* there are issues re-locking the PLL on wakeup ... */
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		/* work around issue 8 */
8478c2ecf20Sopenharmony_ci		for (i = 0xf7f7f7; i > 0xf7f7f7 - 1000; i--) {
8488c2ecf20Sopenharmony_ci			musb_writel(tbase, TUSB_SCRATCH_PAD, 0);
8498c2ecf20Sopenharmony_ci			musb_writel(tbase, TUSB_SCRATCH_PAD, i);
8508c2ecf20Sopenharmony_ci			reg = musb_readl(tbase, TUSB_SCRATCH_PAD);
8518c2ecf20Sopenharmony_ci			if (reg == i)
8528c2ecf20Sopenharmony_ci				break;
8538c2ecf20Sopenharmony_ci			dev_dbg(musb->controller, "TUSB NOR not ready\n");
8548c2ecf20Sopenharmony_ci		}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		/* work around issue 13 (2nd half) */
8578c2ecf20Sopenharmony_ci		tusb_set_clock_source(musb, 1);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci		reg = musb_readl(tbase, TUSB_PRCM_WAKEUP_SOURCE);
8608c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_PRCM_WAKEUP_CLEAR, reg);
8618c2ecf20Sopenharmony_ci		if (reg & ~TUSB_PRCM_WNORCS) {
8628c2ecf20Sopenharmony_ci			musb->is_active = 1;
8638c2ecf20Sopenharmony_ci			schedule_delayed_work(&musb->irq_work, 0);
8648c2ecf20Sopenharmony_ci		}
8658c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "wake %sactive %02x\n",
8668c2ecf20Sopenharmony_ci				musb->is_active ? "" : "in", reg);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci		/* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	if (int_src & TUSB_INT_SRC_USB_IP_CONN)
8728c2ecf20Sopenharmony_ci		del_timer(&musb->dev_timer);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* OTG state change reports (annoyingly) not issued by Mentor core */
8758c2ecf20Sopenharmony_ci	if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG
8768c2ecf20Sopenharmony_ci				| TUSB_INT_SRC_OTG_TIMEOUT
8778c2ecf20Sopenharmony_ci				| TUSB_INT_SRC_ID_STATUS_CHNG))
8788c2ecf20Sopenharmony_ci		idle_timeout = tusb_otg_ints(musb, int_src, tbase);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	/*
8818c2ecf20Sopenharmony_ci	 * Just clear the DMA interrupt if it comes as the completion for both
8828c2ecf20Sopenharmony_ci	 * TX and RX is handled by the DMA callback in tusb6010_omap
8838c2ecf20Sopenharmony_ci	 */
8848c2ecf20Sopenharmony_ci	if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) {
8858c2ecf20Sopenharmony_ci		u32	dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src);
8888c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_DMA_INT_CLEAR, dma_src);
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	/* EP interrupts. In OCP mode tusb6010 mirrors the MUSB interrupts */
8928c2ecf20Sopenharmony_ci	if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX)) {
8938c2ecf20Sopenharmony_ci		u32	musb_src = musb_readl(tbase, TUSB_USBIP_INT_SRC);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_USBIP_INT_CLEAR, musb_src);
8968c2ecf20Sopenharmony_ci		musb->int_rx = (((musb_src >> 16) & 0xffff) << 1);
8978c2ecf20Sopenharmony_ci		musb->int_tx = (musb_src & 0xffff);
8988c2ecf20Sopenharmony_ci	} else {
8998c2ecf20Sopenharmony_ci		musb->int_rx = 0;
9008c2ecf20Sopenharmony_ci		musb->int_tx = 0;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff))
9048c2ecf20Sopenharmony_ci		musb_interrupt(musb);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	/* Acknowledge TUSB interrupts. Clear only non-reserved bits */
9078c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_SRC_CLEAR,
9088c2ecf20Sopenharmony_ci		int_src & ~TUSB_INT_MASK_RESERVED_BITS);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	tusb_musb_try_idle(musb, idle_timeout);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, int_mask);
9138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int dma_off;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/*
9218c2ecf20Sopenharmony_ci * Enables TUSB6010. Caller must take care of locking.
9228c2ecf20Sopenharmony_ci * REVISIT:
9238c2ecf20Sopenharmony_ci * - Check what is unnecessary in MGC_HdrcStart()
9248c2ecf20Sopenharmony_ci */
9258c2ecf20Sopenharmony_cistatic void tusb_musb_enable(struct musb *musb)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	/* Setup TUSB6010 main interrupt mask. Enable all interrupts except SOF.
9308c2ecf20Sopenharmony_ci	 * REVISIT: Enable and deal with TUSB_INT_SRC_USB_IP_SOF */
9318c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, TUSB_INT_SRC_USB_IP_SOF);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	/* Setup TUSB interrupt, disable DMA and GPIO interrupts */
9348c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_USBIP_INT_MASK, 0);
9358c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff);
9368c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/* Clear all subsystem interrups */
9398c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_USBIP_INT_CLEAR, 0x7fffffff);
9408c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_INT_CLEAR, 0x7fffffff);
9418c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_INT_CLEAR, 0x1ff);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* Acknowledge pending interrupt(s) */
9448c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_SRC_CLEAR, ~TUSB_INT_MASK_RESERVED_BITS);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* Only 0 clock cycles for minimum interrupt de-assertion time and
9478c2ecf20Sopenharmony_ci	 * interrupt polarity active low seems to work reliably here */
9488c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_CTRL_CONF,
9498c2ecf20Sopenharmony_ci			TUSB_INT_CTRL_CONF_INT_RELCYC(0));
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	irq_set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* maybe force into the Default-A OTG state machine */
9548c2ecf20Sopenharmony_ci	if (!(musb_readl(tbase, TUSB_DEV_OTG_STAT)
9558c2ecf20Sopenharmony_ci			& TUSB_DEV_OTG_STAT_ID_STATUS))
9568c2ecf20Sopenharmony_ci		musb_writel(tbase, TUSB_INT_SRC_SET,
9578c2ecf20Sopenharmony_ci				TUSB_INT_SRC_ID_STATUS_CHNG);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (is_dma_capable() && dma_off)
9608c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s %s: dma not reactivated\n",
9618c2ecf20Sopenharmony_ci				__FILE__, __func__);
9628c2ecf20Sopenharmony_ci	else
9638c2ecf20Sopenharmony_ci		dma_off = 1;
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci/*
9678c2ecf20Sopenharmony_ci * Disables TUSB6010. Caller must take care of locking.
9688c2ecf20Sopenharmony_ci */
9698c2ecf20Sopenharmony_cistatic void tusb_musb_disable(struct musb *musb)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/* FIXME stop DMA, IRQs, timers, ... */
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	/* disable all IRQs */
9768c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS);
9778c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_USBIP_INT_MASK, 0x7fffffff);
9788c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff);
9798c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	del_timer(&musb->dev_timer);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (is_dma_capable() && !dma_off) {
9848c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s %s: dma still active\n",
9858c2ecf20Sopenharmony_ci				__FILE__, __func__);
9868c2ecf20Sopenharmony_ci		dma_off = 1;
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci/*
9918c2ecf20Sopenharmony_ci * Sets up TUSB6010 CPU interface specific signals and registers
9928c2ecf20Sopenharmony_ci * Note: Settings optimized for OMAP24xx
9938c2ecf20Sopenharmony_ci */
9948c2ecf20Sopenharmony_cistatic void tusb_setup_cpu_interface(struct musb *musb)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/*
9998c2ecf20Sopenharmony_ci	 * Disable GPIO[5:0] pullups (used as output DMA requests)
10008c2ecf20Sopenharmony_ci	 * Don't disable GPIO[7:6] as they are needed for wake-up.
10018c2ecf20Sopenharmony_ci	 */
10028c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PULLUP_1_CTRL, 0x0000003F);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	/* Disable all pullups on NOR IF, DMAREQ0 and DMAREQ1 */
10058c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PULLUP_2_CTRL, 0x01FFFFFF);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* Turn GPIO[5:0] to DMAREQ[5:0] signals */
10088c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_CONF, TUSB_GPIO_CONF_DMAREQ(0x3f));
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* Burst size 16x16 bits, all six DMA requests enabled, DMA request
10118c2ecf20Sopenharmony_ci	 * de-assertion time 2 system clocks p 62 */
10128c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_REQ_CONF,
10138c2ecf20Sopenharmony_ci		TUSB_DMA_REQ_CONF_BURST_SIZE(2) |
10148c2ecf20Sopenharmony_ci		TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) |
10158c2ecf20Sopenharmony_ci		TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Set 0 wait count for synchronous burst access */
10188c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_WAIT_COUNT, 1);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic int tusb_musb_start(struct musb *musb)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
10248c2ecf20Sopenharmony_ci	int		ret = 0;
10258c2ecf20Sopenharmony_ci	unsigned long	flags;
10268c2ecf20Sopenharmony_ci	u32		reg;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	if (musb->board_set_power)
10298c2ecf20Sopenharmony_ci		ret = musb->board_set_power(1);
10308c2ecf20Sopenharmony_ci	if (ret != 0) {
10318c2ecf20Sopenharmony_ci		printk(KERN_ERR "tusb: Cannot enable TUSB6010\n");
10328c2ecf20Sopenharmony_ci		return ret;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (musb_readl(tbase, TUSB_PROD_TEST_RESET) !=
10388c2ecf20Sopenharmony_ci		TUSB_PROD_TEST_RESET_VAL) {
10398c2ecf20Sopenharmony_ci		printk(KERN_ERR "tusb: Unable to detect TUSB6010\n");
10408c2ecf20Sopenharmony_ci		goto err;
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	musb->tusb_revision = tusb_get_revision(musb);
10448c2ecf20Sopenharmony_ci	tusb_print_revision(musb);
10458c2ecf20Sopenharmony_ci	if (musb->tusb_revision < 2) {
10468c2ecf20Sopenharmony_ci		printk(KERN_ERR "tusb: Unsupported TUSB6010 revision %i\n",
10478c2ecf20Sopenharmony_ci				musb->tusb_revision);
10488c2ecf20Sopenharmony_ci		goto err;
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* The uint bit for "USB non-PDR interrupt enable" has to be 1 when
10528c2ecf20Sopenharmony_ci	 * NOR FLASH interface is used */
10538c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_VLYNQ_CTRL, 8);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	/* Select PHY free running 60MHz as a system clock */
10568c2ecf20Sopenharmony_ci	tusb_set_clock_source(musb, 1);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* VBus valid timer 1us, disable DFT/Debug and VLYNQ clocks for
10598c2ecf20Sopenharmony_ci	 * power saving, enable VBus detect and session end comparators,
10608c2ecf20Sopenharmony_ci	 * enable IDpullup, enable VBus charging */
10618c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT,
10628c2ecf20Sopenharmony_ci		TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(0xa) |
10638c2ecf20Sopenharmony_ci		TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN |
10648c2ecf20Sopenharmony_ci		TUSB_PRCM_MNGMT_OTG_SESS_END_EN |
10658c2ecf20Sopenharmony_ci		TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN |
10668c2ecf20Sopenharmony_ci		TUSB_PRCM_MNGMT_OTG_ID_PULLUP);
10678c2ecf20Sopenharmony_ci	tusb_setup_cpu_interface(musb);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	/* simplify:  always sense/pullup ID pins, as if in OTG mode */
10708c2ecf20Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
10718c2ecf20Sopenharmony_ci	reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
10728c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, reg);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
10758c2ecf20Sopenharmony_ci	reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
10768c2ecf20Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL, reg);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	return 0;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cierr:
10838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (musb->board_set_power)
10868c2ecf20Sopenharmony_ci		musb->board_set_power(0);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	return -ENODEV;
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic int tusb_musb_init(struct musb *musb)
10928c2ecf20Sopenharmony_ci{
10938c2ecf20Sopenharmony_ci	struct platform_device	*pdev;
10948c2ecf20Sopenharmony_ci	struct resource		*mem;
10958c2ecf20Sopenharmony_ci	void __iomem		*sync = NULL;
10968c2ecf20Sopenharmony_ci	int			ret;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
10998c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(musb->xceiv))
11008c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	pdev = to_platform_device(musb->controller);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/* dma address for async dma */
11058c2ecf20Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
11068c2ecf20Sopenharmony_ci	if (!mem) {
11078c2ecf20Sopenharmony_ci		pr_debug("no async dma resource?\n");
11088c2ecf20Sopenharmony_ci		ret = -ENODEV;
11098c2ecf20Sopenharmony_ci		goto done;
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci	musb->async = mem->start;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* dma address for sync dma */
11148c2ecf20Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
11158c2ecf20Sopenharmony_ci	if (!mem) {
11168c2ecf20Sopenharmony_ci		pr_debug("no sync dma resource?\n");
11178c2ecf20Sopenharmony_ci		ret = -ENODEV;
11188c2ecf20Sopenharmony_ci		goto done;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci	musb->sync = mem->start;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	sync = ioremap(mem->start, resource_size(mem));
11238c2ecf20Sopenharmony_ci	if (!sync) {
11248c2ecf20Sopenharmony_ci		pr_debug("ioremap for sync failed\n");
11258c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11268c2ecf20Sopenharmony_ci		goto done;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci	musb->sync_va = sync;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* Offsets from base: VLYNQ at 0x000, MUSB regs at 0x400,
11318c2ecf20Sopenharmony_ci	 * FIFOs at 0x600, TUSB at 0x800
11328c2ecf20Sopenharmony_ci	 */
11338c2ecf20Sopenharmony_ci	musb->mregs += TUSB_BASE_OFFSET;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	ret = tusb_musb_start(musb);
11368c2ecf20Sopenharmony_ci	if (ret) {
11378c2ecf20Sopenharmony_ci		printk(KERN_ERR "Could not start tusb6010 (%d)\n",
11388c2ecf20Sopenharmony_ci				ret);
11398c2ecf20Sopenharmony_ci		goto done;
11408c2ecf20Sopenharmony_ci	}
11418c2ecf20Sopenharmony_ci	musb->isr = tusb_musb_interrupt;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	musb->xceiv->set_power = tusb_draw_power;
11448c2ecf20Sopenharmony_ci	the_musb = musb;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	timer_setup(&musb->dev_timer, musb_do_idle, 0);
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cidone:
11498c2ecf20Sopenharmony_ci	if (ret < 0) {
11508c2ecf20Sopenharmony_ci		if (sync)
11518c2ecf20Sopenharmony_ci			iounmap(sync);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci		usb_put_phy(musb->xceiv);
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci	return ret;
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_cistatic int tusb_musb_exit(struct musb *musb)
11598c2ecf20Sopenharmony_ci{
11608c2ecf20Sopenharmony_ci	del_timer_sync(&musb->dev_timer);
11618c2ecf20Sopenharmony_ci	the_musb = NULL;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	if (musb->board_set_power)
11648c2ecf20Sopenharmony_ci		musb->board_set_power(0);
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	iounmap(musb->sync_va);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	usb_put_phy(musb->xceiv);
11698c2ecf20Sopenharmony_ci	return 0;
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic const struct musb_platform_ops tusb_ops = {
11738c2ecf20Sopenharmony_ci	.quirks		= MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB |
11748c2ecf20Sopenharmony_ci			  MUSB_G_NO_SKB_RESERVE,
11758c2ecf20Sopenharmony_ci	.init		= tusb_musb_init,
11768c2ecf20Sopenharmony_ci	.exit		= tusb_musb_exit,
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	.ep_offset	= tusb_ep_offset,
11798c2ecf20Sopenharmony_ci	.ep_select	= tusb_ep_select,
11808c2ecf20Sopenharmony_ci	.fifo_offset	= tusb_fifo_offset,
11818c2ecf20Sopenharmony_ci	.readb		= tusb_readb,
11828c2ecf20Sopenharmony_ci	.writeb		= tusb_writeb,
11838c2ecf20Sopenharmony_ci	.read_fifo	= tusb_read_fifo,
11848c2ecf20Sopenharmony_ci	.write_fifo	= tusb_write_fifo,
11858c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_TUSB_OMAP_DMA
11868c2ecf20Sopenharmony_ci	.dma_init	= tusb_dma_controller_create,
11878c2ecf20Sopenharmony_ci	.dma_exit	= tusb_dma_controller_destroy,
11888c2ecf20Sopenharmony_ci#endif
11898c2ecf20Sopenharmony_ci	.enable		= tusb_musb_enable,
11908c2ecf20Sopenharmony_ci	.disable	= tusb_musb_disable,
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	.set_mode	= tusb_musb_set_mode,
11938c2ecf20Sopenharmony_ci	.try_idle	= tusb_musb_try_idle,
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	.vbus_status	= tusb_musb_vbus_status,
11968c2ecf20Sopenharmony_ci	.set_vbus	= tusb_musb_set_vbus,
11978c2ecf20Sopenharmony_ci};
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic const struct platform_device_info tusb_dev_info = {
12008c2ecf20Sopenharmony_ci	.name		= "musb-hdrc",
12018c2ecf20Sopenharmony_ci	.id		= PLATFORM_DEVID_AUTO,
12028c2ecf20Sopenharmony_ci	.dma_mask	= DMA_BIT_MASK(32),
12038c2ecf20Sopenharmony_ci};
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cistatic int tusb_probe(struct platform_device *pdev)
12068c2ecf20Sopenharmony_ci{
12078c2ecf20Sopenharmony_ci	struct resource musb_resources[3];
12088c2ecf20Sopenharmony_ci	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev);
12098c2ecf20Sopenharmony_ci	struct platform_device		*musb;
12108c2ecf20Sopenharmony_ci	struct tusb6010_glue		*glue;
12118c2ecf20Sopenharmony_ci	struct platform_device_info	pinfo;
12128c2ecf20Sopenharmony_ci	int				ret;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
12158c2ecf20Sopenharmony_ci	if (!glue)
12168c2ecf20Sopenharmony_ci		return -ENOMEM;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	glue->dev			= &pdev->dev;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	pdata->platform_ops		= &tusb_ops;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	usb_phy_generic_register();
12238c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, glue);
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	memset(musb_resources, 0x00, sizeof(*musb_resources) *
12268c2ecf20Sopenharmony_ci			ARRAY_SIZE(musb_resources));
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	musb_resources[0].name = pdev->resource[0].name;
12298c2ecf20Sopenharmony_ci	musb_resources[0].start = pdev->resource[0].start;
12308c2ecf20Sopenharmony_ci	musb_resources[0].end = pdev->resource[0].end;
12318c2ecf20Sopenharmony_ci	musb_resources[0].flags = pdev->resource[0].flags;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	musb_resources[1].name = pdev->resource[1].name;
12348c2ecf20Sopenharmony_ci	musb_resources[1].start = pdev->resource[1].start;
12358c2ecf20Sopenharmony_ci	musb_resources[1].end = pdev->resource[1].end;
12368c2ecf20Sopenharmony_ci	musb_resources[1].flags = pdev->resource[1].flags;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	musb_resources[2].name = pdev->resource[2].name;
12398c2ecf20Sopenharmony_ci	musb_resources[2].start = pdev->resource[2].start;
12408c2ecf20Sopenharmony_ci	musb_resources[2].end = pdev->resource[2].end;
12418c2ecf20Sopenharmony_ci	musb_resources[2].flags = pdev->resource[2].flags;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	pinfo = tusb_dev_info;
12448c2ecf20Sopenharmony_ci	pinfo.parent = &pdev->dev;
12458c2ecf20Sopenharmony_ci	pinfo.res = musb_resources;
12468c2ecf20Sopenharmony_ci	pinfo.num_res = ARRAY_SIZE(musb_resources);
12478c2ecf20Sopenharmony_ci	pinfo.data = pdata;
12488c2ecf20Sopenharmony_ci	pinfo.size_data = sizeof(*pdata);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	glue->musb = musb = platform_device_register_full(&pinfo);
12518c2ecf20Sopenharmony_ci	if (IS_ERR(musb)) {
12528c2ecf20Sopenharmony_ci		ret = PTR_ERR(musb);
12538c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
12548c2ecf20Sopenharmony_ci		return ret;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	return 0;
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic int tusb_remove(struct platform_device *pdev)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	struct tusb6010_glue		*glue = platform_get_drvdata(pdev);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	platform_device_unregister(glue->musb);
12658c2ecf20Sopenharmony_ci	usb_phy_generic_unregister(glue->phy);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	return 0;
12688c2ecf20Sopenharmony_ci}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic struct platform_driver tusb_driver = {
12718c2ecf20Sopenharmony_ci	.probe		= tusb_probe,
12728c2ecf20Sopenharmony_ci	.remove		= tusb_remove,
12738c2ecf20Sopenharmony_ci	.driver		= {
12748c2ecf20Sopenharmony_ci		.name	= "musb-tusb",
12758c2ecf20Sopenharmony_ci	},
12768c2ecf20Sopenharmony_ci};
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TUSB6010 MUSB Glue Layer");
12798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
12808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
12818c2ecf20Sopenharmony_cimodule_platform_driver(tusb_driver);
1282