18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Ingenic JZ4740 "glue layer" 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/usb/role.h> 168c2ecf20Sopenharmony_ci#include <linux/usb/usb_phy_generic.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "musb_core.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct jz4740_glue { 218c2ecf20Sopenharmony_ci struct platform_device *pdev; 228c2ecf20Sopenharmony_ci struct musb *musb; 238c2ecf20Sopenharmony_ci struct clk *clk; 248c2ecf20Sopenharmony_ci struct usb_role_switch *role_sw; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned long flags; 308c2ecf20Sopenharmony_ci irqreturn_t retval = IRQ_NONE, retval_dma = IRQ_NONE; 318c2ecf20Sopenharmony_ci struct musb *musb = __hci; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_USB_INVENTRA_DMA) && musb->dma_controller) 348c2ecf20Sopenharmony_ci retval_dma = dma_controller_irq(irq, musb->dma_controller); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 398c2ecf20Sopenharmony_ci musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 408c2ecf20Sopenharmony_ci musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * The controller is gadget only, the state of the host mode IRQ bits is 448c2ecf20Sopenharmony_ci * undefined. Mask them to make sure that the musb driver core will 458c2ecf20Sopenharmony_ci * never see them set 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | 488c2ecf20Sopenharmony_ci MUSB_INTR_RESET | MUSB_INTR_SOF; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (musb->int_usb || musb->int_tx || musb->int_rx) 518c2ecf20Sopenharmony_ci retval = musb_interrupt(musb); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (retval == IRQ_HANDLED || retval_dma == IRQ_HANDLED) 568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return IRQ_NONE; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { 628c2ecf20Sopenharmony_ci { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, 638c2ecf20Sopenharmony_ci { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, 648c2ecf20Sopenharmony_ci { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic const struct musb_hdrc_config jz4740_musb_config = { 688c2ecf20Sopenharmony_ci /* Silicon does not implement USB OTG. */ 698c2ecf20Sopenharmony_ci .multipoint = 0, 708c2ecf20Sopenharmony_ci /* Max EPs scanned, driver will decide which EP can be used. */ 718c2ecf20Sopenharmony_ci .num_eps = 4, 728c2ecf20Sopenharmony_ci /* RAMbits needed to configure EPs from table */ 738c2ecf20Sopenharmony_ci .ram_bits = 9, 748c2ecf20Sopenharmony_ci .fifo_cfg = jz4740_musb_fifo_cfg, 758c2ecf20Sopenharmony_ci .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int jz4740_musb_role_switch_set(struct usb_role_switch *sw, 798c2ecf20Sopenharmony_ci enum usb_role role) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct jz4740_glue *glue = usb_role_switch_get_drvdata(sw); 828c2ecf20Sopenharmony_ci struct usb_phy *phy = glue->musb->xceiv; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci switch (role) { 858c2ecf20Sopenharmony_ci case USB_ROLE_NONE: 868c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&phy->notifier, USB_EVENT_NONE, phy); 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case USB_ROLE_DEVICE: 898c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&phy->notifier, USB_EVENT_VBUS, phy); 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case USB_ROLE_HOST: 928c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy); 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int jz4740_musb_init(struct musb *musb) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct device *dev = musb->controller->parent; 1028c2ecf20Sopenharmony_ci struct jz4740_glue *glue = dev_get_drvdata(dev); 1038c2ecf20Sopenharmony_ci struct usb_role_switch_desc role_sw_desc = { 1048c2ecf20Sopenharmony_ci .set = jz4740_musb_role_switch_set, 1058c2ecf20Sopenharmony_ci .driver_data = glue, 1068c2ecf20Sopenharmony_ci .fwnode = dev_fwnode(dev), 1078c2ecf20Sopenharmony_ci }; 1088c2ecf20Sopenharmony_ci int err; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci glue->musb = musb; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (dev->of_node) 1138c2ecf20Sopenharmony_ci musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); 1148c2ecf20Sopenharmony_ci else 1158c2ecf20Sopenharmony_ci musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 1168c2ecf20Sopenharmony_ci if (IS_ERR(musb->xceiv)) { 1178c2ecf20Sopenharmony_ci err = PTR_ERR(musb->xceiv); 1188c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 1198c2ecf20Sopenharmony_ci dev_err(dev, "No transceiver configured: %d", err); 1208c2ecf20Sopenharmony_ci return err; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci glue->role_sw = usb_role_switch_register(dev, &role_sw_desc); 1248c2ecf20Sopenharmony_ci if (IS_ERR(glue->role_sw)) { 1258c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register USB role switch"); 1268c2ecf20Sopenharmony_ci return PTR_ERR(glue->role_sw); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Silicon does not implement ConfigData register. 1318c2ecf20Sopenharmony_ci * Set dyn_fifo to avoid reading EP config from hardware. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci musb->dyn_fifo = true; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci musb->isr = jz4740_musb_interrupt; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int jz4740_musb_exit(struct musb *musb) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct jz4740_glue *glue = dev_get_drvdata(musb->controller->parent); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci usb_role_switch_unregister(glue->role_sw); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct musb_platform_ops jz4740_musb_ops = { 1508c2ecf20Sopenharmony_ci .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, 1518c2ecf20Sopenharmony_ci .fifo_mode = 2, 1528c2ecf20Sopenharmony_ci .init = jz4740_musb_init, 1538c2ecf20Sopenharmony_ci .exit = jz4740_musb_exit, 1548c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_INVENTRA_DMA 1558c2ecf20Sopenharmony_ci .dma_init = musbhs_dma_controller_create_noirq, 1568c2ecf20Sopenharmony_ci .dma_exit = musbhs_dma_controller_destroy, 1578c2ecf20Sopenharmony_ci#endif 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct musb_hdrc_platform_data jz4740_musb_pdata = { 1618c2ecf20Sopenharmony_ci .mode = MUSB_PERIPHERAL, 1628c2ecf20Sopenharmony_ci .config = &jz4740_musb_config, 1638c2ecf20Sopenharmony_ci .platform_ops = &jz4740_musb_ops, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = { 1678c2ecf20Sopenharmony_ci { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, 1688c2ecf20Sopenharmony_ci { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, 1698c2ecf20Sopenharmony_ci { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, 1708c2ecf20Sopenharmony_ci { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, 1718c2ecf20Sopenharmony_ci { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, 1728c2ecf20Sopenharmony_ci { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, 1738c2ecf20Sopenharmony_ci { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, 1748c2ecf20Sopenharmony_ci { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, 1758c2ecf20Sopenharmony_ci { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, 1768c2ecf20Sopenharmony_ci { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic struct musb_hdrc_config jz4770_musb_config = { 1808c2ecf20Sopenharmony_ci .multipoint = 1, 1818c2ecf20Sopenharmony_ci .num_eps = 11, 1828c2ecf20Sopenharmony_ci .ram_bits = 11, 1838c2ecf20Sopenharmony_ci .fifo_cfg = jz4770_musb_fifo_cfg, 1848c2ecf20Sopenharmony_ci .fifo_cfg_size = ARRAY_SIZE(jz4770_musb_fifo_cfg), 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct musb_hdrc_platform_data jz4770_musb_pdata = { 1888c2ecf20Sopenharmony_ci .mode = MUSB_PERIPHERAL, /* TODO: support OTG */ 1898c2ecf20Sopenharmony_ci .config = &jz4770_musb_config, 1908c2ecf20Sopenharmony_ci .platform_ops = &jz4740_musb_ops, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int jz4740_probe(struct platform_device *pdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1968c2ecf20Sopenharmony_ci const struct musb_hdrc_platform_data *pdata; 1978c2ecf20Sopenharmony_ci struct platform_device *musb; 1988c2ecf20Sopenharmony_ci struct jz4740_glue *glue; 1998c2ecf20Sopenharmony_ci struct clk *clk; 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); 2038c2ecf20Sopenharmony_ci if (!glue) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pdata = of_device_get_match_data(dev); 2078c2ecf20Sopenharmony_ci if (!pdata) { 2088c2ecf20Sopenharmony_ci dev_err(dev, "missing platform data"); 2098c2ecf20Sopenharmony_ci return -EINVAL; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); 2138c2ecf20Sopenharmony_ci if (!musb) { 2148c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate musb device"); 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, "udc"); 2198c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 2208c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock"); 2218c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 2228c2ecf20Sopenharmony_ci goto err_platform_device_put; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 2268c2ecf20Sopenharmony_ci if (ret) { 2278c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable clock"); 2288c2ecf20Sopenharmony_ci goto err_platform_device_put; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci musb->dev.parent = dev; 2328c2ecf20Sopenharmony_ci musb->dev.dma_mask = &musb->dev.coherent_dma_mask; 2338c2ecf20Sopenharmony_ci musb->dev.coherent_dma_mask = DMA_BIT_MASK(32); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci glue->pdev = musb; 2368c2ecf20Sopenharmony_ci glue->clk = clk; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, glue); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = platform_device_add_resources(musb, pdev->resource, 2418c2ecf20Sopenharmony_ci pdev->num_resources); 2428c2ecf20Sopenharmony_ci if (ret) { 2438c2ecf20Sopenharmony_ci dev_err(dev, "failed to add resources"); 2448c2ecf20Sopenharmony_ci goto err_clk_disable; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); 2488c2ecf20Sopenharmony_ci if (ret) { 2498c2ecf20Sopenharmony_ci dev_err(dev, "failed to add platform_data"); 2508c2ecf20Sopenharmony_ci goto err_clk_disable; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = platform_device_add(musb); 2548c2ecf20Sopenharmony_ci if (ret) { 2558c2ecf20Sopenharmony_ci dev_err(dev, "failed to register musb device"); 2568c2ecf20Sopenharmony_ci goto err_clk_disable; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cierr_clk_disable: 2628c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 2638c2ecf20Sopenharmony_cierr_platform_device_put: 2648c2ecf20Sopenharmony_ci platform_device_put(musb); 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int jz4740_remove(struct platform_device *pdev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct jz4740_glue *glue = platform_get_drvdata(pdev); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci platform_device_unregister(glue->pdev); 2738c2ecf20Sopenharmony_ci clk_disable_unprepare(glue->clk); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic const struct of_device_id jz4740_musb_of_match[] = { 2798c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-musb", .data = &jz4740_musb_pdata }, 2808c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-musb", .data = &jz4770_musb_pdata }, 2818c2ecf20Sopenharmony_ci { /* sentinel */ }, 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4740_musb_of_match); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic struct platform_driver jz4740_driver = { 2868c2ecf20Sopenharmony_ci .probe = jz4740_probe, 2878c2ecf20Sopenharmony_ci .remove = jz4740_remove, 2888c2ecf20Sopenharmony_ci .driver = { 2898c2ecf20Sopenharmony_ci .name = "musb-jz4740", 2908c2ecf20Sopenharmony_ci .of_match_table = jz4740_musb_of_match, 2918c2ecf20Sopenharmony_ci }, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("JZ4740 MUSB Glue Layer"); 2958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>"); 2968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2978c2ecf20Sopenharmony_cimodule_platform_driver(jz4740_driver); 298