]>
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 | { | |
afbdbd37 PC |
77 | struct device *dev = musb->controller->parent; |
78 | ||
79 | if (dev->of_node) | |
80 | musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); | |
81 | else | |
82 | musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); | |
97b9b7dc | 83 | if (IS_ERR(musb->xceiv)) { |
afbdbd37 | 84 | dev_err(dev, "No transceiver configured\n"); |
97b9b7dc | 85 | return PTR_ERR(musb->xceiv); |
10434d27 AS |
86 | } |
87 | ||
88 | /* Silicon does not implement ConfigData register. | |
89 | * Set dyn_fifo to avoid reading EP config from hardware. | |
90 | */ | |
91 | musb->dyn_fifo = true; | |
92 | ||
93 | musb->isr = jz4740_musb_interrupt; | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
7f6283ed TL |
98 | /* |
99 | * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, | |
100 | * so let's not set up the dma function pointers yet. | |
101 | */ | |
10434d27 | 102 | static const struct musb_platform_ops jz4740_musb_ops = { |
f8e9f34f | 103 | .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, |
8a77f05a | 104 | .fifo_mode = 2, |
10434d27 | 105 | .init = jz4740_musb_init, |
10434d27 AS |
106 | }; |
107 | ||
108 | static int jz4740_probe(struct platform_device *pdev) | |
109 | { | |
110 | struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data; | |
111 | struct platform_device *musb; | |
112 | struct jz4740_glue *glue; | |
113 | struct clk *clk; | |
114 | int ret; | |
115 | ||
116 | glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); | |
117 | if (!glue) | |
118 | return -ENOMEM; | |
119 | ||
120 | musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); | |
121 | if (!musb) { | |
122 | dev_err(&pdev->dev, "failed to allocate musb device\n"); | |
123 | return -ENOMEM; | |
124 | } | |
125 | ||
126 | clk = devm_clk_get(&pdev->dev, "udc"); | |
127 | if (IS_ERR(clk)) { | |
128 | dev_err(&pdev->dev, "failed to get clock\n"); | |
129 | ret = PTR_ERR(clk); | |
130 | goto err_platform_device_put; | |
131 | } | |
132 | ||
133 | ret = clk_prepare_enable(clk); | |
134 | if (ret) { | |
135 | dev_err(&pdev->dev, "failed to enable clock\n"); | |
136 | goto err_platform_device_put; | |
137 | } | |
138 | ||
139 | musb->dev.parent = &pdev->dev; | |
140 | ||
141 | glue->dev = &pdev->dev; | |
142 | glue->musb = musb; | |
143 | glue->clk = clk; | |
144 | ||
145 | pdata->platform_ops = &jz4740_musb_ops; | |
146 | ||
147 | platform_set_drvdata(pdev, glue); | |
148 | ||
149 | ret = platform_device_add_resources(musb, pdev->resource, | |
150 | pdev->num_resources); | |
151 | if (ret) { | |
152 | dev_err(&pdev->dev, "failed to add resources\n"); | |
153 | goto err_clk_disable; | |
154 | } | |
155 | ||
156 | ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | |
157 | if (ret) { | |
158 | dev_err(&pdev->dev, "failed to add platform_data\n"); | |
159 | goto err_clk_disable; | |
160 | } | |
161 | ||
162 | ret = platform_device_add(musb); | |
163 | if (ret) { | |
164 | dev_err(&pdev->dev, "failed to register musb device\n"); | |
165 | goto err_clk_disable; | |
166 | } | |
167 | ||
168 | return 0; | |
169 | ||
170 | err_clk_disable: | |
171 | clk_disable_unprepare(clk); | |
172 | err_platform_device_put: | |
173 | platform_device_put(musb); | |
174 | return ret; | |
175 | } | |
176 | ||
177 | static int jz4740_remove(struct platform_device *pdev) | |
178 | { | |
179 | struct jz4740_glue *glue = platform_get_drvdata(pdev); | |
180 | ||
181 | platform_device_unregister(glue->musb); | |
182 | clk_disable_unprepare(glue->clk); | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
e2d5e094 PC |
187 | #ifdef CONFIG_OF |
188 | static const struct of_device_id jz4740_musb_of_match[] = { | |
189 | { .compatible = "ingenic,jz4740-musb" }, | |
190 | {}, | |
191 | }; | |
192 | MODULE_DEVICE_TABLE(of, jz4740_musb_of_match); | |
193 | #endif | |
194 | ||
10434d27 AS |
195 | static struct platform_driver jz4740_driver = { |
196 | .probe = jz4740_probe, | |
197 | .remove = jz4740_remove, | |
198 | .driver = { | |
199 | .name = "musb-jz4740", | |
e2d5e094 | 200 | .of_match_table = of_match_ptr(jz4740_musb_of_match), |
10434d27 AS |
201 | }, |
202 | }; | |
203 | ||
204 | MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer"); | |
205 | MODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>"); | |
206 | MODULE_LICENSE("GPL v2"); | |
207 | module_platform_driver(jz4740_driver); |