157429Smarkm// SPDX-License-Identifier: GPL-2.0+
257429Smarkm/*
357429Smarkm * Microchip PIC32 MUSB "glue layer"
457429Smarkm *
557429Smarkm * Copyright (C) 2015, Microchip Technology Inc.
657429Smarkm *  Cristian Birsan <cristian.birsan@microchip.com>
760573Skris *  Purna Chandra Mandal <purna.mandal@microchip.com>
865668Skris *
965668Skris * Based on the dsps "glue layer" code.
1065668Skris */
1165668Skris
1265668Skris#include <common.h>
1365668Skris#include <dm.h>
1465668Skris#include <asm/global_data.h>
1560573Skris#include <dm/device_compat.h>
1692555Sdes#include <linux/bitops.h>
1760573Skris#include <linux/delay.h>
1865668Skris#include <linux/usb/musb.h>
1965668Skris#include "linux-compat.h"
2065668Skris#include "musb_core.h"
2165668Skris#include "musb_uboot.h"
2265668Skris
2365668SkrisDECLARE_GLOBAL_DATA_PTR;
2465668Skris
2565668Skris#define PIC32_TX_EP_MASK	0x0f		/* EP0 + 7 Tx EPs */
2665668Skris#define PIC32_RX_EP_MASK	0x0e		/* 7 Rx EPs */
2765668Skris
2865668Skris#define MUSB_SOFTRST		0x7f
2965668Skris#define  MUSB_SOFTRST_NRST	BIT(0)
3065668Skris#define  MUSB_SOFTRST_NRSTX	BIT(1)
3165668Skris
3265668Skris#define USBCRCON		0
3365668Skris#define  USBCRCON_USBWKUPEN	BIT(0)  /* Enable Wakeup Interrupt */
3465668Skris#define  USBCRCON_USBRIE	BIT(1)  /* Enable Remote resume Interrupt */
3565668Skris#define  USBCRCON_USBIE		BIT(2)  /* Enable USB General interrupt */
3665668Skris#define  USBCRCON_SENDMONEN	BIT(3)  /* Enable Session End VBUS monitoring */
3757429Smarkm#define  USBCRCON_BSVALMONEN	BIT(4)  /* Enable B-Device VBUS monitoring */
3857429Smarkm#define  USBCRCON_ASVALMONEN	BIT(5)  /* Enable A-Device VBUS monitoring */
3957429Smarkm#define  USBCRCON_VBUSMONEN	BIT(6)  /* Enable VBUS monitoring */
40124208Sdes#define  USBCRCON_PHYIDEN	BIT(7)  /* PHY ID monitoring enable */
4157429Smarkm#define  USBCRCON_USBIDVAL	BIT(8)  /* USB ID value */
42124208Sdes#define  USBCRCON_USBIDOVEN	BIT(9)  /* USB ID override enable */
43124208Sdes#define  USBCRCON_USBWK		BIT(24) /* USB Wakeup Status */
4457429Smarkm#define  USBCRCON_USBRF		BIT(25) /* USB Resume Status */
4557429Smarkm#define  USBCRCON_USBIF		BIT(26) /* USB General Interrupt Status */
4657429Smarkm
4757429Smarkm/* PIC32 controller data */
4857429Smarkmstruct pic32_musb_data {
4957429Smarkm	struct musb_host_data mdata;
5057429Smarkm	struct device dev;
5157429Smarkm	void __iomem *musb_glue;
5257429Smarkm};
5360573Skris
5457429Smarkm#define to_pic32_musb_data(d)	\
5560573Skris	container_of(d, struct pic32_musb_data, dev)
5676259Sgreen
5760573Skrisstatic void pic32_musb_disable(struct musb *musb)
5860573Skris{
5969587Sgreen	/* no way to shut the controller */
6060573Skris}
6176259Sgreen
6276259Sgreenstatic int pic32_musb_enable(struct musb *musb)
6376259Sgreen{
6492555Sdes	/* soft reset by NRSTx */
6598675Sdes	musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX);
6660573Skris	/* set mode */
6760573Skris	musb_platform_set_mode(musb, musb->board_mode);
6860573Skris
6960573Skris	return 0;
7060573Skris}
7160573Skris
7260573Skrisstatic irqreturn_t pic32_interrupt(int irq, void *hci)
7357429Smarkm{
7457429Smarkm	struct musb  *musb = hci;
7557429Smarkm	irqreturn_t ret = IRQ_NONE;
7657429Smarkm	u32 epintr, usbintr;
7757429Smarkm
7857429Smarkm	/* ack usb core interrupts */
7957429Smarkm	musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
8057429Smarkm	if (musb->int_usb)
8157429Smarkm		musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
8257429Smarkm
8376259Sgreen	/* ack endpoint interrupts */
8457429Smarkm	musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX) & PIC32_RX_EP_MASK;
8557429Smarkm	if (musb->int_rx)
8657429Smarkm		musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
8757429Smarkm
8857429Smarkm	musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX) & PIC32_TX_EP_MASK;
8957429Smarkm	if (musb->int_tx)
9057429Smarkm		musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
9157429Smarkm
9298675Sdes	/* drop spurious RX and TX if device is disconnected */
9357429Smarkm	if (musb->int_usb & MUSB_INTR_DISCONNECT) {
9457429Smarkm		musb->int_tx = 0;
9598675Sdes		musb->int_rx = 0;
9657429Smarkm	}
9757429Smarkm
9857429Smarkm	if (musb->int_tx || musb->int_rx || musb->int_usb)
9957429Smarkm		ret = musb_interrupt(musb);
10057429Smarkm
10157429Smarkm	return ret;
10257429Smarkm}
10357429Smarkm
10457429Smarkmstatic int pic32_musb_set_mode(struct musb *musb, u8 mode)
10576259Sgreen{
10657429Smarkm	struct device *dev = musb->controller;
10757429Smarkm	struct pic32_musb_data *pdata = to_pic32_musb_data(dev);
10857429Smarkm
10957429Smarkm	switch (mode) {
11057429Smarkm	case MUSB_HOST:
111124208Sdes		clrsetbits_le32(pdata->musb_glue + USBCRCON,
11257429Smarkm				USBCRCON_USBIDVAL, USBCRCON_USBIDOVEN);
11357429Smarkm		break;
11457429Smarkm	case MUSB_PERIPHERAL:
11557429Smarkm		setbits_le32(pdata->musb_glue + USBCRCON,
11657429Smarkm			     USBCRCON_USBIDVAL | USBCRCON_USBIDOVEN);
11757429Smarkm		break;
11857429Smarkm	case MUSB_OTG:
11960573Skris		dev_err(dev, "support for OTG is unimplemented\n");
12076259Sgreen		break;
121124208Sdes	default:
122124208Sdes		dev_err(dev, "unsupported mode %d\n", mode);
123124208Sdes		return -EINVAL;
124124208Sdes	}
125124208Sdes
12660573Skris	return 0;
127124208Sdes}
128124208Sdes
129124208Sdesstatic int pic32_musb_init(struct musb *musb)
13098675Sdes{
13198675Sdes	struct pic32_musb_data *pdata = to_pic32_musb_data(musb->controller);
13298675Sdes	u32 ctrl, hwvers;
13398675Sdes	u8 power;
13492555Sdes
13592555Sdes	/* Returns zero if not clocked */
13660573Skris	hwvers = musb_read_hwvers(musb->mregs);
137124208Sdes	if (!hwvers)
138124208Sdes		return -ENODEV;
139124208Sdes
140124208Sdes	/* Reset the musb */
141124208Sdes	power = musb_readb(musb->mregs, MUSB_POWER);
142124208Sdes	power = power | MUSB_POWER_RESET;
143124208Sdes	musb_writeb(musb->mregs, MUSB_POWER, power);
14457429Smarkm	mdelay(100);
14557429Smarkm
14657429Smarkm	/* Start the on-chip PHY and its PLL. */
14757429Smarkm	power = power & ~MUSB_POWER_RESET;
14857429Smarkm	musb_writeb(musb->mregs, MUSB_POWER, power);
14957429Smarkm
15057429Smarkm	musb->isr = pic32_interrupt;
15169587Sgreen
152106121Sdes	ctrl =  USBCRCON_USBIF | USBCRCON_USBRF |
15369587Sgreen		USBCRCON_USBWK | USBCRCON_USBIDOVEN |
15469587Sgreen		USBCRCON_PHYIDEN | USBCRCON_USBIE |
15557429Smarkm		USBCRCON_USBRIE | USBCRCON_USBWKUPEN |
15657429Smarkm		USBCRCON_VBUSMONEN;
15792555Sdes	writel(ctrl, pdata->musb_glue + USBCRCON);
15892555Sdes
15992555Sdes	return 0;
16057429Smarkm}
16157429Smarkm
16257429Smarkm/* PIC32 supports only 32bit read operation */
16357429Smarkmvoid musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
16457429Smarkm{
16557429Smarkm	void __iomem *fifo = hw_ep->fifo;
166124208Sdes	u32 val, rem = len % 4;
16757429Smarkm
16857429Smarkm	/* USB stack ensures dst is always 32bit aligned. */
16957429Smarkm	readsl(fifo, dst, len / 4);
17057429Smarkm	if (rem) {
17157429Smarkm		dst += len & ~0x03;
17257429Smarkm		val = musb_readl(fifo, 0);
17357429Smarkm		memcpy(dst, &val, rem);
17457429Smarkm	}
17592555Sdes}
17657429Smarkm
17757429Smarkmconst struct musb_platform_ops pic32_musb_ops = {
17857429Smarkm	.init		= pic32_musb_init,
17957429Smarkm	.set_mode	= pic32_musb_set_mode,
18057429Smarkm	.disable	= pic32_musb_disable,
18157429Smarkm	.enable		= pic32_musb_enable,
18257429Smarkm};
18357429Smarkm
18457429Smarkm/* PIC32 default FIFO config - fits in 8KB */
18557429Smarkmstatic struct musb_fifo_cfg pic32_musb_fifo_config[] = {
18657429Smarkm	{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
18757429Smarkm	{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
18857429Smarkm	{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
18957429Smarkm	{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
19057429Smarkm	{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
19157429Smarkm	{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
19257429Smarkm	{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
19357429Smarkm	{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
19457429Smarkm	{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
19557429Smarkm	{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
19657429Smarkm	{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, },
19757429Smarkm	{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, },
19898675Sdes	{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
19998675Sdes	{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, },
20098675Sdes};
20198675Sdes
20298675Sdesstatic struct musb_hdrc_config pic32_musb_config = {
20398675Sdes	.fifo_cfg	= pic32_musb_fifo_config,
20498675Sdes	.fifo_cfg_size	= ARRAY_SIZE(pic32_musb_fifo_config),
20598675Sdes	.multipoint     = 1,
20698675Sdes	.dyn_fifo       = 1,
20798675Sdes	.num_eps        = 8,
20898675Sdes	.ram_bits       = 11,
20998675Sdes};
21098675Sdes
21198675Sdes/* PIC32 has one MUSB controller which can be host or gadget */
21298675Sdesstatic struct musb_hdrc_platform_data pic32_musb_plat = {
21398675Sdes	.mode           = MUSB_HOST,
21498675Sdes	.config         = &pic32_musb_config,
21598675Sdes	.power          = 250,		/* 500mA */
21698675Sdes	.platform_ops	= &pic32_musb_ops,
21798675Sdes};
21898675Sdes
21998675Sdesstatic int musb_usb_probe(struct udevice *dev)
22098675Sdes{
22198675Sdes	struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
22298675Sdes	struct pic32_musb_data *pdata = dev_get_priv(dev);
22398675Sdes	struct musb_host_data *mdata = &pdata->mdata;
22498675Sdes	struct fdt_resource mc, glue;
22598675Sdes	void *fdt = (void *)gd->fdt_blob;
22698675Sdes	int node = dev_of_offset(dev);
22798675Sdes	void __iomem *mregs;
22898675Sdes	int ret;
22998675Sdes
23098675Sdes	priv->desc_before_addr = true;
23198675Sdes
23298675Sdes	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
23398675Sdes				     "mc", &mc);
23498675Sdes	if (ret < 0) {
23598675Sdes		printf("pic32-musb: resource \"mc\" not found\n");
23698675Sdes		return ret;
23798675Sdes	}
23898675Sdes
23998675Sdes	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
24098675Sdes				     "control", &glue);
24198675Sdes	if (ret < 0) {
24298675Sdes		printf("pic32-musb: resource \"control\" not found\n");
24398675Sdes		return ret;
24498675Sdes	}
24598675Sdes
24698675Sdes	mregs = ioremap(mc.start, fdt_resource_size(&mc));
24798675Sdes	pdata->musb_glue = ioremap(glue.start, fdt_resource_size(&glue));
24898675Sdes
24998675Sdes	/* init controller */
25098675Sdes#ifdef CONFIG_USB_MUSB_HOST
25198675Sdes	mdata->host = musb_init_controller(&pic32_musb_plat,
25298675Sdes					   &pdata->dev, mregs);
25398675Sdes	if (!mdata->host)
25498675Sdes		return -EIO;
25598675Sdes
25698675Sdes	ret = musb_lowlevel_init(mdata);
25798675Sdes#else
25898675Sdes	pic32_musb_plat.mode = MUSB_PERIPHERAL;
25998675Sdes	mdata->host = musb_register(&pic32_musb_plat, &pdata->dev, mregs);
26098675Sdes	if (!mdata->host)
26198675Sdes		return -EIO;
26298675Sdes#endif
26398675Sdes	if ((ret == 0) && mdata->host)
26498675Sdes		printf("PIC32 MUSB OTG\n");
26598675Sdes
26698675Sdes	return ret;
26798675Sdes}
268124208Sdes
26998675Sdesstatic int musb_usb_remove(struct udevice *dev)
27098675Sdes{
27198675Sdes	struct pic32_musb_data *pdata = dev_get_priv(dev);
27298675Sdes
273124208Sdes	musb_stop(pdata->mdata.host);
274124208Sdes
275124208Sdes	return 0;
276124208Sdes}
27798675Sdes
278124208Sdesstatic const struct udevice_id pic32_musb_ids[] = {
279124208Sdes	{ .compatible = "microchip,pic32mzda-usb" },
280124208Sdes	{ }
281124208Sdes};
28298675Sdes
28398675SdesU_BOOT_DRIVER(usb_musb) = {
28498675Sdes	.name		= "pic32-musb",
285124208Sdes	.id		= UCLASS_USB,
28698675Sdes	.of_match	= pic32_musb_ids,
287124208Sdes	.probe		= musb_usb_probe,
288124208Sdes	.remove		= musb_usb_remove,
289124208Sdes#ifdef CONFIG_USB_MUSB_HOST
290124208Sdes	.ops		= &musb_usb_ops,
291124208Sdes#endif
292124208Sdes	.plat_auto	= sizeof(struct usb_plat),
29398675Sdes	.priv_auto	= sizeof(struct pic32_musb_data),
29498675Sdes};
29557429Smarkm