Root/target/linux/ubicom32/files/drivers/uio/uio_ubicom32ring.c

1/*
2 * drivers/uio/uio_ubicom32ring.c
3 *
4 * Userspace I/O platform driver for Ubicom32 ring buffers
5 *
6 * (C) Copyright 2009, Ubicom, Inc.
7 *
8 * This file is part of the Ubicom32 Linux Kernel Port.
9 *
10 * Based on uio_ubicom32ring.c by Magnus Damm
11 *
12 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
13 * it and/or modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
18 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
19 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
20 * the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with the Ubicom32 Linux Kernel Port. If not,
24 * see <http://www.gnu.org/licenses/>.
25 */
26
27#include <linux/platform_device.h>
28#include <linux/uio_driver.h>
29#include <linux/spinlock.h>
30#include <linux/bitops.h>
31#include <linux/interrupt.h>
32#include <linux/stringify.h>
33
34#include <asm/ip5000.h>
35#include <asm/ubicom32ring.h>
36
37#define DRIVER_NAME "uio_ubicom32ring"
38
39struct uio_ubicom32ring_data {
40    struct uio_info *uioinfo;
41
42    struct uio_ubicom32ring_regs *regs;
43
44    /*
45     * IRQ used to kick the ring buffer
46     */
47    int irq_tx;
48    int irq_rx;
49
50    spinlock_t lock;
51
52    unsigned long flags;
53
54    char name[0];
55};
56
57static irqreturn_t uio_ubicom32ring_handler(int irq, struct uio_info *dev_info)
58{
59    struct uio_ubicom32ring_data *priv = dev_info->priv;
60
61    /* Just disable the interrupt in the interrupt controller, and
62     * remember the state so we can allow user space to enable it later.
63     */
64
65    if (!test_and_set_bit(0, &priv->flags))
66        disable_irq_nosync(irq);
67
68    return IRQ_HANDLED;
69}
70
71static int uio_ubicom32ring_irqcontrol(struct uio_info *dev_info, s32 irq_on)
72{
73    struct uio_ubicom32ring_data *priv = dev_info->priv;
74    unsigned long flags;
75
76    /* Allow user space to enable and disable the interrupt
77     * in the interrupt controller, but keep track of the
78     * state to prevent per-irq depth damage.
79     *
80     * Serialize this operation to support multiple tasks.
81     */
82
83    spin_lock_irqsave(&priv->lock, flags);
84
85    if (irq_on & 2) {
86        /*
87         * Kick the ring buffer (if we can)
88         */
89        if (priv->irq_tx != 0xFF) {
90            ubicom32_set_interrupt(priv->irq_tx);
91        }
92    }
93
94    if (priv->irq_rx != 0xFF) {
95        if (irq_on & 1) {
96            if (test_and_clear_bit(0, &priv->flags))
97                enable_irq(dev_info->irq);
98        } else {
99            if (!test_and_set_bit(0, &priv->flags))
100                disable_irq(dev_info->irq);
101        }
102    }
103
104    spin_unlock_irqrestore(&priv->lock, flags);
105
106    return 0;
107}
108
109static int uio_ubicom32ring_probe(struct platform_device *pdev)
110{
111    struct uio_info *uioinfo;
112    struct uio_mem *uiomem;
113    struct uio_ubicom32ring_data *priv;
114    struct uio_ubicom32ring_regs *regs;
115    struct resource *mem_resource;
116    struct resource *irqtx_resource;
117    struct resource *irqrx_resource;
118    int ret = -EINVAL;
119    int i;
120
121    uioinfo = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
122    if (!uioinfo) {
123        dev_err(&pdev->dev, "unable to kmalloc\n");
124        return -ENOMEM;
125    }
126
127    /*
128     * Allocate private data with some string space after
129     */
130    i = sizeof(DRIVER_NAME) + 1;
131    i += pdev->dev.platform_data ? strlen(pdev->dev.platform_data) : 0;
132    priv = kzalloc(sizeof(struct uio_ubicom32ring_data) + i, GFP_KERNEL);
133    if (!priv) {
134        dev_err(&pdev->dev, "unable to kmalloc\n");
135        kfree(uioinfo);
136        return -ENOMEM;
137    }
138
139    strcpy(priv->name, DRIVER_NAME ":");
140    if (pdev->dev.platform_data) {
141        strcat(priv->name, pdev->dev.platform_data);
142    }
143    uioinfo->priv = priv;
144    uioinfo->name = priv->name;
145    uioinfo->version = "0.1";
146
147    priv->uioinfo = uioinfo;
148    spin_lock_init(&priv->lock);
149    priv->flags = 0; /* interrupt is enabled to begin with */
150
151    /*
152     * Get our resources, the IRQ_TX and IRQ_RX are optional.
153     */
154    priv->irq_tx = 0xFF;
155    irqtx_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
156    if (irqtx_resource) {
157        priv->irq_tx = irqtx_resource->start;
158    }
159
160    uioinfo->irq = -1;
161    priv->irq_rx = 0xFF;
162    irqrx_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
163    if (irqrx_resource) {
164        priv->irq_rx = irqrx_resource->start;
165        uioinfo->irq = priv->irq_rx;
166        uioinfo->handler = uio_ubicom32ring_handler;
167    }
168
169    mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
170    if (!mem_resource || !mem_resource->start) {
171        dev_err(&pdev->dev, "No valid memory resource found\n");
172        ret = -ENODEV;
173        goto fail;
174    }
175    regs = (struct uio_ubicom32ring_regs *)mem_resource->start;
176    priv->regs = regs;
177
178    if (regs->version != UIO_UBICOM32RING_REG_VERSION) {
179        dev_err(&pdev->dev, "version %d not supported\n", regs->version);
180        ret = -ENODEV;
181        goto fail;
182    }
183
184    /*
185     * First range is the shared register space, if we have any
186     */
187    uiomem = &uioinfo->mem[0];
188    if (regs->regs_size) {
189        uiomem->memtype = UIO_MEM_PHYS;
190        uiomem->addr = (u32_t)regs->regs;
191        uiomem->size = regs->regs_size;
192        ++uiomem;
193        dev_info(&pdev->dev, "regs:%p (%u) / rings: %d found\n", regs->regs, regs->regs_size, regs->num_rings);
194    } else {
195        dev_info(&pdev->dev, "rings: %d found\n", regs->num_rings);
196    }
197
198    /*
199     * The rest of the range correspond to the rings
200     */
201    for (i = 0; i < regs->num_rings; i++) {
202        dev_info(&pdev->dev, "\t%d: entries:%d ring:%p\n",
203             i, regs->rings[i]->entries, &(regs->rings[i]->ring));
204        if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
205            dev_warn(&pdev->dev, "device has more than "
206                    __stringify(MAX_UIO_MAPS)
207                    " I/O memory resources.\n");
208            break;
209        }
210
211        uiomem->memtype = UIO_MEM_PHYS;
212        uiomem->addr = (u32_t)&(regs->rings[i]->head);
213        uiomem->size = (regs->rings[i]->entries * sizeof(u32_t)) +
214                sizeof(struct uio_ubicom32ring_desc);
215        ++uiomem;
216    }
217
218    while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
219        uiomem->size = 0;
220        ++uiomem;
221    }
222
223    /* This driver requires no hardware specific kernel code to handle
224     * interrupts. Instead, the interrupt handler simply disables the
225     * interrupt in the interrupt controller. User space is responsible
226     * for performing hardware specific acknowledge and re-enabling of
227     * the interrupt in the interrupt controller.
228     *
229     * Interrupt sharing is not supported.
230     */
231    uioinfo->irq_flags = IRQF_DISABLED;
232    uioinfo->irqcontrol = uio_ubicom32ring_irqcontrol;
233
234    ret = uio_register_device(&pdev->dev, priv->uioinfo);
235    if (ret) {
236        dev_err(&pdev->dev, "unable to register uio device\n");
237        goto fail;
238    }
239
240    platform_set_drvdata(pdev, priv);
241
242    dev_info(&pdev->dev, "'%s' using irq: rx %d tx %d, regs %p\n",
243         priv->name, priv->irq_rx, priv->irq_tx, priv->regs);
244
245    return 0;
246
247fail:
248    kfree(uioinfo);
249    kfree(priv);
250    return ret;
251}
252
253static int uio_ubicom32ring_remove(struct platform_device *pdev)
254{
255    struct uio_ubicom32ring_data *priv = platform_get_drvdata(pdev);
256
257    uio_unregister_device(priv->uioinfo);
258    kfree(priv->uioinfo);
259    kfree(priv);
260    return 0;
261}
262
263static struct platform_driver uio_ubicom32ring = {
264    .probe = uio_ubicom32ring_probe,
265    .remove = uio_ubicom32ring_remove,
266    .driver = {
267        .name = DRIVER_NAME,
268        .owner = THIS_MODULE,
269    },
270};
271
272static int __init uio_ubicom32ring_init(void)
273{
274    return platform_driver_register(&uio_ubicom32ring);
275}
276
277static void __exit uio_ubicom32ring_exit(void)
278{
279    platform_driver_unregister(&uio_ubicom32ring);
280}
281
282module_init(uio_ubicom32ring_init);
283module_exit(uio_ubicom32ring_exit);
284
285MODULE_AUTHOR("Patrick Tjin");
286MODULE_DESCRIPTION("Userspace I/O driver for Ubicom32 ring buffers");
287MODULE_LICENSE("GPL v2");
288MODULE_ALIAS("platform:" DRIVER_NAME);
289

Archive Download this file



interactive