]>
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> | |
13 | #include <linux/platform_device.h> | |
3d75bd3d | 14 | #include <linux/usb/usb_phy_generic.h> |
10434d27 AS |
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 | ||
1e572aa5 | 58 | static const struct musb_hdrc_config jz4740_musb_config = { |
10434d27 AS |
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 | { | |
3d75bd3d | 76 | usb_phy_generic_register(); |
10434d27 | 77 | musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); |
97b9b7dc | 78 | if (IS_ERR(musb->xceiv)) { |
10434d27 | 79 | pr_err("HS UDC: no transceiver configured\n"); |
97b9b7dc | 80 | return PTR_ERR(musb->xceiv); |
10434d27 AS |
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 | ||
7f6283ed TL |
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 | */ | |
10434d27 | 104 | static const struct musb_platform_ops jz4740_musb_ops = { |
f8e9f34f | 105 | .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, |
8a77f05a | 106 | .fifo_mode = 2, |
10434d27 AS |
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); | |
3d75bd3d | 185 | usb_phy_generic_unregister(pdev); |
10434d27 AS |
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); |