17a96b6eaSConor Dooley // SPDX-License-Identifier: GPL-2.0 27a96b6eaSConor Dooley /* 37a96b6eaSConor Dooley * PolarFire SoC (MPFS) MUSB Glue Layer 47a96b6eaSConor Dooley * 57a96b6eaSConor Dooley * Copyright (c) 2020-2022 Microchip Corporation. All rights reserved. 67a96b6eaSConor Dooley * Based on {omap2430,tusb6010,ux500}.c 77a96b6eaSConor Dooley * 87a96b6eaSConor Dooley */ 97a96b6eaSConor Dooley 107a96b6eaSConor Dooley #include <linux/clk.h> 117a96b6eaSConor Dooley #include <linux/dma-mapping.h> 127a96b6eaSConor Dooley #include <linux/err.h> 137a96b6eaSConor Dooley #include <linux/io.h> 147a96b6eaSConor Dooley #include <linux/kernel.h> 157a96b6eaSConor Dooley #include <linux/module.h> 167a96b6eaSConor Dooley #include <linux/platform_device.h> 177a96b6eaSConor Dooley #include <linux/usb/usb_phy_generic.h> 187a96b6eaSConor Dooley #include "musb_core.h" 197a96b6eaSConor Dooley #include "musb_dma.h" 207a96b6eaSConor Dooley 217a96b6eaSConor Dooley #define MPFS_MUSB_MAX_EP_NUM 8 227a96b6eaSConor Dooley #define MPFS_MUSB_RAM_BITS 12 237a96b6eaSConor Dooley 247a96b6eaSConor Dooley struct mpfs_glue { 257a96b6eaSConor Dooley struct device *dev; 267a96b6eaSConor Dooley struct platform_device *musb; 277a96b6eaSConor Dooley struct platform_device *phy; 287a96b6eaSConor Dooley struct clk *clk; 297a96b6eaSConor Dooley }; 307a96b6eaSConor Dooley 317a96b6eaSConor Dooley static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = { 327a96b6eaSConor Dooley { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, 337a96b6eaSConor Dooley { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, 347a96b6eaSConor Dooley { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, 357a96b6eaSConor Dooley { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, 367a96b6eaSConor Dooley { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, 377a96b6eaSConor Dooley { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, 387a96b6eaSConor Dooley { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 1024, }, 397a96b6eaSConor Dooley { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 4096, }, 407a96b6eaSConor Dooley }; 417a96b6eaSConor Dooley 427a96b6eaSConor Dooley static const struct musb_hdrc_config mpfs_musb_hdrc_config = { 437a96b6eaSConor Dooley .fifo_cfg = mpfs_musb_mode_cfg, 447a96b6eaSConor Dooley .fifo_cfg_size = ARRAY_SIZE(mpfs_musb_mode_cfg), 457a96b6eaSConor Dooley .multipoint = true, 467a96b6eaSConor Dooley .dyn_fifo = true, 477a96b6eaSConor Dooley .num_eps = MPFS_MUSB_MAX_EP_NUM, 487a96b6eaSConor Dooley .ram_bits = MPFS_MUSB_RAM_BITS, 497a96b6eaSConor Dooley }; 507a96b6eaSConor Dooley 517a96b6eaSConor Dooley static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci) 527a96b6eaSConor Dooley { 537a96b6eaSConor Dooley unsigned long flags; 547a96b6eaSConor Dooley irqreturn_t ret = IRQ_NONE; 557a96b6eaSConor Dooley struct musb *musb = __hci; 567a96b6eaSConor Dooley 577a96b6eaSConor Dooley spin_lock_irqsave(&musb->lock, flags); 587a96b6eaSConor Dooley 597a96b6eaSConor Dooley musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 607a96b6eaSConor Dooley musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 617a96b6eaSConor Dooley musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 627a96b6eaSConor Dooley 637a96b6eaSConor Dooley if (musb->int_usb || musb->int_tx || musb->int_rx) { 647a96b6eaSConor Dooley musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 657a96b6eaSConor Dooley musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 667a96b6eaSConor Dooley musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 677a96b6eaSConor Dooley ret = musb_interrupt(musb); 687a96b6eaSConor Dooley } 697a96b6eaSConor Dooley 707a96b6eaSConor Dooley spin_unlock_irqrestore(&musb->lock, flags); 717a96b6eaSConor Dooley 727a96b6eaSConor Dooley return ret; 737a96b6eaSConor Dooley } 747a96b6eaSConor Dooley 757a96b6eaSConor Dooley static void mpfs_musb_set_vbus(struct musb *musb, int is_on) 767a96b6eaSConor Dooley { 777a96b6eaSConor Dooley u8 devctl; 787a96b6eaSConor Dooley 797a96b6eaSConor Dooley /* 807a96b6eaSConor Dooley * HDRC controls CPEN, but beware current surges during device 817a96b6eaSConor Dooley * connect. They can trigger transient overcurrent conditions 827a96b6eaSConor Dooley * that must be ignored. 837a96b6eaSConor Dooley */ 847a96b6eaSConor Dooley devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 857a96b6eaSConor Dooley 867a96b6eaSConor Dooley if (is_on) { 877a96b6eaSConor Dooley musb->is_active = 1; 887a96b6eaSConor Dooley musb->xceiv->otg->default_a = 1; 897a96b6eaSConor Dooley musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; 907a96b6eaSConor Dooley devctl |= MUSB_DEVCTL_SESSION; 917a96b6eaSConor Dooley MUSB_HST_MODE(musb); 927a96b6eaSConor Dooley } else { 937a96b6eaSConor Dooley musb->is_active = 0; 947a96b6eaSConor Dooley 957a96b6eaSConor Dooley /* 967a96b6eaSConor Dooley * NOTE: skipping A_WAIT_VFALL -> A_IDLE and 977a96b6eaSConor Dooley * jumping right to B_IDLE... 987a96b6eaSConor Dooley */ 997a96b6eaSConor Dooley musb->xceiv->otg->default_a = 0; 1007a96b6eaSConor Dooley musb->xceiv->otg->state = OTG_STATE_B_IDLE; 1017a96b6eaSConor Dooley devctl &= ~MUSB_DEVCTL_SESSION; 1027a96b6eaSConor Dooley 1037a96b6eaSConor Dooley MUSB_DEV_MODE(musb); 1047a96b6eaSConor Dooley } 1057a96b6eaSConor Dooley 1067a96b6eaSConor Dooley musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); 1077a96b6eaSConor Dooley 1087a96b6eaSConor Dooley dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", 1097a96b6eaSConor Dooley usb_otg_state_string(musb->xceiv->otg->state), 1107a96b6eaSConor Dooley musb_readb(musb->mregs, MUSB_DEVCTL)); 1117a96b6eaSConor Dooley } 1127a96b6eaSConor Dooley 1137a96b6eaSConor Dooley static int mpfs_musb_init(struct musb *musb) 1147a96b6eaSConor Dooley { 1157a96b6eaSConor Dooley struct device *dev = musb->controller; 1167a96b6eaSConor Dooley 1177a96b6eaSConor Dooley musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 1187a96b6eaSConor Dooley if (IS_ERR(musb->xceiv)) { 1197a96b6eaSConor Dooley dev_err(dev, "HS UDC: no transceiver configured\n"); 1207a96b6eaSConor Dooley return PTR_ERR(musb->xceiv); 1217a96b6eaSConor Dooley } 1227a96b6eaSConor Dooley 1237a96b6eaSConor Dooley musb->dyn_fifo = true; 1247a96b6eaSConor Dooley musb->isr = mpfs_musb_interrupt; 1257a96b6eaSConor Dooley 1267a96b6eaSConor Dooley musb_platform_set_vbus(musb, 1); 1277a96b6eaSConor Dooley 1287a96b6eaSConor Dooley return 0; 1297a96b6eaSConor Dooley } 1307a96b6eaSConor Dooley 1317a96b6eaSConor Dooley static const struct musb_platform_ops mpfs_ops = { 1327a96b6eaSConor Dooley .quirks = MUSB_DMA_INVENTRA, 1337a96b6eaSConor Dooley .init = mpfs_musb_init, 1347a96b6eaSConor Dooley .fifo_mode = 2, 1357a96b6eaSConor Dooley #ifdef CONFIG_USB_INVENTRA_DMA 1367a96b6eaSConor Dooley .dma_init = musbhs_dma_controller_create, 1377a96b6eaSConor Dooley .dma_exit = musbhs_dma_controller_destroy, 1387a96b6eaSConor Dooley #endif 1397a96b6eaSConor Dooley .set_vbus = mpfs_musb_set_vbus 1407a96b6eaSConor Dooley }; 1417a96b6eaSConor Dooley 1427a96b6eaSConor Dooley static int mpfs_probe(struct platform_device *pdev) 1437a96b6eaSConor Dooley { 1447a96b6eaSConor Dooley struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); 1457a96b6eaSConor Dooley struct mpfs_glue *glue; 1467a96b6eaSConor Dooley struct platform_device *musb_pdev; 1477a96b6eaSConor Dooley struct device *dev = &pdev->dev; 1487a96b6eaSConor Dooley struct clk *clk; 1497a96b6eaSConor Dooley int ret; 1507a96b6eaSConor Dooley 1517a96b6eaSConor Dooley glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); 1527a96b6eaSConor Dooley if (!glue) 1537a96b6eaSConor Dooley return -ENOMEM; 1547a96b6eaSConor Dooley 1557a96b6eaSConor Dooley musb_pdev = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); 1567a96b6eaSConor Dooley if (!musb_pdev) { 1577a96b6eaSConor Dooley dev_err(dev, "failed to allocate musb device\n"); 1587a96b6eaSConor Dooley return -ENOMEM; 1597a96b6eaSConor Dooley } 1607a96b6eaSConor Dooley 1617a96b6eaSConor Dooley clk = devm_clk_get(&pdev->dev, NULL); 1627a96b6eaSConor Dooley if (IS_ERR(clk)) { 1637a96b6eaSConor Dooley dev_err(&pdev->dev, "failed to get clock\n"); 1647a96b6eaSConor Dooley ret = PTR_ERR(clk); 1657a96b6eaSConor Dooley goto err_phy_release; 1667a96b6eaSConor Dooley } 1677a96b6eaSConor Dooley 1687a96b6eaSConor Dooley ret = clk_prepare_enable(clk); 1697a96b6eaSConor Dooley if (ret) { 1707a96b6eaSConor Dooley dev_err(&pdev->dev, "failed to enable clock\n"); 1717a96b6eaSConor Dooley goto err_phy_release; 1727a96b6eaSConor Dooley } 1737a96b6eaSConor Dooley 1747a96b6eaSConor Dooley musb_pdev->dev.parent = dev; 1757a96b6eaSConor Dooley musb_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(39); 1767a96b6eaSConor Dooley musb_pdev->dev.dma_mask = &musb_pdev->dev.coherent_dma_mask; 1777a96b6eaSConor Dooley device_set_of_node_from_dev(&musb_pdev->dev, dev); 1787a96b6eaSConor Dooley 1797a96b6eaSConor Dooley glue->dev = dev; 1807a96b6eaSConor Dooley glue->musb = musb_pdev; 1817a96b6eaSConor Dooley glue->clk = clk; 1827a96b6eaSConor Dooley 1837a96b6eaSConor Dooley pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 184*21cdd6a0SDan Carpenter if (!pdata) { 185*21cdd6a0SDan Carpenter ret = -ENOMEM; 1867a96b6eaSConor Dooley goto err_clk_disable; 187*21cdd6a0SDan Carpenter } 1887a96b6eaSConor Dooley 1897a96b6eaSConor Dooley pdata->config = &mpfs_musb_hdrc_config; 1907a96b6eaSConor Dooley pdata->platform_ops = &mpfs_ops; 1917a96b6eaSConor Dooley 1927a96b6eaSConor Dooley pdata->mode = usb_get_dr_mode(dev); 1937a96b6eaSConor Dooley if (pdata->mode == USB_DR_MODE_UNKNOWN) { 1947a96b6eaSConor Dooley dev_info(dev, "No dr_mode property found, defaulting to otg\n"); 1957a96b6eaSConor Dooley pdata->mode = USB_DR_MODE_OTG; 1967a96b6eaSConor Dooley } 1977a96b6eaSConor Dooley 1987a96b6eaSConor Dooley glue->phy = usb_phy_generic_register(); 1997a96b6eaSConor Dooley if (IS_ERR(glue->phy)) { 2007a96b6eaSConor Dooley dev_err(dev, "failed to register usb-phy %ld\n", 2017a96b6eaSConor Dooley PTR_ERR(glue->phy)); 202*21cdd6a0SDan Carpenter ret = PTR_ERR(glue->phy); 2037a96b6eaSConor Dooley goto err_clk_disable; 2047a96b6eaSConor Dooley } 2057a96b6eaSConor Dooley 2067a96b6eaSConor Dooley platform_set_drvdata(pdev, glue); 2077a96b6eaSConor Dooley 2087a96b6eaSConor Dooley ret = platform_device_add_resources(musb_pdev, pdev->resource, pdev->num_resources); 2097a96b6eaSConor Dooley if (ret) { 2107a96b6eaSConor Dooley dev_err(dev, "failed to add resources\n"); 2117a96b6eaSConor Dooley goto err_clk_disable; 2127a96b6eaSConor Dooley } 2137a96b6eaSConor Dooley 2147a96b6eaSConor Dooley ret = platform_device_add_data(musb_pdev, pdata, sizeof(*pdata)); 2157a96b6eaSConor Dooley if (ret) { 2167a96b6eaSConor Dooley dev_err(dev, "failed to add platform_data\n"); 2177a96b6eaSConor Dooley goto err_clk_disable; 2187a96b6eaSConor Dooley } 2197a96b6eaSConor Dooley 2207a96b6eaSConor Dooley ret = platform_device_add(musb_pdev); 2217a96b6eaSConor Dooley if (ret) { 2227a96b6eaSConor Dooley dev_err(dev, "failed to register musb device\n"); 2237a96b6eaSConor Dooley goto err_clk_disable; 2247a96b6eaSConor Dooley } 2257a96b6eaSConor Dooley 2267a96b6eaSConor Dooley dev_info(&pdev->dev, "Registered MPFS MUSB driver\n"); 2277a96b6eaSConor Dooley return 0; 2287a96b6eaSConor Dooley 2297a96b6eaSConor Dooley err_clk_disable: 2307a96b6eaSConor Dooley clk_disable_unprepare(clk); 2317a96b6eaSConor Dooley 2327a96b6eaSConor Dooley err_phy_release: 2337a96b6eaSConor Dooley usb_phy_generic_unregister(glue->phy); 2347a96b6eaSConor Dooley platform_device_put(musb_pdev); 2357a96b6eaSConor Dooley return ret; 2367a96b6eaSConor Dooley } 2377a96b6eaSConor Dooley 2387a96b6eaSConor Dooley static int mpfs_remove(struct platform_device *pdev) 2397a96b6eaSConor Dooley { 2407a96b6eaSConor Dooley struct mpfs_glue *glue = platform_get_drvdata(pdev); 2417a96b6eaSConor Dooley 2427a96b6eaSConor Dooley platform_device_unregister(glue->musb); 2437a96b6eaSConor Dooley usb_phy_generic_unregister(pdev); 2447a96b6eaSConor Dooley 2457a96b6eaSConor Dooley return 0; 2467a96b6eaSConor Dooley } 2477a96b6eaSConor Dooley 2487a96b6eaSConor Dooley #ifdef CONFIG_OF 2497a96b6eaSConor Dooley static const struct of_device_id mpfs_id_table[] = { 2507a96b6eaSConor Dooley { .compatible = "microchip,mpfs-musb" }, 2517a96b6eaSConor Dooley { } 2527a96b6eaSConor Dooley }; 2537a96b6eaSConor Dooley MODULE_DEVICE_TABLE(of, mpfs_id_table); 2547a96b6eaSConor Dooley #endif 2557a96b6eaSConor Dooley 2567a96b6eaSConor Dooley static struct platform_driver mpfs_musb_driver = { 2577a96b6eaSConor Dooley .probe = mpfs_probe, 2587a96b6eaSConor Dooley .remove = mpfs_remove, 2597a96b6eaSConor Dooley .driver = { 2607a96b6eaSConor Dooley .name = "mpfs-musb", 2617a96b6eaSConor Dooley .of_match_table = of_match_ptr(mpfs_id_table) 2627a96b6eaSConor Dooley }, 2637a96b6eaSConor Dooley }; 2647a96b6eaSConor Dooley 2657a96b6eaSConor Dooley module_platform_driver(mpfs_musb_driver); 2667a96b6eaSConor Dooley 2677a96b6eaSConor Dooley MODULE_DESCRIPTION("PolarFire SoC MUSB Glue Layer"); 2687a96b6eaSConor Dooley MODULE_LICENSE("GPL"); 269