1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Ingenic JZ4740 "glue layer" 4 * 5 * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net> 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/dma-mapping.h> 10 #include <linux/errno.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/of_device.h> 14 #include <linux/platform_device.h> 15 #include <linux/usb/usb_phy_generic.h> 16 17 #include "musb_core.h" 18 19 struct jz4740_glue { 20 struct platform_device *pdev; 21 struct clk *clk; 22 }; 23 24 static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) 25 { 26 unsigned long flags; 27 irqreturn_t retval = IRQ_NONE; 28 struct musb *musb = __hci; 29 30 spin_lock_irqsave(&musb->lock, flags); 31 32 musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 33 musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 34 musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 35 36 /* 37 * The controller is gadget only, the state of the host mode IRQ bits is 38 * undefined. Mask them to make sure that the musb driver core will 39 * never see them set 40 */ 41 musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | 42 MUSB_INTR_RESET | MUSB_INTR_SOF; 43 44 if (musb->int_usb || musb->int_tx || musb->int_rx) 45 retval = musb_interrupt(musb); 46 47 spin_unlock_irqrestore(&musb->lock, flags); 48 49 return retval; 50 } 51 52 static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { 53 { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, 54 { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, 55 { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, 56 }; 57 58 static const struct musb_hdrc_config jz4740_musb_config = { 59 /* Silicon does not implement USB OTG. */ 60 .multipoint = 0, 61 /* Max EPs scanned, driver will decide which EP can be used. */ 62 .num_eps = 4, 63 /* RAMbits needed to configure EPs from table */ 64 .ram_bits = 9, 65 .fifo_cfg = jz4740_musb_fifo_cfg, 66 .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), 67 }; 68 69 static int jz4740_musb_init(struct musb *musb) 70 { 71 struct device *dev = musb->controller->parent; 72 int err; 73 74 if (dev->of_node) 75 musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); 76 else 77 musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 78 if (IS_ERR(musb->xceiv)) { 79 err = PTR_ERR(musb->xceiv); 80 if (err != -EPROBE_DEFER) 81 dev_err(dev, "No transceiver configured: %d", err); 82 return err; 83 } 84 85 /* 86 * Silicon does not implement ConfigData register. 87 * Set dyn_fifo to avoid reading EP config from hardware. 88 */ 89 musb->dyn_fifo = true; 90 91 musb->isr = jz4740_musb_interrupt; 92 93 return 0; 94 } 95 96 /* 97 * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, 98 * so let's not set up the dma function pointers yet. 99 */ 100 static const struct musb_platform_ops jz4740_musb_ops = { 101 .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, 102 .fifo_mode = 2, 103 .init = jz4740_musb_init, 104 }; 105 106 static const struct musb_hdrc_platform_data jz4740_musb_pdata = { 107 .mode = MUSB_PERIPHERAL, 108 .config = &jz4740_musb_config, 109 .platform_ops = &jz4740_musb_ops, 110 }; 111 112 static int jz4740_probe(struct platform_device *pdev) 113 { 114 struct device *dev = &pdev->dev; 115 const struct musb_hdrc_platform_data *pdata = &jz4740_musb_pdata; 116 struct platform_device *musb; 117 struct jz4740_glue *glue; 118 struct clk *clk; 119 int ret; 120 121 glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); 122 if (!glue) 123 return -ENOMEM; 124 125 musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); 126 if (!musb) { 127 dev_err(dev, "failed to allocate musb device"); 128 return -ENOMEM; 129 } 130 131 clk = devm_clk_get(dev, "udc"); 132 if (IS_ERR(clk)) { 133 dev_err(dev, "failed to get clock"); 134 ret = PTR_ERR(clk); 135 goto err_platform_device_put; 136 } 137 138 ret = clk_prepare_enable(clk); 139 if (ret) { 140 dev_err(dev, "failed to enable clock"); 141 goto err_platform_device_put; 142 } 143 144 musb->dev.parent = dev; 145 146 glue->pdev = musb; 147 glue->clk = clk; 148 149 platform_set_drvdata(pdev, glue); 150 151 ret = platform_device_add_resources(musb, pdev->resource, 152 pdev->num_resources); 153 if (ret) { 154 dev_err(dev, "failed to add resources"); 155 goto err_clk_disable; 156 } 157 158 ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); 159 if (ret) { 160 dev_err(dev, "failed to add platform_data"); 161 goto err_clk_disable; 162 } 163 164 ret = platform_device_add(musb); 165 if (ret) { 166 dev_err(dev, "failed to register musb device"); 167 goto err_clk_disable; 168 } 169 170 return 0; 171 172 err_clk_disable: 173 clk_disable_unprepare(clk); 174 err_platform_device_put: 175 platform_device_put(musb); 176 return ret; 177 } 178 179 static int jz4740_remove(struct platform_device *pdev) 180 { 181 struct jz4740_glue *glue = platform_get_drvdata(pdev); 182 183 platform_device_unregister(glue->pdev); 184 clk_disable_unprepare(glue->clk); 185 186 return 0; 187 } 188 189 #ifdef CONFIG_OF 190 static const struct of_device_id jz4740_musb_of_match[] = { 191 { .compatible = "ingenic,jz4740-musb" }, 192 { /* sentinel */ }, 193 }; 194 MODULE_DEVICE_TABLE(of, jz4740_musb_of_match); 195 #endif 196 197 static struct platform_driver jz4740_driver = { 198 .probe = jz4740_probe, 199 .remove = jz4740_remove, 200 .driver = { 201 .name = "musb-jz4740", 202 .of_match_table = of_match_ptr(jz4740_musb_of_match), 203 }, 204 }; 205 206 MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer"); 207 MODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>"); 208 MODULE_LICENSE("GPL v2"); 209 module_platform_driver(jz4740_driver); 210