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