GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / usb / musb / jz4740.c
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/platform_device.h>
14 #include <linux/usb/usb_phy_generic.h>
15
16 #include "musb_core.h"
17
18 struct jz4740_glue {
19         struct device           *dev;
20         struct platform_device  *musb;
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 struct musb_hdrc_platform_data jz4740_musb_platform_data = {
70         .mode   = MUSB_PERIPHERAL,
71         .config = &jz4740_musb_config,
72 };
73
74 static int jz4740_musb_init(struct musb *musb)
75 {
76         usb_phy_generic_register();
77         musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
78         if (IS_ERR(musb->xceiv)) {
79                 pr_err("HS UDC: no transceiver configured\n");
80                 return PTR_ERR(musb->xceiv);
81         }
82
83         /* Silicon does not implement ConfigData register.
84          * Set dyn_fifo to avoid reading EP config from hardware.
85          */
86         musb->dyn_fifo = true;
87
88         musb->isr = jz4740_musb_interrupt;
89
90         return 0;
91 }
92
93 static int jz4740_musb_exit(struct musb *musb)
94 {
95         usb_put_phy(musb->xceiv);
96
97         return 0;
98 }
99
100 /*
101  * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA,
102  * so let's not set up the dma function pointers yet.
103  */
104 static const struct musb_platform_ops jz4740_musb_ops = {
105         .quirks         = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
106         .fifo_mode      = 2,
107         .init           = jz4740_musb_init,
108         .exit           = jz4740_musb_exit,
109 };
110
111 static int jz4740_probe(struct platform_device *pdev)
112 {
113         struct musb_hdrc_platform_data  *pdata = &jz4740_musb_platform_data;
114         struct platform_device          *musb;
115         struct jz4740_glue              *glue;
116         struct clk                      *clk;
117         int                             ret;
118
119         glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
120         if (!glue)
121                 return -ENOMEM;
122
123         musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
124         if (!musb) {
125                 dev_err(&pdev->dev, "failed to allocate musb device\n");
126                 return -ENOMEM;
127         }
128
129         clk = devm_clk_get(&pdev->dev, "udc");
130         if (IS_ERR(clk)) {
131                 dev_err(&pdev->dev, "failed to get clock\n");
132                 ret = PTR_ERR(clk);
133                 goto err_platform_device_put;
134         }
135
136         ret = clk_prepare_enable(clk);
137         if (ret) {
138                 dev_err(&pdev->dev, "failed to enable clock\n");
139                 goto err_platform_device_put;
140         }
141
142         musb->dev.parent                = &pdev->dev;
143
144         glue->dev                       = &pdev->dev;
145         glue->musb                      = musb;
146         glue->clk                       = clk;
147
148         pdata->platform_ops             = &jz4740_musb_ops;
149
150         platform_set_drvdata(pdev, glue);
151
152         ret = platform_device_add_resources(musb, pdev->resource,
153                                             pdev->num_resources);
154         if (ret) {
155                 dev_err(&pdev->dev, "failed to add resources\n");
156                 goto err_clk_disable;
157         }
158
159         ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
160         if (ret) {
161                 dev_err(&pdev->dev, "failed to add platform_data\n");
162                 goto err_clk_disable;
163         }
164
165         ret = platform_device_add(musb);
166         if (ret) {
167                 dev_err(&pdev->dev, "failed to register musb device\n");
168                 goto err_clk_disable;
169         }
170
171         return 0;
172
173 err_clk_disable:
174         clk_disable_unprepare(clk);
175 err_platform_device_put:
176         platform_device_put(musb);
177         return ret;
178 }
179
180 static int jz4740_remove(struct platform_device *pdev)
181 {
182         struct jz4740_glue      *glue = platform_get_drvdata(pdev);
183
184         platform_device_unregister(glue->musb);
185         usb_phy_generic_unregister(pdev);
186         clk_disable_unprepare(glue->clk);
187
188         return 0;
189 }
190
191 static struct platform_driver jz4740_driver = {
192         .probe          = jz4740_probe,
193         .remove         = jz4740_remove,
194         .driver         = {
195                 .name   = "musb-jz4740",
196         },
197 };
198
199 MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer");
200 MODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>");
201 MODULE_LICENSE("GPL v2");
202 module_platform_driver(jz4740_driver);