Date:2013-05-21 22:56:12 (8 years 11 months ago)
Author:Apelete Seketeli
Commit:708f4c01e4b5fdc7b44e0eeb6c342e770b9a8fcb
Message:usb: musb: add support for JZ4740 usb device controller

Add support for Ingenic JZ4740 USB Device Controller through a
specific musb glue layer.

The platform data already available in tree for that USB Device
Controller was previously used by an out-of-tree USB gadget driver
which was not relying on the musb driver and was written by Ingenic
and the Qi-Hardware community.

JZ4740 UDC not being OTG compatible and missing some hardware
registers, this musb glue layer is written from scratch to be used in
gadget mode only and take silicon design specifics into account.

Signed-off-by: Apelete Seketeli <apelete@seketeli.net>
Files: arch/mips/include/asm/mach-jz4740/platform.h (1 diff)
arch/mips/jz4740/board-qi_lb60.c (1 diff)
arch/mips/jz4740/platform.c (2 diffs)
drivers/usb/musb/Kconfig (2 diffs)
drivers/usb/musb/Makefile (1 diff)
drivers/usb/musb/jz4740.c (1 diff)

Change Details

arch/mips/include/asm/mach-jz4740/platform.h
2121
2222extern struct platform_device jz4740_usb_ohci_device;
2323extern struct platform_device jz4740_udc_device;
24extern struct platform_device jz4740_udc_xceiv_device;
2425extern struct platform_device jz4740_mmc_device;
2526extern struct platform_device jz4740_rtc_device;
2627extern struct platform_device jz4740_i2c_device;
arch/mips/jz4740/board-qi_lb60.c
454454
455455static struct platform_device *jz_platform_devices[] __initdata = {
456456    &jz4740_udc_device,
457    &jz4740_udc_xceiv_device,
457458    &jz4740_mmc_device,
458459    &jz4740_nand_device,
459460    &qi_lb60_keypad,
arch/mips/jz4740/platform.c
2121
2222#include <linux/dma-mapping.h>
2323
24#include <linux/usb/musb.h>
25
2426#include <asm/mach-jz4740/platform.h>
2527#include <asm/mach-jz4740/base.h>
2628#include <asm/mach-jz4740/irq.h>
...... 
5658    .resource = jz4740_usb_ohci_resources,
5759};
5860
59/* UDC (USB gadget controller) */
60static struct resource jz4740_usb_gdt_resources[] = {
61    {
62        .start = JZ4740_UDC_BASE_ADDR,
63        .end = JZ4740_UDC_BASE_ADDR + 0x1000 - 1,
64        .flags = IORESOURCE_MEM,
61/* USB Device Controller */
62struct platform_device jz4740_udc_xceiv_device = {
63    .name = "usb_phy_gen_xceiv",
64    .id = 0,
65};
66
67static struct resource jz4740_udc_resources[] = {
68    [0] = {
69        .start = JZ4740_UDC_BASE_ADDR,
70        .end = JZ4740_UDC_BASE_ADDR + 0x10000 - 1,
71        .flags = IORESOURCE_MEM,
6572    },
66    {
67        .start = JZ4740_IRQ_UDC,
68        .end = JZ4740_IRQ_UDC,
69        .flags = IORESOURCE_IRQ,
73    [1] = {
74        .start = JZ4740_IRQ_UDC,
75        .end = JZ4740_IRQ_UDC,
76        .flags = IORESOURCE_IRQ,
77        .name = "mc",
7078    },
7179};
7280
7381struct platform_device jz4740_udc_device = {
74    .name = "jz-udc",
75    .id = -1,
76    .dev = {
77        .dma_mask = &jz4740_udc_device.dev.coherent_dma_mask,
82    .name = "musb-jz4740",
83    .id = -1,
84    .dev = {
85        .dma_mask = &jz4740_udc_device.dev.coherent_dma_mask,
7886        .coherent_dma_mask = DMA_BIT_MASK(32),
7987    },
80    .num_resources = ARRAY_SIZE(jz4740_usb_gdt_resources),
81    .resource = jz4740_usb_gdt_resources,
88    .num_resources = ARRAY_SIZE(jz4740_udc_resources),
89    .resource = jz4740_udc_resources,
8290};
8391
8492/* MMC/SD controller */
drivers/usb/musb/Kconfig
9393config USB_MUSB_UX500
9494    tristate "Ux500 platforms"
9595
96config USB_MUSB_JZ4740
97    tristate "JZ4740"
98    depends on MACH_JZ4740
99    depends on USB_MUSB_GADGET
100    depends on USB_OTG_BLACKLIST_HUB
101
96102endchoice
97103
98104config USB_MUSB_AM335X_CHILD
...... 
100106
101107choice
102108    prompt 'MUSB DMA mode'
103    default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM
109    default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM || USB_MUSB_JZ4740
104110    default USB_UX500_DMA if USB_MUSB_UX500
105111    default USB_INVENTRA_DMA if USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
106112    default USB_TI_CPPI_DMA if USB_MUSB_DAVINCI
drivers/usb/musb/Makefile
1919obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o
2020obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o
2121obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
22obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o
2223
2324
2425obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o
drivers/usb/musb/jz4740.c
1/*
2 * Ingenic JZ4740 "glue layer"
3 *
4 * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * You should have received a copy of the GNU General Public License along
12 * with this program; if not, write to the Free Software Foundation, Inc.,
13 * 675 Mass Ave, Cambridge, MA 02139, USA.
14 */
15
16#include <linux/clk.h>
17#include <linux/dma-mapping.h>
18#include <linux/errno.h>
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22
23#include "musb_core.h"
24
25struct jz4740_glue {
26    struct device *dev;
27    struct platform_device *musb;
28    struct clk *clk;
29};
30
31static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
32{
33    unsigned long flags;
34    irqreturn_t retval = IRQ_NONE;
35    struct musb *musb = __hci;
36
37    spin_lock_irqsave(&musb->lock, flags);
38
39    musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
40    musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
41    musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
42
43    /*
44     * The controller is gadget only, the state of the host mode IRQ bits is
45     * undefined. Mask them to make sure that the musb driver core will
46     * never see them set
47     */
48    musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME |
49        MUSB_INTR_RESET | MUSB_INTR_SOF;
50
51    if (musb->int_usb || musb->int_tx || musb->int_rx)
52        retval = musb_interrupt(musb);
53
54    spin_unlock_irqrestore(&musb->lock, flags);
55
56    return retval;
57}
58
59static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
60{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
61{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
62{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
63};
64static struct musb_hdrc_config jz4740_musb_config = {
65    /* Silicon does not implement USB OTG. */
66    .multipoint = 0,
67    /* Max EPs scanned, driver will decide which EP can be used. */
68    .num_eps = 4,
69    /* RAMbits needed to configure EPs from table */
70    .ram_bits = 9,
71    .fifo_cfg = jz4740_musb_fifo_cfg,
72    .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg),
73};
74
75static struct musb_hdrc_platform_data jz4740_musb_platform_data = {
76    .mode = MUSB_PERIPHERAL,
77    .config = &jz4740_musb_config,
78};
79
80static int jz4740_musb_init(struct musb *musb)
81{
82    musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
83    if (!musb->xceiv) {
84        pr_err("HS UDC: no transceiver configured\n");
85        return -ENODEV;
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
98static int jz4740_musb_exit(struct musb *musb)
99{
100    usb_put_phy(musb->xceiv);
101
102    return 0;
103}
104
105static const struct musb_platform_ops jz4740_musb_ops = {
106    .init = jz4740_musb_init,
107    .exit = jz4740_musb_exit,
108};
109
110static int jz4740_probe(struct platform_device *pdev)
111{
112    struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data;
113    struct platform_device *musb;
114    struct jz4740_glue *glue;
115    struct clk *clk;
116    int ret;
117
118    glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
119    if (!glue)
120        return -ENOMEM;
121
122    musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
123    if (!musb) {
124        dev_err(&pdev->dev, "failed to allocate musb device\n");
125        return -ENOMEM;
126    }
127
128    clk = devm_clk_get(&pdev->dev, "udc");
129    if (IS_ERR(clk)) {
130        dev_err(&pdev->dev, "failed to get clock\n");
131        ret = PTR_ERR(clk);
132        goto err_platform_device_put;
133    }
134
135    ret = clk_prepare_enable(clk);
136    if (ret) {
137        dev_err(&pdev->dev, "failed to enable clock\n");
138        goto err_platform_device_put;
139    }
140
141    musb->dev.parent = &pdev->dev;
142
143    glue->dev = &pdev->dev;
144    glue->musb = musb;
145    glue->clk = clk;
146
147    pdata->platform_ops = &jz4740_musb_ops;
148
149    platform_set_drvdata(pdev, glue);
150
151    ret = platform_device_add_resources(musb, pdev->resource,
152                        pdev->num_resources);
153    if (ret) {
154        dev_err(&pdev->dev, "failed to add resources\n");
155        goto err_clk_disable;
156    }
157
158    ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
159    if (ret) {
160        dev_err(&pdev->dev, "failed to add platform_data\n");
161        goto err_clk_disable;
162    }
163
164    ret = platform_device_add(musb);
165    if (ret) {
166        dev_err(&pdev->dev, "failed to register musb device\n");
167        goto err_clk_disable;
168    }
169
170    return 0;
171
172err_clk_disable:
173    clk_disable_unprepare(clk);
174err_platform_device_put:
175    platform_device_put(musb);
176    return ret;
177}
178
179static int jz4740_remove(struct platform_device *pdev)
180{
181    struct jz4740_glue *glue = platform_get_drvdata(pdev);
182
183    platform_device_unregister(glue->musb);
184    clk_disable_unprepare(glue->clk);
185
186    return 0;
187}
188
189static struct platform_driver jz4740_driver = {
190    .probe = jz4740_probe,
191    .remove = jz4740_remove,
192    .driver = {
193        .name = "musb-jz4740",
194    },
195};
196
197MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer");
198MODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>");
199MODULE_LICENSE("GPL v2");
200module_platform_driver(jz4740_driver);

Archive Download the corresponding diff file



interactive