162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TUSB6010 USB 2.0 OTG Dual Role controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006 Nokia Corporation
662306a36Sopenharmony_ci * Tony Lindgren <tony@atomide.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Notes:
962306a36Sopenharmony_ci * - Driver assumes that interface to external host (main CPU) is
1062306a36Sopenharmony_ci *   configured for NOR FLASH interface instead of VLYNQ serial
1162306a36Sopenharmony_ci *   interface.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/err.h>
2062306a36Sopenharmony_ci#include <linux/prefetch.h>
2162306a36Sopenharmony_ci#include <linux/usb.h>
2262306a36Sopenharmony_ci#include <linux/irq.h>
2362306a36Sopenharmony_ci#include <linux/io.h>
2462306a36Sopenharmony_ci#include <linux/iopoll.h>
2562306a36Sopenharmony_ci#include <linux/device.h>
2662306a36Sopenharmony_ci#include <linux/platform_device.h>
2762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2862306a36Sopenharmony_ci#include <linux/usb/usb_phy_generic.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "musb_core.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct tusb6010_glue {
3362306a36Sopenharmony_ci	struct device		*dev;
3462306a36Sopenharmony_ci	struct platform_device	*musb;
3562306a36Sopenharmony_ci	struct platform_device	*phy;
3662306a36Sopenharmony_ci	struct gpio_desc	*enable;
3762306a36Sopenharmony_ci	struct gpio_desc	*intpin;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define TUSB_REV_MAJOR(reg_val)		((reg_val >> 4) & 0xf)
4362306a36Sopenharmony_ci#define TUSB_REV_MINOR(reg_val)		(reg_val & 0xf)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * Checks the revision. We need to use the DMA register as 3.0 does not
4762306a36Sopenharmony_ci * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic u8 tusb_get_revision(struct musb *musb)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
5262306a36Sopenharmony_ci	u32		die_id;
5362306a36Sopenharmony_ci	u8		rev;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	rev = musb_readl(tbase, TUSB_DMA_CTRL_REV) & 0xff;
5662306a36Sopenharmony_ci	if (TUSB_REV_MAJOR(rev) == 3) {
5762306a36Sopenharmony_ci		die_id = TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase,
5862306a36Sopenharmony_ci				TUSB_DIDR1_HI));
5962306a36Sopenharmony_ci		if (die_id >= TUSB_DIDR1_HI_REV_31)
6062306a36Sopenharmony_ci			rev |= 1;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return rev;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void tusb_print_revision(struct musb *musb)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
6962306a36Sopenharmony_ci	u8		rev;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	rev = musb->tusb_revision;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	pr_info("tusb: %s%i.%i %s%i.%i %s%i.%i %s%i.%i %s%i %s%i.%i\n",
7462306a36Sopenharmony_ci		"prcm",
7562306a36Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_PRCM_REV)),
7662306a36Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_PRCM_REV)),
7762306a36Sopenharmony_ci		"int",
7862306a36Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_INT_CTRL_REV)),
7962306a36Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_INT_CTRL_REV)),
8062306a36Sopenharmony_ci		"gpio",
8162306a36Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_GPIO_REV)),
8262306a36Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_GPIO_REV)),
8362306a36Sopenharmony_ci		"dma",
8462306a36Sopenharmony_ci		TUSB_REV_MAJOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)),
8562306a36Sopenharmony_ci		TUSB_REV_MINOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)),
8662306a36Sopenharmony_ci		"dieid",
8762306a36Sopenharmony_ci		TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, TUSB_DIDR1_HI)),
8862306a36Sopenharmony_ci		"rev",
8962306a36Sopenharmony_ci		TUSB_REV_MAJOR(rev), TUSB_REV_MINOR(rev));
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define WBUS_QUIRK_MASK	(TUSB_PHY_OTG_CTRL_TESTM2 | TUSB_PHY_OTG_CTRL_TESTM1 \
9362306a36Sopenharmony_ci				| TUSB_PHY_OTG_CTRL_TESTM0)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * Workaround for spontaneous WBUS wake-up issue #2 for tusb3.0.
9762306a36Sopenharmony_ci * Disables power detection in PHY for the duration of idle.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistatic void tusb_wbus_quirk(struct musb *musb, int enabled)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
10262306a36Sopenharmony_ci	static u32	phy_otg_ctrl, phy_otg_ena;
10362306a36Sopenharmony_ci	u32		tmp;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (enabled) {
10662306a36Sopenharmony_ci		phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
10762306a36Sopenharmony_ci		phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
10862306a36Sopenharmony_ci		tmp = TUSB_PHY_OTG_CTRL_WRPROTECT
10962306a36Sopenharmony_ci				| phy_otg_ena | WBUS_QUIRK_MASK;
11062306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp);
11162306a36Sopenharmony_ci		tmp = phy_otg_ena & ~WBUS_QUIRK_MASK;
11262306a36Sopenharmony_ci		tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2;
11362306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp);
11462306a36Sopenharmony_ci		dev_dbg(musb->controller, "Enabled tusb wbus quirk ctrl %08x ena %08x\n",
11562306a36Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL),
11662306a36Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE));
11762306a36Sopenharmony_ci	} else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)
11862306a36Sopenharmony_ci					& TUSB_PHY_OTG_CTRL_TESTM2) {
11962306a36Sopenharmony_ci		tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl;
12062306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp);
12162306a36Sopenharmony_ci		tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena;
12262306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp);
12362306a36Sopenharmony_ci		dev_dbg(musb->controller, "Disabled tusb wbus quirk ctrl %08x ena %08x\n",
12462306a36Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL),
12562306a36Sopenharmony_ci			musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE));
12662306a36Sopenharmony_ci		phy_otg_ctrl = 0;
12762306a36Sopenharmony_ci		phy_otg_ena = 0;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic u32 tusb_fifo_offset(u8 epnum)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	return 0x200 + (epnum * 0x20);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic u32 tusb_ep_offset(u8 epnum, u16 offset)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	return 0x10 + offset;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* TUSB mapping: "flat" plus ep0 special cases */
14262306a36Sopenharmony_cistatic void tusb_ep_select(void __iomem *mbase, u8 epnum)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	musb_writeb(mbase, MUSB_INDEX, epnum);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_cistatic u8 tusb_readb(void __iomem *addr, u32 offset)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	u16 tmp;
15362306a36Sopenharmony_ci	u8 val;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	tmp = __raw_readw(addr + (offset & ~1));
15662306a36Sopenharmony_ci	if (offset & 1)
15762306a36Sopenharmony_ci		val = (tmp >> 8);
15862306a36Sopenharmony_ci	else
15962306a36Sopenharmony_ci		val = tmp & 0xff;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return val;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void tusb_writeb(void __iomem *addr, u32 offset, u8 data)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u16 tmp;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	tmp = __raw_readw(addr + (offset & ~1));
16962306a36Sopenharmony_ci	if (offset & 1)
17062306a36Sopenharmony_ci		tmp = (data << 8) | (tmp & 0xff);
17162306a36Sopenharmony_ci	else
17262306a36Sopenharmony_ci		tmp = (tmp & 0xff00) | data;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	__raw_writew(tmp, addr + (offset & ~1));
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/*
17862306a36Sopenharmony_ci * TUSB 6010 may use a parallel bus that doesn't support byte ops;
17962306a36Sopenharmony_ci * so both loading and unloading FIFOs need explicit byte counts.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic inline void
18362306a36Sopenharmony_citusb_fifo_write_unaligned(void __iomem *fifo, const u8 *buf, u16 len)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	u32		val;
18662306a36Sopenharmony_ci	int		i;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (len > 4) {
18962306a36Sopenharmony_ci		for (i = 0; i < (len >> 2); i++) {
19062306a36Sopenharmony_ci			memcpy(&val, buf, 4);
19162306a36Sopenharmony_ci			musb_writel(fifo, 0, val);
19262306a36Sopenharmony_ci			buf += 4;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci		len %= 4;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	if (len > 0) {
19762306a36Sopenharmony_ci		/* Write the rest 1 - 3 bytes to FIFO */
19862306a36Sopenharmony_ci		val = 0;
19962306a36Sopenharmony_ci		memcpy(&val, buf, len);
20062306a36Sopenharmony_ci		musb_writel(fifo, 0, val);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic inline void tusb_fifo_read_unaligned(void __iomem *fifo,
20562306a36Sopenharmony_ci						void *buf, u16 len)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	u32		val;
20862306a36Sopenharmony_ci	int		i;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (len > 4) {
21162306a36Sopenharmony_ci		for (i = 0; i < (len >> 2); i++) {
21262306a36Sopenharmony_ci			val = musb_readl(fifo, 0);
21362306a36Sopenharmony_ci			memcpy(buf, &val, 4);
21462306a36Sopenharmony_ci			buf += 4;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci		len %= 4;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	if (len > 0) {
21962306a36Sopenharmony_ci		/* Read the rest 1 - 3 bytes from FIFO */
22062306a36Sopenharmony_ci		val = musb_readl(fifo, 0);
22162306a36Sopenharmony_ci		memcpy(buf, &val, len);
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void tusb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct musb *musb = hw_ep->musb;
22862306a36Sopenharmony_ci	void __iomem	*ep_conf = hw_ep->conf;
22962306a36Sopenharmony_ci	void __iomem	*fifo = hw_ep->fifo;
23062306a36Sopenharmony_ci	u8		epnum = hw_ep->epnum;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	prefetch(buf);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
23562306a36Sopenharmony_ci			'T', epnum, fifo, len, buf);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (epnum)
23862306a36Sopenharmony_ci		musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
23962306a36Sopenharmony_ci			TUSB_EP_CONFIG_XFR_SIZE(len));
24062306a36Sopenharmony_ci	else
24162306a36Sopenharmony_ci		musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_DIR_TX |
24262306a36Sopenharmony_ci			TUSB_EP0_CONFIG_XFR_SIZE(len));
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (likely((0x01 & (unsigned long) buf) == 0)) {
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		/* Best case is 32bit-aligned destination address */
24762306a36Sopenharmony_ci		if ((0x02 & (unsigned long) buf) == 0) {
24862306a36Sopenharmony_ci			if (len >= 4) {
24962306a36Sopenharmony_ci				iowrite32_rep(fifo, buf, len >> 2);
25062306a36Sopenharmony_ci				buf += (len & ~0x03);
25162306a36Sopenharmony_ci				len &= 0x03;
25262306a36Sopenharmony_ci			}
25362306a36Sopenharmony_ci		} else {
25462306a36Sopenharmony_ci			if (len >= 2) {
25562306a36Sopenharmony_ci				u32 val;
25662306a36Sopenharmony_ci				int i;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci				/* Cannot use writesw, fifo is 32-bit */
25962306a36Sopenharmony_ci				for (i = 0; i < (len >> 2); i++) {
26062306a36Sopenharmony_ci					val = (u32)(*(u16 *)buf);
26162306a36Sopenharmony_ci					buf += 2;
26262306a36Sopenharmony_ci					val |= (*(u16 *)buf) << 16;
26362306a36Sopenharmony_ci					buf += 2;
26462306a36Sopenharmony_ci					musb_writel(fifo, 0, val);
26562306a36Sopenharmony_ci				}
26662306a36Sopenharmony_ci				len &= 0x03;
26762306a36Sopenharmony_ci			}
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (len > 0)
27262306a36Sopenharmony_ci		tusb_fifo_write_unaligned(fifo, buf, len);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void tusb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct musb *musb = hw_ep->musb;
27862306a36Sopenharmony_ci	void __iomem	*ep_conf = hw_ep->conf;
27962306a36Sopenharmony_ci	void __iomem	*fifo = hw_ep->fifo;
28062306a36Sopenharmony_ci	u8		epnum = hw_ep->epnum;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
28362306a36Sopenharmony_ci			'R', epnum, fifo, len, buf);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (epnum)
28662306a36Sopenharmony_ci		musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
28762306a36Sopenharmony_ci			TUSB_EP_CONFIG_XFR_SIZE(len));
28862306a36Sopenharmony_ci	else
28962306a36Sopenharmony_ci		musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_XFR_SIZE(len));
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (likely((0x01 & (unsigned long) buf) == 0)) {
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* Best case is 32bit-aligned destination address */
29462306a36Sopenharmony_ci		if ((0x02 & (unsigned long) buf) == 0) {
29562306a36Sopenharmony_ci			if (len >= 4) {
29662306a36Sopenharmony_ci				ioread32_rep(fifo, buf, len >> 2);
29762306a36Sopenharmony_ci				buf += (len & ~0x03);
29862306a36Sopenharmony_ci				len &= 0x03;
29962306a36Sopenharmony_ci			}
30062306a36Sopenharmony_ci		} else {
30162306a36Sopenharmony_ci			if (len >= 2) {
30262306a36Sopenharmony_ci				u32 val;
30362306a36Sopenharmony_ci				int i;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci				/* Cannot use readsw, fifo is 32-bit */
30662306a36Sopenharmony_ci				for (i = 0; i < (len >> 2); i++) {
30762306a36Sopenharmony_ci					val = musb_readl(fifo, 0);
30862306a36Sopenharmony_ci					*(u16 *)buf = (u16)(val & 0xffff);
30962306a36Sopenharmony_ci					buf += 2;
31062306a36Sopenharmony_ci					*(u16 *)buf = (u16)(val >> 16);
31162306a36Sopenharmony_ci					buf += 2;
31262306a36Sopenharmony_ci				}
31362306a36Sopenharmony_ci				len &= 0x03;
31462306a36Sopenharmony_ci			}
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (len > 0)
31962306a36Sopenharmony_ci		tusb_fifo_read_unaligned(fifo, buf, len);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic struct musb *the_musb;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/* This is used by gadget drivers, and OTG transceiver logic, allowing
32562306a36Sopenharmony_ci * at most mA current to be drawn from VBUS during a Default-B session
32662306a36Sopenharmony_ci * (that is, while VBUS exceeds 4.4V).  In Default-A (including pure host
32762306a36Sopenharmony_ci * mode), or low power Default-B sessions, something else supplies power.
32862306a36Sopenharmony_ci * Caller must take care of locking.
32962306a36Sopenharmony_ci */
33062306a36Sopenharmony_cistatic int tusb_draw_power(struct usb_phy *x, unsigned mA)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct musb	*musb = the_musb;
33362306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
33462306a36Sopenharmony_ci	u32		reg;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* tps65030 seems to consume max 100mA, with maybe 60mA available
33762306a36Sopenharmony_ci	 * (measured on one board) for things other than tps and tusb.
33862306a36Sopenharmony_ci	 *
33962306a36Sopenharmony_ci	 * Boards sharing the CPU clock with CLKIN will need to prevent
34062306a36Sopenharmony_ci	 * certain idle sleep states while the USB link is active.
34162306a36Sopenharmony_ci	 *
34262306a36Sopenharmony_ci	 * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }.
34362306a36Sopenharmony_ci	 * The actual current usage would be very board-specific.  For now,
34462306a36Sopenharmony_ci	 * it's simpler to just use an aggregate (also board-specific).
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	if (x->otg->default_a || mA < (musb->min_power << 1))
34762306a36Sopenharmony_ci		mA = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PRCM_MNGMT);
35062306a36Sopenharmony_ci	if (mA) {
35162306a36Sopenharmony_ci		musb->is_bus_powered = 1;
35262306a36Sopenharmony_ci		reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN;
35362306a36Sopenharmony_ci	} else {
35462306a36Sopenharmony_ci		musb->is_bus_powered = 0;
35562306a36Sopenharmony_ci		reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT, reg);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	dev_dbg(musb->controller, "draw max %d mA VBUS\n", mA);
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/* workaround for issue 13:  change clock during chip idle
36462306a36Sopenharmony_ci * (to be fixed in rev3 silicon) ... symptoms include disconnect
36562306a36Sopenharmony_ci * or looping suspend/resume cycles
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_cistatic void tusb_set_clock_source(struct musb *musb, unsigned mode)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
37062306a36Sopenharmony_ci	u32		reg;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PRCM_CONF);
37362306a36Sopenharmony_ci	reg &= ~TUSB_PRCM_CONF_SYS_CLKSEL(0x3);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* 0 = refclk (clkin, XI)
37662306a36Sopenharmony_ci	 * 1 = PHY 60 MHz (internal PLL)
37762306a36Sopenharmony_ci	 * 2 = not supported
37862306a36Sopenharmony_ci	 * 3 = what?
37962306a36Sopenharmony_ci	 */
38062306a36Sopenharmony_ci	if (mode > 0)
38162306a36Sopenharmony_ci		reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_CONF, reg);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* FIXME tusb6010_platform_retime(mode == 0); */
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/*
38962306a36Sopenharmony_ci * Idle TUSB6010 until next wake-up event; NOR access always wakes.
39062306a36Sopenharmony_ci * Other code ensures that we idle unless we're connected _and_ the
39162306a36Sopenharmony_ci * USB link is not suspended ... and tells us the relevant wakeup
39262306a36Sopenharmony_ci * events.  SW_EN for voltage is handled separately.
39362306a36Sopenharmony_ci */
39462306a36Sopenharmony_cistatic void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
39762306a36Sopenharmony_ci	u32		reg;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if ((wakeup_enables & TUSB_PRCM_WBUS)
40062306a36Sopenharmony_ci			&& (musb->tusb_revision == TUSB_REV_30))
40162306a36Sopenharmony_ci		tusb_wbus_quirk(musb, 1);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	tusb_set_clock_source(musb, 0);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	wakeup_enables |= TUSB_PRCM_WNORCS;
40662306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* REVISIT writeup of WID implies that if WID set and ID is grounded,
40962306a36Sopenharmony_ci	 * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared.
41062306a36Sopenharmony_ci	 * Presumably that's mostly to save power, hence WID is immaterial ...
41162306a36Sopenharmony_ci	 */
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PRCM_MNGMT);
41462306a36Sopenharmony_ci	/* issue 4: when driving vbus, use hipower (vbus_det) comparator */
41562306a36Sopenharmony_ci	if (is_host_active(musb)) {
41662306a36Sopenharmony_ci		reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
41762306a36Sopenharmony_ci		reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
41862306a36Sopenharmony_ci	} else {
41962306a36Sopenharmony_ci		reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
42062306a36Sopenharmony_ci		reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci	reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE;
42362306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT, reg);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/*
42962306a36Sopenharmony_ci * Updates cable VBUS status. Caller must take care of locking.
43062306a36Sopenharmony_ci */
43162306a36Sopenharmony_cistatic int tusb_musb_vbus_status(struct musb *musb)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
43462306a36Sopenharmony_ci	u32		otg_stat, prcm_mngmt;
43562306a36Sopenharmony_ci	int		ret = 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
43862306a36Sopenharmony_ci	prcm_mngmt = musb_readl(tbase, TUSB_PRCM_MNGMT);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* Temporarily enable VBUS detection if it was disabled for
44162306a36Sopenharmony_ci	 * suspend mode. Unless it's enabled otg_stat and devctl will
44262306a36Sopenharmony_ci	 * not show correct VBUS state.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (!(prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) {
44562306a36Sopenharmony_ci		u32 tmp = prcm_mngmt;
44662306a36Sopenharmony_ci		tmp |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
44762306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PRCM_MNGMT, tmp);
44862306a36Sopenharmony_ci		otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
44962306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PRCM_MNGMT, prcm_mngmt);
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID)
45362306a36Sopenharmony_ci		ret = 1;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return ret;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void musb_do_idle(struct timer_list *t)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct musb	*musb = from_timer(musb, t, dev_timer);
46162306a36Sopenharmony_ci	unsigned long	flags;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	switch (musb->xceiv->otg->state) {
46662306a36Sopenharmony_ci	case OTG_STATE_A_WAIT_BCON:
46762306a36Sopenharmony_ci		if ((musb->a_wait_bcon != 0)
46862306a36Sopenharmony_ci			&& (musb->idle_timeout == 0
46962306a36Sopenharmony_ci				|| time_after(jiffies, musb->idle_timeout))) {
47062306a36Sopenharmony_ci			dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n",
47162306a36Sopenharmony_ci					usb_otg_state_string(musb->xceiv->otg->state));
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		fallthrough;
47462306a36Sopenharmony_ci	case OTG_STATE_A_IDLE:
47562306a36Sopenharmony_ci		tusb_musb_set_vbus(musb, 0);
47662306a36Sopenharmony_ci		break;
47762306a36Sopenharmony_ci	default:
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!musb->is_active) {
48262306a36Sopenharmony_ci		u32	wakeups;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		/* wait until hub_wq handles port change status */
48562306a36Sopenharmony_ci		if (is_host_active(musb) && (musb->port1_status >> 16))
48662306a36Sopenharmony_ci			goto done;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		if (!musb->gadget_driver) {
48962306a36Sopenharmony_ci			wakeups = 0;
49062306a36Sopenharmony_ci		} else {
49162306a36Sopenharmony_ci			wakeups = TUSB_PRCM_WHOSTDISCON
49262306a36Sopenharmony_ci				| TUSB_PRCM_WBUS
49362306a36Sopenharmony_ci					| TUSB_PRCM_WVBUS;
49462306a36Sopenharmony_ci			wakeups |= TUSB_PRCM_WID;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci		tusb_allow_idle(musb, wakeups);
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_cidone:
49962306a36Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/*
50362306a36Sopenharmony_ci * Maybe put TUSB6010 into idle mode depending on USB link status,
50462306a36Sopenharmony_ci * like "disconnected" or "suspended".  We'll be woken out of it by
50562306a36Sopenharmony_ci * connect, resume, or disconnect.
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * Needs to be called as the last function everywhere where there is
50862306a36Sopenharmony_ci * register access to TUSB6010 because of NOR flash wake-up.
50962306a36Sopenharmony_ci * Caller should own controller spinlock.
51062306a36Sopenharmony_ci *
51162306a36Sopenharmony_ci * Delay because peripheral enables D+ pullup 3msec after SE0, and
51262306a36Sopenharmony_ci * we don't want to treat that full speed J as a wakeup event.
51362306a36Sopenharmony_ci * ... peripherals must draw only suspend current after 10 msec.
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cistatic void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	unsigned long		default_timeout = jiffies + msecs_to_jiffies(3);
51862306a36Sopenharmony_ci	static unsigned long	last_timer;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (timeout == 0)
52162306a36Sopenharmony_ci		timeout = default_timeout;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Never idle if active, or when VBUS timeout is not set as host */
52462306a36Sopenharmony_ci	if (musb->is_active || ((musb->a_wait_bcon == 0)
52562306a36Sopenharmony_ci			&& (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) {
52662306a36Sopenharmony_ci		dev_dbg(musb->controller, "%s active, deleting timer\n",
52762306a36Sopenharmony_ci			usb_otg_state_string(musb->xceiv->otg->state));
52862306a36Sopenharmony_ci		del_timer(&musb->dev_timer);
52962306a36Sopenharmony_ci		last_timer = jiffies;
53062306a36Sopenharmony_ci		return;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (time_after(last_timer, timeout)) {
53462306a36Sopenharmony_ci		if (!timer_pending(&musb->dev_timer))
53562306a36Sopenharmony_ci			last_timer = timeout;
53662306a36Sopenharmony_ci		else {
53762306a36Sopenharmony_ci			dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n");
53862306a36Sopenharmony_ci			return;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci	last_timer = timeout;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
54462306a36Sopenharmony_ci		usb_otg_state_string(musb->xceiv->otg->state),
54562306a36Sopenharmony_ci		(unsigned long)jiffies_to_msecs(timeout - jiffies));
54662306a36Sopenharmony_ci	mod_timer(&musb->dev_timer, timeout);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci/* ticks of 60 MHz clock */
55062306a36Sopenharmony_ci#define DEVCLOCK		60000000
55162306a36Sopenharmony_ci#define OTG_TIMER_MS(msecs)	((msecs) \
55262306a36Sopenharmony_ci		? (TUSB_DEV_OTG_TIMER_VAL((DEVCLOCK/1000)*(msecs)) \
55362306a36Sopenharmony_ci				| TUSB_DEV_OTG_TIMER_ENABLE) \
55462306a36Sopenharmony_ci		: 0)
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
55962306a36Sopenharmony_ci	u32		conf, prcm, timer;
56062306a36Sopenharmony_ci	u8		devctl;
56162306a36Sopenharmony_ci	struct usb_otg	*otg = musb->xceiv->otg;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/* HDRC controls CPEN, but beware current surges during device
56462306a36Sopenharmony_ci	 * connect.  They can trigger transient overcurrent conditions
56562306a36Sopenharmony_ci	 * that must be ignored.
56662306a36Sopenharmony_ci	 */
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	prcm = musb_readl(tbase, TUSB_PRCM_MNGMT);
56962306a36Sopenharmony_ci	conf = musb_readl(tbase, TUSB_DEV_CONF);
57062306a36Sopenharmony_ci	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (is_on) {
57362306a36Sopenharmony_ci		timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
57462306a36Sopenharmony_ci		otg->default_a = 1;
57562306a36Sopenharmony_ci		musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
57662306a36Sopenharmony_ci		devctl |= MUSB_DEVCTL_SESSION;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		conf |= TUSB_DEV_CONF_USB_HOST_MODE;
57962306a36Sopenharmony_ci		MUSB_HST_MODE(musb);
58062306a36Sopenharmony_ci	} else {
58162306a36Sopenharmony_ci		u32	otg_stat;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		timer = 0;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		/* If ID pin is grounded, we want to be a_idle */
58662306a36Sopenharmony_ci		otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
58762306a36Sopenharmony_ci		if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
58862306a36Sopenharmony_ci			switch (musb->xceiv->otg->state) {
58962306a36Sopenharmony_ci			case OTG_STATE_A_WAIT_VRISE:
59062306a36Sopenharmony_ci			case OTG_STATE_A_WAIT_BCON:
59162306a36Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
59262306a36Sopenharmony_ci				break;
59362306a36Sopenharmony_ci			case OTG_STATE_A_WAIT_VFALL:
59462306a36Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_IDLE;
59562306a36Sopenharmony_ci				break;
59662306a36Sopenharmony_ci			default:
59762306a36Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_IDLE;
59862306a36Sopenharmony_ci			}
59962306a36Sopenharmony_ci			musb->is_active = 0;
60062306a36Sopenharmony_ci			otg->default_a = 1;
60162306a36Sopenharmony_ci			MUSB_HST_MODE(musb);
60262306a36Sopenharmony_ci		} else {
60362306a36Sopenharmony_ci			musb->is_active = 0;
60462306a36Sopenharmony_ci			otg->default_a = 0;
60562306a36Sopenharmony_ci			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
60662306a36Sopenharmony_ci			MUSB_DEV_MODE(musb);
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		devctl &= ~MUSB_DEVCTL_SESSION;
61062306a36Sopenharmony_ci		conf &= ~TUSB_DEV_CONF_USB_HOST_MODE;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci	prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT, prcm);
61562306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DEV_OTG_TIMER, timer);
61662306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DEV_CONF, conf);
61762306a36Sopenharmony_ci	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n",
62062306a36Sopenharmony_ci		usb_otg_state_string(musb->xceiv->otg->state),
62162306a36Sopenharmony_ci		musb_readb(musb->mregs, MUSB_DEVCTL),
62262306a36Sopenharmony_ci		musb_readl(tbase, TUSB_DEV_OTG_STAT),
62362306a36Sopenharmony_ci		conf, prcm);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/*
62762306a36Sopenharmony_ci * Sets the mode to OTG, peripheral or host by changing the ID detection.
62862306a36Sopenharmony_ci * Caller must take care of locking.
62962306a36Sopenharmony_ci *
63062306a36Sopenharmony_ci * Note that if a mini-A cable is plugged in the ID line will stay down as
63162306a36Sopenharmony_ci * the weak ID pull-up is not able to pull the ID up.
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_cistatic int tusb_musb_set_mode(struct musb *musb, u8 musb_mode)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
63662306a36Sopenharmony_ci	u32		otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
63962306a36Sopenharmony_ci	phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
64062306a36Sopenharmony_ci	phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
64162306a36Sopenharmony_ci	dev_conf = musb_readl(tbase, TUSB_DEV_CONF);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	switch (musb_mode) {
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	case MUSB_HOST:		/* Disable PHY ID detect, ground ID */
64662306a36Sopenharmony_ci		phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
64762306a36Sopenharmony_ci		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
64862306a36Sopenharmony_ci		dev_conf |= TUSB_DEV_CONF_ID_SEL;
64962306a36Sopenharmony_ci		dev_conf &= ~TUSB_DEV_CONF_SOFT_ID;
65062306a36Sopenharmony_ci		break;
65162306a36Sopenharmony_ci	case MUSB_PERIPHERAL:	/* Disable PHY ID detect, keep ID pull-up on */
65262306a36Sopenharmony_ci		phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
65362306a36Sopenharmony_ci		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
65462306a36Sopenharmony_ci		dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
65562306a36Sopenharmony_ci		break;
65662306a36Sopenharmony_ci	case MUSB_OTG:		/* Use PHY ID detection */
65762306a36Sopenharmony_ci		phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
65862306a36Sopenharmony_ci		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
65962306a36Sopenharmony_ci		dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
66062306a36Sopenharmony_ci		break;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	default:
66362306a36Sopenharmony_ci		dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode);
66462306a36Sopenharmony_ci		return -EINVAL;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL,
66862306a36Sopenharmony_ci			TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl);
66962306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE,
67062306a36Sopenharmony_ci			TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena);
67162306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DEV_CONF, dev_conf);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
67462306a36Sopenharmony_ci	if ((musb_mode == MUSB_PERIPHERAL) &&
67562306a36Sopenharmony_ci		!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS))
67662306a36Sopenharmony_ci			INFO("Cannot be peripheral with mini-A cable "
67762306a36Sopenharmony_ci			"otg_stat: %08x\n", otg_stat);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic inline unsigned long
68362306a36Sopenharmony_citusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	u32		otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
68662306a36Sopenharmony_ci	unsigned long	idle_timeout = 0;
68762306a36Sopenharmony_ci	struct usb_otg	*otg = musb->xceiv->otg;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* ID pin */
69062306a36Sopenharmony_ci	if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) {
69162306a36Sopenharmony_ci		int	default_a;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
69462306a36Sopenharmony_ci		dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B');
69562306a36Sopenharmony_ci		otg->default_a = default_a;
69662306a36Sopenharmony_ci		tusb_musb_set_vbus(musb, default_a);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		/* Don't allow idling immediately */
69962306a36Sopenharmony_ci		if (default_a)
70062306a36Sopenharmony_ci			idle_timeout = jiffies + (HZ * 3);
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* VBUS state change */
70462306a36Sopenharmony_ci	if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		/* B-dev state machine:  no vbus ~= disconnect */
70762306a36Sopenharmony_ci		if (!otg->default_a) {
70862306a36Sopenharmony_ci			/* ? musb_root_disconnect(musb); */
70962306a36Sopenharmony_ci			musb->port1_status &=
71062306a36Sopenharmony_ci				~(USB_PORT_STAT_CONNECTION
71162306a36Sopenharmony_ci				| USB_PORT_STAT_ENABLE
71262306a36Sopenharmony_ci				| USB_PORT_STAT_LOW_SPEED
71362306a36Sopenharmony_ci				| USB_PORT_STAT_HIGH_SPEED
71462306a36Sopenharmony_ci				| USB_PORT_STAT_TEST
71562306a36Sopenharmony_ci				);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci			if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
71862306a36Sopenharmony_ci				dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n");
71962306a36Sopenharmony_ci				if (musb->xceiv->otg->state != OTG_STATE_B_IDLE) {
72062306a36Sopenharmony_ci					/* INTR_DISCONNECT can hide... */
72162306a36Sopenharmony_ci					musb->xceiv->otg->state = OTG_STATE_B_IDLE;
72262306a36Sopenharmony_ci					musb->int_usb |= MUSB_INTR_DISCONNECT;
72362306a36Sopenharmony_ci				}
72462306a36Sopenharmony_ci				musb->is_active = 0;
72562306a36Sopenharmony_ci			}
72662306a36Sopenharmony_ci			dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
72762306a36Sopenharmony_ci				usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
72862306a36Sopenharmony_ci			idle_timeout = jiffies + (1 * HZ);
72962306a36Sopenharmony_ci			schedule_delayed_work(&musb->irq_work, 0);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		} else /* A-dev state machine */ {
73262306a36Sopenharmony_ci			dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
73362306a36Sopenharmony_ci				usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci			switch (musb->xceiv->otg->state) {
73662306a36Sopenharmony_ci			case OTG_STATE_A_IDLE:
73762306a36Sopenharmony_ci				dev_dbg(musb->controller, "Got SRP, turning on VBUS\n");
73862306a36Sopenharmony_ci				musb_platform_set_vbus(musb, 1);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci				/* CONNECT can wake if a_wait_bcon is set */
74162306a36Sopenharmony_ci				if (musb->a_wait_bcon != 0)
74262306a36Sopenharmony_ci					musb->is_active = 0;
74362306a36Sopenharmony_ci				else
74462306a36Sopenharmony_ci					musb->is_active = 1;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci				/*
74762306a36Sopenharmony_ci				 * OPT FS A TD.4.6 needs few seconds for
74862306a36Sopenharmony_ci				 * A_WAIT_VRISE
74962306a36Sopenharmony_ci				 */
75062306a36Sopenharmony_ci				idle_timeout = jiffies + (2 * HZ);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci				break;
75362306a36Sopenharmony_ci			case OTG_STATE_A_WAIT_VRISE:
75462306a36Sopenharmony_ci				/* ignore; A-session-valid < VBUS_VALID/2,
75562306a36Sopenharmony_ci				 * we monitor this with the timer
75662306a36Sopenharmony_ci				 */
75762306a36Sopenharmony_ci				break;
75862306a36Sopenharmony_ci			case OTG_STATE_A_WAIT_VFALL:
75962306a36Sopenharmony_ci				/* REVISIT this irq triggers during short
76062306a36Sopenharmony_ci				 * spikes caused by enumeration ...
76162306a36Sopenharmony_ci				 */
76262306a36Sopenharmony_ci				if (musb->vbuserr_retry) {
76362306a36Sopenharmony_ci					musb->vbuserr_retry--;
76462306a36Sopenharmony_ci					tusb_musb_set_vbus(musb, 1);
76562306a36Sopenharmony_ci				} else {
76662306a36Sopenharmony_ci					musb->vbuserr_retry
76762306a36Sopenharmony_ci						= VBUSERR_RETRY_COUNT;
76862306a36Sopenharmony_ci					tusb_musb_set_vbus(musb, 0);
76962306a36Sopenharmony_ci				}
77062306a36Sopenharmony_ci				break;
77162306a36Sopenharmony_ci			default:
77262306a36Sopenharmony_ci				break;
77362306a36Sopenharmony_ci			}
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* OTG timer expiration */
77862306a36Sopenharmony_ci	if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) {
77962306a36Sopenharmony_ci		u8	devctl;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		dev_dbg(musb->controller, "%s timer, %03x\n",
78262306a36Sopenharmony_ci			usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		switch (musb->xceiv->otg->state) {
78562306a36Sopenharmony_ci		case OTG_STATE_A_WAIT_VRISE:
78662306a36Sopenharmony_ci			/* VBUS has probably been valid for a while now,
78762306a36Sopenharmony_ci			 * but may well have bounced out of range a bit
78862306a36Sopenharmony_ci			 */
78962306a36Sopenharmony_ci			devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
79062306a36Sopenharmony_ci			if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
79162306a36Sopenharmony_ci				if ((devctl & MUSB_DEVCTL_VBUS)
79262306a36Sopenharmony_ci						!= MUSB_DEVCTL_VBUS) {
79362306a36Sopenharmony_ci					dev_dbg(musb->controller, "devctl %02x\n", devctl);
79462306a36Sopenharmony_ci					break;
79562306a36Sopenharmony_ci				}
79662306a36Sopenharmony_ci				musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
79762306a36Sopenharmony_ci				musb->is_active = 0;
79862306a36Sopenharmony_ci				idle_timeout = jiffies
79962306a36Sopenharmony_ci					+ msecs_to_jiffies(musb->a_wait_bcon);
80062306a36Sopenharmony_ci			} else {
80162306a36Sopenharmony_ci				/* REVISIT report overcurrent to hub? */
80262306a36Sopenharmony_ci				ERR("vbus too slow, devctl %02x\n", devctl);
80362306a36Sopenharmony_ci				tusb_musb_set_vbus(musb, 0);
80462306a36Sopenharmony_ci			}
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		case OTG_STATE_A_WAIT_BCON:
80762306a36Sopenharmony_ci			if (musb->a_wait_bcon != 0)
80862306a36Sopenharmony_ci				idle_timeout = jiffies
80962306a36Sopenharmony_ci					+ msecs_to_jiffies(musb->a_wait_bcon);
81062306a36Sopenharmony_ci			break;
81162306a36Sopenharmony_ci		case OTG_STATE_A_SUSPEND:
81262306a36Sopenharmony_ci			break;
81362306a36Sopenharmony_ci		case OTG_STATE_B_WAIT_ACON:
81462306a36Sopenharmony_ci			break;
81562306a36Sopenharmony_ci		default:
81662306a36Sopenharmony_ci			break;
81762306a36Sopenharmony_ci		}
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci	schedule_delayed_work(&musb->irq_work, 0);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return idle_timeout;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct musb	*musb = __hci;
82762306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
82862306a36Sopenharmony_ci	unsigned long	flags, idle_timeout = 0;
82962306a36Sopenharmony_ci	u32		int_mask, int_src;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* Mask all interrupts to allow using both edge and level GPIO irq */
83462306a36Sopenharmony_ci	int_mask = musb_readl(tbase, TUSB_INT_MASK);
83562306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS;
83862306a36Sopenharmony_ci	dev_dbg(musb->controller, "TUSB IRQ %08x\n", int_src);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	musb->int_usb = (u8) int_src;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/* Acknowledge wake-up source interrupts */
84362306a36Sopenharmony_ci	if (int_src & TUSB_INT_SRC_DEV_WAKEUP) {
84462306a36Sopenharmony_ci		u32	reg;
84562306a36Sopenharmony_ci		u32	i;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		if (musb->tusb_revision == TUSB_REV_30)
84862306a36Sopenharmony_ci			tusb_wbus_quirk(musb, 0);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		/* there are issues re-locking the PLL on wakeup ... */
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		/* work around issue 8 */
85362306a36Sopenharmony_ci		for (i = 0xf7f7f7; i > 0xf7f7f7 - 1000; i--) {
85462306a36Sopenharmony_ci			musb_writel(tbase, TUSB_SCRATCH_PAD, 0);
85562306a36Sopenharmony_ci			musb_writel(tbase, TUSB_SCRATCH_PAD, i);
85662306a36Sopenharmony_ci			reg = musb_readl(tbase, TUSB_SCRATCH_PAD);
85762306a36Sopenharmony_ci			if (reg == i)
85862306a36Sopenharmony_ci				break;
85962306a36Sopenharmony_ci			dev_dbg(musb->controller, "TUSB NOR not ready\n");
86062306a36Sopenharmony_ci		}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		/* work around issue 13 (2nd half) */
86362306a36Sopenharmony_ci		tusb_set_clock_source(musb, 1);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		reg = musb_readl(tbase, TUSB_PRCM_WAKEUP_SOURCE);
86662306a36Sopenharmony_ci		musb_writel(tbase, TUSB_PRCM_WAKEUP_CLEAR, reg);
86762306a36Sopenharmony_ci		if (reg & ~TUSB_PRCM_WNORCS) {
86862306a36Sopenharmony_ci			musb->is_active = 1;
86962306a36Sopenharmony_ci			schedule_delayed_work(&musb->irq_work, 0);
87062306a36Sopenharmony_ci		}
87162306a36Sopenharmony_ci		dev_dbg(musb->controller, "wake %sactive %02x\n",
87262306a36Sopenharmony_ci				musb->is_active ? "" : "in", reg);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		/* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (int_src & TUSB_INT_SRC_USB_IP_CONN)
87862306a36Sopenharmony_ci		del_timer(&musb->dev_timer);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	/* OTG state change reports (annoyingly) not issued by Mentor core */
88162306a36Sopenharmony_ci	if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG
88262306a36Sopenharmony_ci				| TUSB_INT_SRC_OTG_TIMEOUT
88362306a36Sopenharmony_ci				| TUSB_INT_SRC_ID_STATUS_CHNG))
88462306a36Sopenharmony_ci		idle_timeout = tusb_otg_ints(musb, int_src, tbase);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/*
88762306a36Sopenharmony_ci	 * Just clear the DMA interrupt if it comes as the completion for both
88862306a36Sopenharmony_ci	 * TX and RX is handled by the DMA callback in tusb6010_omap
88962306a36Sopenharmony_ci	 */
89062306a36Sopenharmony_ci	if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) {
89162306a36Sopenharmony_ci		u32	dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src);
89462306a36Sopenharmony_ci		musb_writel(tbase, TUSB_DMA_INT_CLEAR, dma_src);
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* EP interrupts. In OCP mode tusb6010 mirrors the MUSB interrupts */
89862306a36Sopenharmony_ci	if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX)) {
89962306a36Sopenharmony_ci		u32	musb_src = musb_readl(tbase, TUSB_USBIP_INT_SRC);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		musb_writel(tbase, TUSB_USBIP_INT_CLEAR, musb_src);
90262306a36Sopenharmony_ci		musb->int_rx = (((musb_src >> 16) & 0xffff) << 1);
90362306a36Sopenharmony_ci		musb->int_tx = (musb_src & 0xffff);
90462306a36Sopenharmony_ci	} else {
90562306a36Sopenharmony_ci		musb->int_rx = 0;
90662306a36Sopenharmony_ci		musb->int_tx = 0;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff))
91062306a36Sopenharmony_ci		musb_interrupt(musb);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/* Acknowledge TUSB interrupts. Clear only non-reserved bits */
91362306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_SRC_CLEAR,
91462306a36Sopenharmony_ci		int_src & ~TUSB_INT_MASK_RESERVED_BITS);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	tusb_musb_try_idle(musb, idle_timeout);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, int_mask);
91962306a36Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return IRQ_HANDLED;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic int dma_off;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/*
92762306a36Sopenharmony_ci * Enables TUSB6010. Caller must take care of locking.
92862306a36Sopenharmony_ci * REVISIT:
92962306a36Sopenharmony_ci * - Check what is unnecessary in MGC_HdrcStart()
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_cistatic void tusb_musb_enable(struct musb *musb)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* Setup TUSB6010 main interrupt mask. Enable all interrupts except SOF.
93662306a36Sopenharmony_ci	 * REVISIT: Enable and deal with TUSB_INT_SRC_USB_IP_SOF */
93762306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, TUSB_INT_SRC_USB_IP_SOF);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* Setup TUSB interrupt, disable DMA and GPIO interrupts */
94062306a36Sopenharmony_ci	musb_writel(tbase, TUSB_USBIP_INT_MASK, 0);
94162306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff);
94262306a36Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* Clear all subsystem interrups */
94562306a36Sopenharmony_ci	musb_writel(tbase, TUSB_USBIP_INT_CLEAR, 0x7fffffff);
94662306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_INT_CLEAR, 0x7fffffff);
94762306a36Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_INT_CLEAR, 0x1ff);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	/* Acknowledge pending interrupt(s) */
95062306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_SRC_CLEAR, ~TUSB_INT_MASK_RESERVED_BITS);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* Only 0 clock cycles for minimum interrupt de-assertion time and
95362306a36Sopenharmony_ci	 * interrupt polarity active low seems to work reliably here */
95462306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_CTRL_CONF,
95562306a36Sopenharmony_ci			TUSB_INT_CTRL_CONF_INT_RELCYC(0));
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	irq_set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* maybe force into the Default-A OTG state machine */
96062306a36Sopenharmony_ci	if (!(musb_readl(tbase, TUSB_DEV_OTG_STAT)
96162306a36Sopenharmony_ci			& TUSB_DEV_OTG_STAT_ID_STATUS))
96262306a36Sopenharmony_ci		musb_writel(tbase, TUSB_INT_SRC_SET,
96362306a36Sopenharmony_ci				TUSB_INT_SRC_ID_STATUS_CHNG);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (is_dma_capable() && dma_off)
96662306a36Sopenharmony_ci		printk(KERN_WARNING "%s %s: dma not reactivated\n",
96762306a36Sopenharmony_ci				__FILE__, __func__);
96862306a36Sopenharmony_ci	else
96962306a36Sopenharmony_ci		dma_off = 1;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci/*
97362306a36Sopenharmony_ci * Disables TUSB6010. Caller must take care of locking.
97462306a36Sopenharmony_ci */
97562306a36Sopenharmony_cistatic void tusb_musb_disable(struct musb *musb)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* FIXME stop DMA, IRQs, timers, ... */
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	/* disable all IRQs */
98262306a36Sopenharmony_ci	musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS);
98362306a36Sopenharmony_ci	musb_writel(tbase, TUSB_USBIP_INT_MASK, 0x7fffffff);
98462306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff);
98562306a36Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	del_timer(&musb->dev_timer);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (is_dma_capable() && !dma_off) {
99062306a36Sopenharmony_ci		printk(KERN_WARNING "%s %s: dma still active\n",
99162306a36Sopenharmony_ci				__FILE__, __func__);
99262306a36Sopenharmony_ci		dma_off = 1;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci/*
99762306a36Sopenharmony_ci * Sets up TUSB6010 CPU interface specific signals and registers
99862306a36Sopenharmony_ci * Note: Settings optimized for OMAP24xx
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_cistatic void tusb_setup_cpu_interface(struct musb *musb)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/*
100562306a36Sopenharmony_ci	 * Disable GPIO[5:0] pullups (used as output DMA requests)
100662306a36Sopenharmony_ci	 * Don't disable GPIO[7:6] as they are needed for wake-up.
100762306a36Sopenharmony_ci	 */
100862306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PULLUP_1_CTRL, 0x0000003F);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* Disable all pullups on NOR IF, DMAREQ0 and DMAREQ1 */
101162306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PULLUP_2_CTRL, 0x01FFFFFF);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/* Turn GPIO[5:0] to DMAREQ[5:0] signals */
101462306a36Sopenharmony_ci	musb_writel(tbase, TUSB_GPIO_CONF, TUSB_GPIO_CONF_DMAREQ(0x3f));
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/* Burst size 16x16 bits, all six DMA requests enabled, DMA request
101762306a36Sopenharmony_ci	 * de-assertion time 2 system clocks p 62 */
101862306a36Sopenharmony_ci	musb_writel(tbase, TUSB_DMA_REQ_CONF,
101962306a36Sopenharmony_ci		TUSB_DMA_REQ_CONF_BURST_SIZE(2) |
102062306a36Sopenharmony_ci		TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) |
102162306a36Sopenharmony_ci		TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Set 0 wait count for synchronous burst access */
102462306a36Sopenharmony_ci	musb_writel(tbase, TUSB_WAIT_COUNT, 1);
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic int tusb_musb_start(struct musb *musb)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent);
103062306a36Sopenharmony_ci	void __iomem	*tbase = musb->ctrl_base;
103162306a36Sopenharmony_ci	unsigned long	flags;
103262306a36Sopenharmony_ci	u32		reg;
103362306a36Sopenharmony_ci	int		ret;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/*
103662306a36Sopenharmony_ci	 * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
103762306a36Sopenharmony_ci	 * 1.5 V voltage regulators of PM companion chip. Companion chip will then
103862306a36Sopenharmony_ci	 * provide then PGOOD signal to TUSB6010 which will release it from reset.
103962306a36Sopenharmony_ci	 */
104062306a36Sopenharmony_ci	gpiod_set_value(glue->enable, 1);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* Wait for 100ms until TUSB6010 pulls INT pin down */
104362306a36Sopenharmony_ci	ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true,
104462306a36Sopenharmony_ci				glue->intpin);
104562306a36Sopenharmony_ci	if (ret) {
104662306a36Sopenharmony_ci		pr_err("tusb: Powerup response failed\n");
104762306a36Sopenharmony_ci		return ret;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (musb_readl(tbase, TUSB_PROD_TEST_RESET) !=
105362306a36Sopenharmony_ci		TUSB_PROD_TEST_RESET_VAL) {
105462306a36Sopenharmony_ci		printk(KERN_ERR "tusb: Unable to detect TUSB6010\n");
105562306a36Sopenharmony_ci		goto err;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	musb->tusb_revision = tusb_get_revision(musb);
105962306a36Sopenharmony_ci	tusb_print_revision(musb);
106062306a36Sopenharmony_ci	if (musb->tusb_revision < 2) {
106162306a36Sopenharmony_ci		printk(KERN_ERR "tusb: Unsupported TUSB6010 revision %i\n",
106262306a36Sopenharmony_ci				musb->tusb_revision);
106362306a36Sopenharmony_ci		goto err;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* The uint bit for "USB non-PDR interrupt enable" has to be 1 when
106762306a36Sopenharmony_ci	 * NOR FLASH interface is used */
106862306a36Sopenharmony_ci	musb_writel(tbase, TUSB_VLYNQ_CTRL, 8);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* Select PHY free running 60MHz as a system clock */
107162306a36Sopenharmony_ci	tusb_set_clock_source(musb, 1);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* VBus valid timer 1us, disable DFT/Debug and VLYNQ clocks for
107462306a36Sopenharmony_ci	 * power saving, enable VBus detect and session end comparators,
107562306a36Sopenharmony_ci	 * enable IDpullup, enable VBus charging */
107662306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PRCM_MNGMT,
107762306a36Sopenharmony_ci		TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(0xa) |
107862306a36Sopenharmony_ci		TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN |
107962306a36Sopenharmony_ci		TUSB_PRCM_MNGMT_OTG_SESS_END_EN |
108062306a36Sopenharmony_ci		TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN |
108162306a36Sopenharmony_ci		TUSB_PRCM_MNGMT_OTG_ID_PULLUP);
108262306a36Sopenharmony_ci	tusb_setup_cpu_interface(musb);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* simplify:  always sense/pullup ID pins, as if in OTG mode */
108562306a36Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
108662306a36Sopenharmony_ci	reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
108762306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, reg);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
109062306a36Sopenharmony_ci	reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
109162306a36Sopenharmony_ci	musb_writel(tbase, TUSB_PHY_OTG_CTRL, reg);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cierr:
109862306a36Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	gpiod_set_value(glue->enable, 0);
110162306a36Sopenharmony_ci	msleep(10);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return -ENODEV;
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic int tusb_musb_init(struct musb *musb)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct platform_device	*pdev;
110962306a36Sopenharmony_ci	struct resource		*mem;
111062306a36Sopenharmony_ci	void __iomem		*sync = NULL;
111162306a36Sopenharmony_ci	int			ret;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
111462306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(musb->xceiv))
111562306a36Sopenharmony_ci		return -EPROBE_DEFER;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	pdev = to_platform_device(musb->controller);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	/* dma address for async dma */
112062306a36Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
112162306a36Sopenharmony_ci	if (!mem) {
112262306a36Sopenharmony_ci		pr_debug("no async dma resource?\n");
112362306a36Sopenharmony_ci		ret = -ENODEV;
112462306a36Sopenharmony_ci		goto done;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci	musb->async = mem->start;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	/* dma address for sync dma */
112962306a36Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
113062306a36Sopenharmony_ci	if (!mem) {
113162306a36Sopenharmony_ci		pr_debug("no sync dma resource?\n");
113262306a36Sopenharmony_ci		ret = -ENODEV;
113362306a36Sopenharmony_ci		goto done;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci	musb->sync = mem->start;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	sync = ioremap(mem->start, resource_size(mem));
113862306a36Sopenharmony_ci	if (!sync) {
113962306a36Sopenharmony_ci		pr_debug("ioremap for sync failed\n");
114062306a36Sopenharmony_ci		ret = -ENOMEM;
114162306a36Sopenharmony_ci		goto done;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci	musb->sync_va = sync;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* Offsets from base: VLYNQ at 0x000, MUSB regs at 0x400,
114662306a36Sopenharmony_ci	 * FIFOs at 0x600, TUSB at 0x800
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci	musb->mregs += TUSB_BASE_OFFSET;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	ret = tusb_musb_start(musb);
115162306a36Sopenharmony_ci	if (ret) {
115262306a36Sopenharmony_ci		printk(KERN_ERR "Could not start tusb6010 (%d)\n",
115362306a36Sopenharmony_ci				ret);
115462306a36Sopenharmony_ci		goto done;
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci	musb->isr = tusb_musb_interrupt;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	musb->xceiv->set_power = tusb_draw_power;
115962306a36Sopenharmony_ci	the_musb = musb;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	timer_setup(&musb->dev_timer, musb_do_idle, 0);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cidone:
116462306a36Sopenharmony_ci	if (ret < 0) {
116562306a36Sopenharmony_ci		if (sync)
116662306a36Sopenharmony_ci			iounmap(sync);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		usb_put_phy(musb->xceiv);
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci	return ret;
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic int tusb_musb_exit(struct musb *musb)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	del_timer_sync(&musb->dev_timer);
117862306a36Sopenharmony_ci	the_musb = NULL;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	gpiod_set_value(glue->enable, 0);
118162306a36Sopenharmony_ci	msleep(10);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	iounmap(musb->sync_va);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	usb_put_phy(musb->xceiv);
118662306a36Sopenharmony_ci	return 0;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic const struct musb_platform_ops tusb_ops = {
119062306a36Sopenharmony_ci	.quirks		= MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB |
119162306a36Sopenharmony_ci			  MUSB_G_NO_SKB_RESERVE,
119262306a36Sopenharmony_ci	.init		= tusb_musb_init,
119362306a36Sopenharmony_ci	.exit		= tusb_musb_exit,
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	.ep_offset	= tusb_ep_offset,
119662306a36Sopenharmony_ci	.ep_select	= tusb_ep_select,
119762306a36Sopenharmony_ci	.fifo_offset	= tusb_fifo_offset,
119862306a36Sopenharmony_ci	.readb		= tusb_readb,
119962306a36Sopenharmony_ci	.writeb		= tusb_writeb,
120062306a36Sopenharmony_ci	.read_fifo	= tusb_read_fifo,
120162306a36Sopenharmony_ci	.write_fifo	= tusb_write_fifo,
120262306a36Sopenharmony_ci#ifdef CONFIG_USB_TUSB_OMAP_DMA
120362306a36Sopenharmony_ci	.dma_init	= tusb_dma_controller_create,
120462306a36Sopenharmony_ci	.dma_exit	= tusb_dma_controller_destroy,
120562306a36Sopenharmony_ci#endif
120662306a36Sopenharmony_ci	.enable		= tusb_musb_enable,
120762306a36Sopenharmony_ci	.disable	= tusb_musb_disable,
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	.set_mode	= tusb_musb_set_mode,
121062306a36Sopenharmony_ci	.try_idle	= tusb_musb_try_idle,
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	.vbus_status	= tusb_musb_vbus_status,
121362306a36Sopenharmony_ci	.set_vbus	= tusb_musb_set_vbus,
121462306a36Sopenharmony_ci};
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic const struct platform_device_info tusb_dev_info = {
121762306a36Sopenharmony_ci	.name		= "musb-hdrc",
121862306a36Sopenharmony_ci	.id		= PLATFORM_DEVID_AUTO,
121962306a36Sopenharmony_ci	.dma_mask	= DMA_BIT_MASK(32),
122062306a36Sopenharmony_ci};
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_cistatic int tusb_probe(struct platform_device *pdev)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	struct resource musb_resources[3];
122562306a36Sopenharmony_ci	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev);
122662306a36Sopenharmony_ci	struct platform_device		*musb;
122762306a36Sopenharmony_ci	struct tusb6010_glue		*glue;
122862306a36Sopenharmony_ci	struct platform_device_info	pinfo;
122962306a36Sopenharmony_ci	int				ret;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
123262306a36Sopenharmony_ci	if (!glue)
123362306a36Sopenharmony_ci		return -ENOMEM;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	glue->dev			= &pdev->dev;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	glue->enable = devm_gpiod_get(glue->dev, "enable", GPIOD_OUT_LOW);
123862306a36Sopenharmony_ci	if (IS_ERR(glue->enable))
123962306a36Sopenharmony_ci		return dev_err_probe(glue->dev, PTR_ERR(glue->enable),
124062306a36Sopenharmony_ci				     "could not obtain power on/off GPIO\n");
124162306a36Sopenharmony_ci	glue->intpin = devm_gpiod_get(glue->dev, "int", GPIOD_IN);
124262306a36Sopenharmony_ci	if (IS_ERR(glue->intpin))
124362306a36Sopenharmony_ci		return dev_err_probe(glue->dev, PTR_ERR(glue->intpin),
124462306a36Sopenharmony_ci				     "could not obtain INT GPIO\n");
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	pdata->platform_ops		= &tusb_ops;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	usb_phy_generic_register();
124962306a36Sopenharmony_ci	platform_set_drvdata(pdev, glue);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	memset(musb_resources, 0x00, sizeof(*musb_resources) *
125262306a36Sopenharmony_ci			ARRAY_SIZE(musb_resources));
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	musb_resources[0].name = pdev->resource[0].name;
125562306a36Sopenharmony_ci	musb_resources[0].start = pdev->resource[0].start;
125662306a36Sopenharmony_ci	musb_resources[0].end = pdev->resource[0].end;
125762306a36Sopenharmony_ci	musb_resources[0].flags = pdev->resource[0].flags;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	musb_resources[1].name = pdev->resource[1].name;
126062306a36Sopenharmony_ci	musb_resources[1].start = pdev->resource[1].start;
126162306a36Sopenharmony_ci	musb_resources[1].end = pdev->resource[1].end;
126262306a36Sopenharmony_ci	musb_resources[1].flags = pdev->resource[1].flags;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	musb_resources[2] = DEFINE_RES_IRQ_NAMED(gpiod_to_irq(glue->intpin), "mc");
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	pinfo = tusb_dev_info;
126762306a36Sopenharmony_ci	pinfo.parent = &pdev->dev;
126862306a36Sopenharmony_ci	pinfo.res = musb_resources;
126962306a36Sopenharmony_ci	pinfo.num_res = ARRAY_SIZE(musb_resources);
127062306a36Sopenharmony_ci	pinfo.data = pdata;
127162306a36Sopenharmony_ci	pinfo.size_data = sizeof(*pdata);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	glue->musb = musb = platform_device_register_full(&pinfo);
127462306a36Sopenharmony_ci	if (IS_ERR(musb)) {
127562306a36Sopenharmony_ci		ret = PTR_ERR(musb);
127662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
127762306a36Sopenharmony_ci		return ret;
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return 0;
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic void tusb_remove(struct platform_device *pdev)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct tusb6010_glue		*glue = platform_get_drvdata(pdev);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	platform_device_unregister(glue->musb);
128862306a36Sopenharmony_ci	usb_phy_generic_unregister(glue->phy);
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_cistatic struct platform_driver tusb_driver = {
129262306a36Sopenharmony_ci	.probe		= tusb_probe,
129362306a36Sopenharmony_ci	.remove_new	= tusb_remove,
129462306a36Sopenharmony_ci	.driver		= {
129562306a36Sopenharmony_ci		.name	= "musb-tusb",
129662306a36Sopenharmony_ci	},
129762306a36Sopenharmony_ci};
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ciMODULE_DESCRIPTION("TUSB6010 MUSB Glue Layer");
130062306a36Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
130162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
130262306a36Sopenharmony_cimodule_platform_driver(tusb_driver);
1303