Skip to content

Commit 7a96b6e

Browse files
ConchuODgregkh
authored andcommitted
usb: musb: Add support for PolarFire SoC's musb controller
Add support for Microchips's PolarFire SoC's musb controller in host, peripheral and otg mode. Tested-by: Valentina Fernandez <[email protected]> Signed-off-by: Conor Dooley <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 44830e1 commit 7a96b6e

File tree

3 files changed

+278
-1
lines changed

3 files changed

+278
-1
lines changed

drivers/usb/musb/Kconfig

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ config USB_MUSB_MEDIATEK
123123
select GENERIC_PHY
124124
select USB_ROLE_SWITCH
125125

126+
config USB_MUSB_POLARFIRE_SOC
127+
tristate "Microchip PolarFire SoC platforms"
128+
depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST
129+
depends on NOP_USB_XCEIV
130+
select USB_MUSB_DUAL_ROLE
131+
help
132+
Say Y here to enable support for USB on Microchip's PolarFire SoC.
133+
134+
This support is also available as a module. If so, the module
135+
will be called mpfs.
136+
126137
comment "MUSB DMA mode"
127138

128139
config MUSB_PIO_ONLY
@@ -146,7 +157,7 @@ config USB_UX500_DMA
146157

147158
config USB_INVENTRA_DMA
148159
bool 'Inventra'
149-
depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740
160+
depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740 || USB_MUSB_POLARFIRE_SOC
150161
help
151162
Enable DMA transfers using Mentor's engine.
152163

drivers/usb/musb/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
2525
obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o
2626
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
2727
obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o
28+
obj-$(CONFIG_USB_MUSB_POLARFIRE_SOC) += mpfs.o
2829

2930
# the kconfig must guarantee that only one of the
3031
# possible I/O schemes will be enabled at a time ...

drivers/usb/musb/mpfs.c

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* PolarFire SoC (MPFS) MUSB Glue Layer
4+
*
5+
* Copyright (c) 2020-2022 Microchip Corporation. All rights reserved.
6+
* Based on {omap2430,tusb6010,ux500}.c
7+
*
8+
*/
9+
10+
#include <linux/clk.h>
11+
#include <linux/dma-mapping.h>
12+
#include <linux/err.h>
13+
#include <linux/io.h>
14+
#include <linux/kernel.h>
15+
#include <linux/module.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/usb/usb_phy_generic.h>
18+
#include "musb_core.h"
19+
#include "musb_dma.h"
20+
21+
#define MPFS_MUSB_MAX_EP_NUM 8
22+
#define MPFS_MUSB_RAM_BITS 12
23+
24+
struct mpfs_glue {
25+
struct device *dev;
26+
struct platform_device *musb;
27+
struct platform_device *phy;
28+
struct clk *clk;
29+
};
30+
31+
static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
32+
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
33+
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
34+
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
35+
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
36+
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
37+
{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
38+
{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 1024, },
39+
{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 4096, },
40+
};
41+
42+
static const struct musb_hdrc_config mpfs_musb_hdrc_config = {
43+
.fifo_cfg = mpfs_musb_mode_cfg,
44+
.fifo_cfg_size = ARRAY_SIZE(mpfs_musb_mode_cfg),
45+
.multipoint = true,
46+
.dyn_fifo = true,
47+
.num_eps = MPFS_MUSB_MAX_EP_NUM,
48+
.ram_bits = MPFS_MUSB_RAM_BITS,
49+
};
50+
51+
static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
52+
{
53+
unsigned long flags;
54+
irqreturn_t ret = IRQ_NONE;
55+
struct musb *musb = __hci;
56+
57+
spin_lock_irqsave(&musb->lock, flags);
58+
59+
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
60+
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
61+
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
62+
63+
if (musb->int_usb || musb->int_tx || musb->int_rx) {
64+
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
65+
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
66+
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
67+
ret = musb_interrupt(musb);
68+
}
69+
70+
spin_unlock_irqrestore(&musb->lock, flags);
71+
72+
return ret;
73+
}
74+
75+
static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
76+
{
77+
u8 devctl;
78+
79+
/*
80+
* HDRC controls CPEN, but beware current surges during device
81+
* connect. They can trigger transient overcurrent conditions
82+
* that must be ignored.
83+
*/
84+
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
85+
86+
if (is_on) {
87+
musb->is_active = 1;
88+
musb->xceiv->otg->default_a = 1;
89+
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
90+
devctl |= MUSB_DEVCTL_SESSION;
91+
MUSB_HST_MODE(musb);
92+
} else {
93+
musb->is_active = 0;
94+
95+
/*
96+
* NOTE: skipping A_WAIT_VFALL -> A_IDLE and
97+
* jumping right to B_IDLE...
98+
*/
99+
musb->xceiv->otg->default_a = 0;
100+
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
101+
devctl &= ~MUSB_DEVCTL_SESSION;
102+
103+
MUSB_DEV_MODE(musb);
104+
}
105+
106+
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
107+
108+
dev_dbg(musb->controller, "VBUS %s, devctl %02x\n",
109+
usb_otg_state_string(musb->xceiv->otg->state),
110+
musb_readb(musb->mregs, MUSB_DEVCTL));
111+
}
112+
113+
static int mpfs_musb_init(struct musb *musb)
114+
{
115+
struct device *dev = musb->controller;
116+
117+
musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
118+
if (IS_ERR(musb->xceiv)) {
119+
dev_err(dev, "HS UDC: no transceiver configured\n");
120+
return PTR_ERR(musb->xceiv);
121+
}
122+
123+
musb->dyn_fifo = true;
124+
musb->isr = mpfs_musb_interrupt;
125+
126+
musb_platform_set_vbus(musb, 1);
127+
128+
return 0;
129+
}
130+
131+
static const struct musb_platform_ops mpfs_ops = {
132+
.quirks = MUSB_DMA_INVENTRA,
133+
.init = mpfs_musb_init,
134+
.fifo_mode = 2,
135+
#ifdef CONFIG_USB_INVENTRA_DMA
136+
.dma_init = musbhs_dma_controller_create,
137+
.dma_exit = musbhs_dma_controller_destroy,
138+
#endif
139+
.set_vbus = mpfs_musb_set_vbus
140+
};
141+
142+
static int mpfs_probe(struct platform_device *pdev)
143+
{
144+
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
145+
struct mpfs_glue *glue;
146+
struct platform_device *musb_pdev;
147+
struct device *dev = &pdev->dev;
148+
struct clk *clk;
149+
int ret;
150+
151+
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
152+
if (!glue)
153+
return -ENOMEM;
154+
155+
musb_pdev = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
156+
if (!musb_pdev) {
157+
dev_err(dev, "failed to allocate musb device\n");
158+
return -ENOMEM;
159+
}
160+
161+
clk = devm_clk_get(&pdev->dev, NULL);
162+
if (IS_ERR(clk)) {
163+
dev_err(&pdev->dev, "failed to get clock\n");
164+
ret = PTR_ERR(clk);
165+
goto err_phy_release;
166+
}
167+
168+
ret = clk_prepare_enable(clk);
169+
if (ret) {
170+
dev_err(&pdev->dev, "failed to enable clock\n");
171+
goto err_phy_release;
172+
}
173+
174+
musb_pdev->dev.parent = dev;
175+
musb_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(39);
176+
musb_pdev->dev.dma_mask = &musb_pdev->dev.coherent_dma_mask;
177+
device_set_of_node_from_dev(&musb_pdev->dev, dev);
178+
179+
glue->dev = dev;
180+
glue->musb = musb_pdev;
181+
glue->clk = clk;
182+
183+
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
184+
if (!pdata)
185+
goto err_clk_disable;
186+
187+
pdata->config = &mpfs_musb_hdrc_config;
188+
pdata->platform_ops = &mpfs_ops;
189+
190+
pdata->mode = usb_get_dr_mode(dev);
191+
if (pdata->mode == USB_DR_MODE_UNKNOWN) {
192+
dev_info(dev, "No dr_mode property found, defaulting to otg\n");
193+
pdata->mode = USB_DR_MODE_OTG;
194+
}
195+
196+
glue->phy = usb_phy_generic_register();
197+
if (IS_ERR(glue->phy)) {
198+
dev_err(dev, "failed to register usb-phy %ld\n",
199+
PTR_ERR(glue->phy));
200+
goto err_clk_disable;
201+
}
202+
203+
platform_set_drvdata(pdev, glue);
204+
205+
ret = platform_device_add_resources(musb_pdev, pdev->resource, pdev->num_resources);
206+
if (ret) {
207+
dev_err(dev, "failed to add resources\n");
208+
goto err_clk_disable;
209+
}
210+
211+
ret = platform_device_add_data(musb_pdev, pdata, sizeof(*pdata));
212+
if (ret) {
213+
dev_err(dev, "failed to add platform_data\n");
214+
goto err_clk_disable;
215+
}
216+
217+
ret = platform_device_add(musb_pdev);
218+
if (ret) {
219+
dev_err(dev, "failed to register musb device\n");
220+
goto err_clk_disable;
221+
}
222+
223+
dev_info(&pdev->dev, "Registered MPFS MUSB driver\n");
224+
return 0;
225+
226+
err_clk_disable:
227+
clk_disable_unprepare(clk);
228+
229+
err_phy_release:
230+
usb_phy_generic_unregister(glue->phy);
231+
platform_device_put(musb_pdev);
232+
return ret;
233+
}
234+
235+
static int mpfs_remove(struct platform_device *pdev)
236+
{
237+
struct mpfs_glue *glue = platform_get_drvdata(pdev);
238+
239+
platform_device_unregister(glue->musb);
240+
usb_phy_generic_unregister(pdev);
241+
242+
return 0;
243+
}
244+
245+
#ifdef CONFIG_OF
246+
static const struct of_device_id mpfs_id_table[] = {
247+
{ .compatible = "microchip,mpfs-musb" },
248+
{ }
249+
};
250+
MODULE_DEVICE_TABLE(of, mpfs_id_table);
251+
#endif
252+
253+
static struct platform_driver mpfs_musb_driver = {
254+
.probe = mpfs_probe,
255+
.remove = mpfs_remove,
256+
.driver = {
257+
.name = "mpfs-musb",
258+
.of_match_table = of_match_ptr(mpfs_id_table)
259+
},
260+
};
261+
262+
module_platform_driver(mpfs_musb_driver);
263+
264+
MODULE_DESCRIPTION("PolarFire SoC MUSB Glue Layer");
265+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)