| // SPDX-License-Identifier: GPL-2.0 |
| |
| #include <clk.h> |
| #include <common.h> |
| #include <dm.h> |
| #include <dt-bindings/clock/mt8516-clk.h> |
| |
| #include "musb_core.h" |
| |
| #define USB_L1INTS (0x00a0) /* USB level 1 interrupt status register */ |
| #define USB_L1INTM (0x00a4) /* USB level 1 interrupt mask register */ |
| #define USB_L1INTP (0x00a8) /* USB level 1 interrupt polarity register */ |
| #define TX_INT_STATUS (1<<0) |
| #define RX_INT_STATUS (1<<1) |
| #define USBCOM_INT_STATUS (1<<2) |
| #define DMA_INT_STATUS (1<<3) |
| #define PSR_INT_STATUS (1<<4) |
| #define QINT_STATUS (1<<5) |
| #define QHIF_INT_STATUS (1<<6) |
| #define DPDM_INT_STATUS (1<<7) |
| #define VBUSVALID_INT_STATUS (1<<8) |
| #define IDDIG_INT_STATUS (1<<9) |
| #define DRVVBUS_INT_STATUS (1<<10) |
| |
| /*EP Fifo Config*/ |
| static struct musb_fifo_cfg fifo_cfg[] __initdata = { |
| { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE}, |
| { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE}, |
| { .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE}, |
| { .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE}, |
| { .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE}, |
| { .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE}, |
| { .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| { .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE}, |
| }; |
| |
| static struct musb_hdrc_config mt8xx_musb_hdrc_config = { |
| .multipoint = true, |
| .dyn_fifo = true, |
| .num_eps = 16, |
| .ram_bits = 16, |
| .fifo_cfg = fifo_cfg, |
| .fifo_cfg_size = ARRAY_SIZE(fifo_cfg), |
| }; |
| |
| struct omap_musb_board_data { |
| u8 interface_type; |
| struct udevice *dev; |
| void (*set_phy_power)(struct udevice *dev, u8 on); |
| void (*clear_irq)(struct udevice *dev); |
| void (*reset)(struct udevice *dev); |
| }; |
| |
| struct mt8xx_glue { |
| struct device *dev; |
| struct platform_device *usb_phy; |
| |
| void *base; |
| struct musb *musb; |
| struct omap_musb_board_data otg_board_data; |
| struct musb_hdrc_platform_data musb_pdata; |
| }; |
| #define glue_to_musb(g) platform_get_drvdata(g->musb) |
| |
| static irqreturn_t mt8xx_musb_interrupt(int irq, void *__hci) |
| { |
| unsigned long flags; |
| irqreturn_t retval = IRQ_NONE; |
| struct musb *musb = __hci; |
| u32 l1ints, l1intm; |
| |
| l1ints = musb_readl(musb->mregs, USB_L1INTS); |
| l1intm = musb_readl(musb->mregs, USB_L1INTM); |
| |
| if (!(l1ints & l1intm)) |
| return IRQ_HANDLED; |
| |
| spin_lock_irqsave(&musb->lock, flags); |
| |
| musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); |
| musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); |
| musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); |
| mb(); /* */ |
| musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); |
| musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); |
| musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); |
| |
| if (musb->int_usb & (1 << 2)) { |
| musb_writew(musb->mregs, 0x74, 3); |
| spin_unlock_irqrestore(&musb->lock, flags); |
| return 0; |
| } |
| |
| if (musb->int_usb || musb->int_tx || musb->int_rx) |
| retval = musb_interrupt(musb); |
| |
| spin_unlock_irqrestore(&musb->lock, flags); |
| |
| return retval; |
| } |
| |
| static void mt8xx_phy_poweron(void) |
| { |
| #if 0 |
| udelay(50); |
| |
| writeb(readb(0x11110000 + 0x81d) & ~0x10, (char*) 0x11110000 + 0x81d); |
| writeb(readb(0x11110000 + 0x86b) & ~0x04, (char*) 0x11110000 + 0x86b); |
| writeb(readb(0x11110000 + 0x86e) & ~0x01, (char*) 0x11110000 + 0x86e); |
| writeb(readb(0x11110000 + 0x86a) & ~0x04, (char*) 0x11110000 + 0x86a); |
| writeb(readb(0x11110000 + 0x821) & ~0x03, (char*) 0x11110000 + 0x821); |
| writeb(readb(0x11110000 + 0x868) & ~0x40, (char*) 0x11110000 + 0x868); |
| writeb(readb(0x11110000 + 0x868) & ~0x80, (char*) 0x11110000 + 0x868); |
| writeb(readb(0x11110000 + 0x868) & ~0x30, (char*) 0x11110000 + 0x868); |
| writeb(readb(0x11110000 + 0x868) & ~0x04, (char*) 0x11110000 + 0x868); |
| writeb(readb(0x11110000 + 0x869) & ~0x3c, (char*) 0x11110000 + 0x869); |
| |
| writeb(readb(0x11110000 + 0x86a) & ~0x10, (char*) 0x11110000 + 0x86a); |
| writeb(readb(0x11110000 + 0x86a) & ~0x20, (char*) 0x11110000 + 0x86a); |
| writeb(readb(0x11110000 + 0x86a) & ~0x08, (char*) 0x11110000 + 0x86a); |
| writeb(readb(0x11110000 + 0x86a) & ~0x02, (char*) 0x11110000 + 0x86a); |
| writeb(readb(0x11110000 + 0x86a) & ~0x80, (char*) 0x11110000 + 0x86a); |
| |
| writeb(readb(0x11110000 + 0x81a) & ~0x80, (char*) 0x11110000 + 0x81a); |
| writeb(readb(0x11110000 + 0x81a) | 0x10, (char*) 0x11110000 + 0x81a); |
| writeb(readb(0x11110000 + 0x818) & ~0x08, (char*) 0x11110000 + 0x818); |
| writeb(readb(0x11110000 + 0x818) | 0x06, (char*) 0x11110000 + 0x818); |
| |
| udelay(800); |
| |
| writeb(readb(0x11110000 + 0x86c) & ~0x10, (char*) 0x11110000 + 0x86c); |
| writeb(readb(0x11110000 + 0x86c) | 0x2e, (char*) 0x11110000 + 0x86c); |
| writeb(readb(0x11110000 + 0x86d) | 0x3e, (char*) 0x11110000 + 0x86d); |
| #endif |
| |
| #define USBPHY_CLR8(reg, val) writeb(readb(0x11110800 + reg) & ~val, 0x11110800 + reg) |
| #define USBPHY_SET8(reg, val) writeb(readb(0x11110800 + reg) | val, 0x11110800 + reg) |
| |
| /* |
| * swtich to USB function. |
| * (system register, force ip into usb mode). |
| */ |
| USBPHY_CLR8(0x6b, 0x04); |
| USBPHY_CLR8(0x6e, 0x01); |
| USBPHY_CLR8(0x21, 0x03); |
| |
| /* RG_USB20_BC11_SW_EN = 1'b0 */ |
| USBPHY_SET8(0x22, 0x04); |
| USBPHY_CLR8(0x1a, 0x80); |
| |
| /* RG_USB20_DP_100K_EN = 1'b0 */ |
| /* RG_USB20_DP_100K_EN = 1'b0 */ |
| USBPHY_CLR8(0x22, 0x03); |
| |
| /*OTG enable*/ |
| USBPHY_SET8(0x20, 0x10); |
| /* release force suspendm */ |
| USBPHY_CLR8(0x6a, 0x04); |
| |
| udelay(800); |
| |
| /* force enter device mode */ |
| USBPHY_CLR8(0x6c, 0x10); |
| USBPHY_SET8(0x6c, 0x2E); |
| USBPHY_SET8(0x6d, 0x3E); |
| |
| /*** recover *****/ |
| |
| |
| USBPHY_CLR8(0x1d, 0x10); |
| |
| /* force_uart_en = 1'b0 */ |
| USBPHY_CLR8(0x6b, 0x04); |
| /* RG_UART_EN = 1'b0 */ |
| USBPHY_CLR8(0x6e, 0x01); |
| /* force_uart_en = 1'b0 */ |
| USBPHY_CLR8(0x6a, 0x04); |
| |
| USBPHY_CLR8(0x21, 0x03); |
| USBPHY_CLR8(0x68, 0xf4); |
| |
| /* RG_DATAIN[3:0] = 4'b0000 */ |
| USBPHY_CLR8(0x69, 0x3c); |
| |
| USBPHY_CLR8(0x6a, 0xba); |
| |
| /* RG_USB20_BC11_SW_EN = 1'b0 */ |
| USBPHY_CLR8(0x1a, 0x80); |
| /* RG_USB20_OTG_VBUSSCMP_EN = 1'b1 */ |
| USBPHY_SET8(0x1a, 0x10); |
| |
| //HQA adjustment |
| USBPHY_CLR8(0x18, 0x08); |
| USBPHY_SET8(0x18, 0x06); |
| udelay(800); |
| } |
| |
| static int mt8xx_musb_init(struct musb *musb) |
| { |
| int ret; |
| int status; |
| |
| // musb_writew(musb->mregs, 0x74, 3); |
| musb->isr = mt8xx_musb_interrupt; |
| |
| musb_writel(musb->mregs, USB_L1INTM, |
| TX_INT_STATUS | RX_INT_STATUS | USBCOM_INT_STATUS | |
| DMA_INT_STATUS); |
| return 0; |
| } |
| |
| static int mt8xx_musb_exit(struct musb *musb) |
| { |
| return 0; |
| } |
| |
| static int mt8xx_musb_enable(struct musb *musb) |
| { |
| unsigned long flags; |
| |
| mt8xx_phy_poweron(); |
| |
| flags = musb_readl(musb->mregs, USB_L1INTM); |
| |
| musb_writel(musb->mregs, USB_L1INTM, (~IDDIG_INT_STATUS) & flags); |
| mdelay(10); |
| musb_writel(musb->mregs, USB_L1INTM, flags); |
| |
| return 0; |
| } |
| |
| static const struct musb_platform_ops mt8xx_ops = { |
| .init = mt8xx_musb_init, |
| .exit = mt8xx_musb_exit, |
| .enable = mt8xx_musb_enable, |
| }; |
| |
| |
| static void mt_usb_phy_recover(void) |
| { |
| |
| /* clean PUPD_BIST_EN */ |
| /* PUPD_BIST_EN = 1'b0 */ |
| /* PMIC will use it to detect charger type */ |
| USBPHY_CLR8(0x1d, 0x10); |
| |
| /* force_uart_en = 1'b0 */ |
| USBPHY_CLR8(0x6b, 0x04); |
| /* RG_UART_EN = 1'b0 */ |
| USBPHY_CLR8(0x6e, 0x01); |
| /* force_uart_en = 1'b0 */ |
| USBPHY_CLR8(0x6a, 0x04); |
| |
| USBPHY_CLR8(0x21, 0x03); |
| USBPHY_CLR8(0x68, 0xf4); |
| |
| /* RG_DATAIN[3:0] = 4'b0000 */ |
| USBPHY_CLR8(0x69, 0x3c); |
| |
| USBPHY_CLR8(0x6a, 0xba); |
| |
| /* RG_USB20_BC11_SW_EN = 1'b0 */ |
| USBPHY_CLR8(0x1a, 0x80); |
| /* RG_USB20_OTG_VBUSSCMP_EN = 1'b1 */ |
| USBPHY_SET8(0x1a, 0x10); |
| |
| //HQA adjustment |
| USBPHY_CLR8(0x18, 0x08); |
| USBPHY_SET8(0x18, 0x06); |
| udelay(800); |
| |
| /* force enter device mode */ |
| //USBPHY_CLR8(0x6c, 0x10); |
| //USBPHY_SET8(0x6c, 0x2E); |
| //USBPHY_SET8(0x6d, 0x3E); |
| } |
| |
| static int mt8xx_probe(struct udevice *udev) |
| { |
| struct mt8xx_glue *glue = dev_get_platdata(udev); |
| struct udevice *clk_dev; |
| struct udevice *clk_cg_dev; |
| int ret; |
| int i; |
| |
| unsigned long usb_clk[] = { |
| CLK_TOP_USB_PHY48M, |
| CLK_TOP_USB_78M, |
| CLK_TOP_USBIF, |
| CLK_TOP_USB, |
| CLK_TOP_USB_1P, |
| }; |
| |
| ret = uclass_get_device_by_driver(UCLASS_CLK, |
| DM_GET_DRIVER(mtk_clk_topckgen), &clk_dev); |
| if (ret) |
| return ret; |
| |
| ret = uclass_get_device_by_driver(UCLASS_CLK, |
| DM_GET_DRIVER(mtk_clk_topckgen_cg), &clk_cg_dev); |
| if (ret) |
| return ret; |
| #if 1 |
| for (i = 0; i < ARRAY_SIZE(usb_clk); i++) { |
| struct clk clk = { .id = usb_clk[i], .dev = clk_dev }; |
| if (i != 0) |
| clk.dev = clk_cg_dev; |
| |
| ret = clk_enable(&clk); |
| if (ret) |
| return ret; |
| ret = clk_disable(&clk); |
| if (ret) |
| return ret; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(usb_clk); i++) { |
| struct clk clk = { .id = usb_clk[i], .dev = clk_dev }; |
| if (i != 0) |
| clk.dev = clk_cg_dev; |
| |
| ret = clk_enable(&clk); |
| if (ret) |
| return ret; |
| } |
| |
| struct clk clk1 = { .id = CLK_TOP_USB_78M_SEL, .dev = clk_dev }; |
| struct clk clk2 = { .id = CLK_TOP_UNIVPLL_D16, .dev = clk_dev }; |
| |
| clk_set_parent(&clk1, &clk2); |
| #endif |
| |
| u32 l1ints, l1intm; |
| |
| u8 tmpReg8; |
| |
| /* connect */ |
| tmpReg8 = musb_readb(glue->base, MUSB_POWER); |
| tmpReg8 &= ~MUSB_POWER_SOFTCONN; |
| musb_writeb(glue->base, MUSB_POWER, tmpReg8); |
| |
| mt_usb_phy_recover(); |
| |
| glue->base = (void*) 0x11100000; |
| glue->otg_board_data.dev = udev; |
| glue->musb_pdata.config = &mt8xx_musb_hdrc_config; |
| glue->musb_pdata.platform_ops = &mt8xx_ops; |
| glue->musb_pdata.board_data = &glue->otg_board_data; |
| glue->musb_pdata.mode = MUSB_PERIPHERAL; |
| |
| musb_writeb(glue->base, MUSB_POWER, 0); |
| |
| l1ints = musb_readl(glue->base, USB_L1INTS); |
| l1intm = musb_readl(glue->base, USB_L1INTM); |
| musb_writew(glue->base, MUSB_INTRRX, 0xffff); |
| musb_writew(glue->base, MUSB_INTRTX, 0xffff); |
| musb_writeb(glue->base, MUSB_INTRUSB, 0xff); |
| musb_writeb(glue->base, 0xb, 39); |
| |
| glue->musb = musb_register(&glue->musb_pdata, |
| (struct device *)&glue->otg_board_data, |
| glue->base); |
| if (ret) |
| return ret; |
| |
| return 0; |
| } |
| |
| static int mt8xx_remove(struct udevice *udev) |
| { |
| return 0; |
| } |
| |
| static const struct udevice_id mtk_musb_ids[] = { |
| { .compatible = "mediatek,mtk-musb" }, |
| { } |
| }; |
| #if 0 |
| U_BOOT_DRIVER(usb_dev_generic_drv) = { |
| .id = UCLASS_USB_DEV_GENERIC, |
| .name = "usb_dev_generic_drv", |
| }; |
| UCLASS_DRIVER(usb_dev_generic) = { |
| .id = UCLASS_USB_DEV_GENERIC, |
| .name = "usb_dev_generic", |
| }; |
| #endif |
| U_BOOT_DRIVER(mtk_musb) = { |
| .name = "mtk-musb", |
| .id = UCLASS_USB_GADGET_GENERIC, |
| .of_match = mtk_musb_ids, |
| .ops = &mt8xx_ops, |
| .probe = mt8xx_probe, |
| .remove = mt8xx_remove, |
| .platdata_auto_alloc_size = sizeof(struct mt8xx_glue), |
| }; |