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