1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Microchip PIC32 MUSB "glue layer" 4 * 5 * Copyright (C) 2015, Microchip Technology Inc. 6 * Cristian Birsan <cristian.birsan@microchip.com> 7 * Purna Chandra Mandal <purna.mandal@microchip.com> 8 * 9 * Based on the dsps "glue layer" code. 10 */ 11 12#include <common.h> 13#include <dm.h> 14#include <asm/global_data.h> 15#include <dm/device_compat.h> 16#include <linux/bitops.h> 17#include <linux/delay.h> 18#include <linux/usb/musb.h> 19#include "linux-compat.h" 20#include "musb_core.h" 21#include "musb_uboot.h" 22 23DECLARE_GLOBAL_DATA_PTR; 24 25#define PIC32_TX_EP_MASK 0x0f /* EP0 + 7 Tx EPs */ 26#define PIC32_RX_EP_MASK 0x0e /* 7 Rx EPs */ 27 28#define MUSB_SOFTRST 0x7f 29#define MUSB_SOFTRST_NRST BIT(0) 30#define MUSB_SOFTRST_NRSTX BIT(1) 31 32#define USBCRCON 0 33#define USBCRCON_USBWKUPEN BIT(0) /* Enable Wakeup Interrupt */ 34#define USBCRCON_USBRIE BIT(1) /* Enable Remote resume Interrupt */ 35#define USBCRCON_USBIE BIT(2) /* Enable USB General interrupt */ 36#define USBCRCON_SENDMONEN BIT(3) /* Enable Session End VBUS monitoring */ 37#define USBCRCON_BSVALMONEN BIT(4) /* Enable B-Device VBUS monitoring */ 38#define USBCRCON_ASVALMONEN BIT(5) /* Enable A-Device VBUS monitoring */ 39#define USBCRCON_VBUSMONEN BIT(6) /* Enable VBUS monitoring */ 40#define USBCRCON_PHYIDEN BIT(7) /* PHY ID monitoring enable */ 41#define USBCRCON_USBIDVAL BIT(8) /* USB ID value */ 42#define USBCRCON_USBIDOVEN BIT(9) /* USB ID override enable */ 43#define USBCRCON_USBWK BIT(24) /* USB Wakeup Status */ 44#define USBCRCON_USBRF BIT(25) /* USB Resume Status */ 45#define USBCRCON_USBIF BIT(26) /* USB General Interrupt Status */ 46 47/* PIC32 controller data */ 48struct pic32_musb_data { 49 struct musb_host_data mdata; 50 struct device dev; 51 void __iomem *musb_glue; 52}; 53 54#define to_pic32_musb_data(d) \ 55 container_of(d, struct pic32_musb_data, dev) 56 57static void pic32_musb_disable(struct musb *musb) 58{ 59 /* no way to shut the controller */ 60} 61 62static int pic32_musb_enable(struct musb *musb) 63{ 64 /* soft reset by NRSTx */ 65 musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX); 66 /* set mode */ 67 musb_platform_set_mode(musb, musb->board_mode); 68 69 return 0; 70} 71 72static irqreturn_t pic32_interrupt(int irq, void *hci) 73{ 74 struct musb *musb = hci; 75 irqreturn_t ret = IRQ_NONE; 76 u32 epintr, usbintr; 77 78 /* ack usb core interrupts */ 79 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 80 if (musb->int_usb) 81 musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 82 83 /* ack endpoint interrupts */ 84 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX) & PIC32_RX_EP_MASK; 85 if (musb->int_rx) 86 musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 87 88 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX) & PIC32_TX_EP_MASK; 89 if (musb->int_tx) 90 musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 91 92 /* drop spurious RX and TX if device is disconnected */ 93 if (musb->int_usb & MUSB_INTR_DISCONNECT) { 94 musb->int_tx = 0; 95 musb->int_rx = 0; 96 } 97 98 if (musb->int_tx || musb->int_rx || musb->int_usb) 99 ret = musb_interrupt(musb); 100 101 return ret; 102} 103 104static int pic32_musb_set_mode(struct musb *musb, u8 mode) 105{ 106 struct device *dev = musb->controller; 107 struct pic32_musb_data *pdata = to_pic32_musb_data(dev); 108 109 switch (mode) { 110 case MUSB_HOST: 111 clrsetbits_le32(pdata->musb_glue + USBCRCON, 112 USBCRCON_USBIDVAL, USBCRCON_USBIDOVEN); 113 break; 114 case MUSB_PERIPHERAL: 115 setbits_le32(pdata->musb_glue + USBCRCON, 116 USBCRCON_USBIDVAL | USBCRCON_USBIDOVEN); 117 break; 118 case MUSB_OTG: 119 dev_err(dev, "support for OTG is unimplemented\n"); 120 break; 121 default: 122 dev_err(dev, "unsupported mode %d\n", mode); 123 return -EINVAL; 124 } 125 126 return 0; 127} 128 129static int pic32_musb_init(struct musb *musb) 130{ 131 struct pic32_musb_data *pdata = to_pic32_musb_data(musb->controller); 132 u32 ctrl, hwvers; 133 u8 power; 134 135 /* Returns zero if not clocked */ 136 hwvers = musb_read_hwvers(musb->mregs); 137 if (!hwvers) 138 return -ENODEV; 139 140 /* Reset the musb */ 141 power = musb_readb(musb->mregs, MUSB_POWER); 142 power = power | MUSB_POWER_RESET; 143 musb_writeb(musb->mregs, MUSB_POWER, power); 144 mdelay(100); 145 146 /* Start the on-chip PHY and its PLL. */ 147 power = power & ~MUSB_POWER_RESET; 148 musb_writeb(musb->mregs, MUSB_POWER, power); 149 150 musb->isr = pic32_interrupt; 151 152 ctrl = USBCRCON_USBIF | USBCRCON_USBRF | 153 USBCRCON_USBWK | USBCRCON_USBIDOVEN | 154 USBCRCON_PHYIDEN | USBCRCON_USBIE | 155 USBCRCON_USBRIE | USBCRCON_USBWKUPEN | 156 USBCRCON_VBUSMONEN; 157 writel(ctrl, pdata->musb_glue + USBCRCON); 158 159 return 0; 160} 161 162/* PIC32 supports only 32bit read operation */ 163void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) 164{ 165 void __iomem *fifo = hw_ep->fifo; 166 u32 val, rem = len % 4; 167 168 /* USB stack ensures dst is always 32bit aligned. */ 169 readsl(fifo, dst, len / 4); 170 if (rem) { 171 dst += len & ~0x03; 172 val = musb_readl(fifo, 0); 173 memcpy(dst, &val, rem); 174 } 175} 176 177const struct musb_platform_ops pic32_musb_ops = { 178 .init = pic32_musb_init, 179 .set_mode = pic32_musb_set_mode, 180 .disable = pic32_musb_disable, 181 .enable = pic32_musb_enable, 182}; 183 184/* PIC32 default FIFO config - fits in 8KB */ 185static struct musb_fifo_cfg pic32_musb_fifo_config[] = { 186 { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, 187 { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, 188 { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, 189 { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, 190 { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, 191 { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, 192 { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, 193 { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, 194 { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, 195 { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, 196 { .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, }, 197 { .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, }, 198 { .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, }, 199 { .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, }, 200}; 201 202static struct musb_hdrc_config pic32_musb_config = { 203 .fifo_cfg = pic32_musb_fifo_config, 204 .fifo_cfg_size = ARRAY_SIZE(pic32_musb_fifo_config), 205 .multipoint = 1, 206 .dyn_fifo = 1, 207 .num_eps = 8, 208 .ram_bits = 11, 209}; 210 211/* PIC32 has one MUSB controller which can be host or gadget */ 212static struct musb_hdrc_platform_data pic32_musb_plat = { 213 .mode = MUSB_HOST, 214 .config = &pic32_musb_config, 215 .power = 250, /* 500mA */ 216 .platform_ops = &pic32_musb_ops, 217}; 218 219static int musb_usb_probe(struct udevice *dev) 220{ 221 struct usb_bus_priv *priv = dev_get_uclass_priv(dev); 222 struct pic32_musb_data *pdata = dev_get_priv(dev); 223 struct musb_host_data *mdata = &pdata->mdata; 224 struct fdt_resource mc, glue; 225 void *fdt = (void *)gd->fdt_blob; 226 int node = dev_of_offset(dev); 227 void __iomem *mregs; 228 int ret; 229 230 priv->desc_before_addr = true; 231 232 ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", 233 "mc", &mc); 234 if (ret < 0) { 235 printf("pic32-musb: resource \"mc\" not found\n"); 236 return ret; 237 } 238 239 ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", 240 "control", &glue); 241 if (ret < 0) { 242 printf("pic32-musb: resource \"control\" not found\n"); 243 return ret; 244 } 245 246 mregs = ioremap(mc.start, fdt_resource_size(&mc)); 247 pdata->musb_glue = ioremap(glue.start, fdt_resource_size(&glue)); 248 249 /* init controller */ 250#ifdef CONFIG_USB_MUSB_HOST 251 mdata->host = musb_init_controller(&pic32_musb_plat, 252 &pdata->dev, mregs); 253 if (!mdata->host) 254 return -EIO; 255 256 ret = musb_lowlevel_init(mdata); 257#else 258 pic32_musb_plat.mode = MUSB_PERIPHERAL; 259 mdata->host = musb_register(&pic32_musb_plat, &pdata->dev, mregs); 260 if (!mdata->host) 261 return -EIO; 262#endif 263 if ((ret == 0) && mdata->host) 264 printf("PIC32 MUSB OTG\n"); 265 266 return ret; 267} 268 269static int musb_usb_remove(struct udevice *dev) 270{ 271 struct pic32_musb_data *pdata = dev_get_priv(dev); 272 273 musb_stop(pdata->mdata.host); 274 275 return 0; 276} 277 278static const struct udevice_id pic32_musb_ids[] = { 279 { .compatible = "microchip,pic32mzda-usb" }, 280 { } 281}; 282 283U_BOOT_DRIVER(usb_musb) = { 284 .name = "pic32-musb", 285 .id = UCLASS_USB, 286 .of_match = pic32_musb_ids, 287 .probe = musb_usb_probe, 288 .remove = musb_usb_remove, 289#ifdef CONFIG_USB_MUSB_HOST 290 .ops = &musb_usb_ops, 291#endif 292 .plat_auto = sizeof(struct usb_plat), 293 .priv_auto = sizeof(struct pic32_musb_data), 294}; 295