source: trunk/target/linux/ar7/files/drivers/vlynq/vlynq.c @ 9656

Last change on this file since 9656 was 9656, checked in by nbd, 10 years ago

Fix VLYNQ device enable for DG834Gv1

This patch allows VLYNQ devices on the DG834Gv1 to be successfully
enabled.

Currently the "vlynq_enable_device" function attempts to set the VLYNQ
device clock divisor to values from 1 through 8 until a link is
successfully established. On the DG834Gv1 (but not the DG834Gv2),
setting the VLYNQ device clock divisor to 1 (full rate) results in all
further VLYNQ operations failing (including software reset), so the
device is never enabled. This patches changes the function to only
attempt divisors 2 through 8, and hence the device is successfully
enabled.

Signed-off-by: Nick Forbes <nick.forbes@…>


File size: 16.3 KB
Line 
1/*
2 * Copyright (C) 2006, 2007 OpenWrt.org
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include <linux/init.h>
20#include <linux/types.h>
21#include <linux/kernel.h>
22#include <linux/string.h>
23#include <linux/device.h>
24#include <linux/module.h>
25#include <linux/errno.h>
26#include <linux/platform_device.h>
27#include <linux/interrupt.h>
28#include <linux/device.h>
29#include <linux/io.h>
30
31#include <linux/vlynq.h>
32
33#define VLYNQ_CTRL_PM_ENABLE            0x80000000
34#define VLYNQ_CTRL_CLOCK_INT            0x00008000
35#define VLYNQ_CTRL_CLOCK_DIV(x)         (((x) & 7) << 16)
36#define VLYNQ_CTRL_INT_LOCAL            0x00004000
37#define VLYNQ_CTRL_INT_ENABLE           0x00002000
38#define VLYNQ_CTRL_INT_VECTOR(x)        (((x) & 0x1f) << 8)
39#define VLYNQ_CTRL_INT2CFG              0x00000080
40#define VLYNQ_CTRL_RESET                0x00000001
41
42#define VLYNQ_INT_OFFSET                0x00000014
43#define VLYNQ_REMOTE_OFFSET             0x00000080
44
45#define VLYNQ_STATUS_LINK               0x00000001
46#define VLYNQ_STATUS_LERROR             0x00000080
47#define VLYNQ_STATUS_RERROR             0x00000100
48
49#define VINT_ENABLE                     0x00000100
50#define VINT_TYPE_EDGE                  0x00000080
51#define VINT_LEVEL_LOW                  0x00000040
52#define VINT_VECTOR(x)                  ((x) & 0x1f)
53#define VINT_OFFSET(irq)                (8 * ((irq) % 4))
54
55#define VLYNQ_AUTONEGO_V2               0x00010000
56
57struct vlynq_regs {
58        u32 revision;
59        u32 control;
60        u32 status;
61        u32 int_prio;
62        u32 int_status;
63        u32 int_pending;
64        u32 int_ptr;
65        u32 tx_offset;
66        struct vlynq_mapping rx_mapping[4];
67        u32 chip;
68        u32 autonego;
69        u32 unused[6];
70        u32 int_device[8];
71} __attribute__ ((packed));
72
73#define vlynq_reg_read(reg) readl(&(reg))
74#define vlynq_reg_write(reg, val)  writel(val, &(reg))
75
76static int __vlynq_enable_device(struct vlynq_device *dev);
77
78#ifdef VLYNQ_DEBUG
79static void vlynq_dump_regs(struct vlynq_device *dev)
80{
81        int i;
82        printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n",
83                        dev->local, dev->remote);
84        for (i = 0; i < 32; i++) {
85                printk(KERN_DEBUG "VLYNQ: local %d: %08x\n",
86                        i + 1, ((u32 *)dev->local)[i]);
87                printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n",
88                        i + 1, ((u32 *)dev->remote)[i]);
89        }
90}
91
92static void vlynq_dump_mem(u32 *base, int count)
93{
94        int i;
95        for (i = 0; i < (count + 3) / 4; i++) {
96                if (i % 4 == 0) printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4);
97                printk(KERN_DEBUG " 0x%08x", *(base + i));
98        }
99        printk(KERN_DEBUG "\n");
100}
101#endif
102
103int vlynq_linked(struct vlynq_device *dev)
104{
105        int i;
106
107        for (i = 0; i < 100; i++)
108                if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK)
109                        return 1;
110                else
111                        cpu_relax();
112
113        return 0;
114}
115
116static void vlynq_irq_unmask(unsigned int irq)
117{
118        u32 val;
119        struct vlynq_device *dev = get_irq_chip_data(irq);
120        int virq;
121
122        BUG_ON(!dev);
123        virq = irq - dev->irq_start;
124        val = vlynq_reg_read(dev->remote->int_device[virq >> 2]);
125        val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq);
126        vlynq_reg_write(dev->remote->int_device[virq >> 2], val);
127}
128
129static void vlynq_irq_mask(unsigned int irq)
130{
131        u32 val;
132        struct vlynq_device *dev = get_irq_chip_data(irq);
133        int virq;
134
135        BUG_ON(!dev);
136        virq = irq - dev->irq_start;
137        val = vlynq_reg_read(dev->remote->int_device[virq >> 2]);
138        val &= ~(VINT_ENABLE << VINT_OFFSET(virq));
139        vlynq_reg_write(dev->remote->int_device[virq >> 2], val);
140}
141
142static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
143{
144        u32 val;
145        struct vlynq_device *dev = get_irq_chip_data(irq);
146        int virq;
147
148        BUG_ON(!dev);
149        virq = irq - dev->irq_start;
150        val = vlynq_reg_read(dev->remote->int_device[virq >> 2]);
151        switch (flow_type & IRQ_TYPE_SENSE_MASK) {
152        case IRQ_TYPE_EDGE_RISING:
153        case IRQ_TYPE_EDGE_FALLING:
154        case IRQ_TYPE_EDGE_BOTH:
155                val |= VINT_TYPE_EDGE << VINT_OFFSET(virq);
156                val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
157                break;
158        case IRQ_TYPE_LEVEL_HIGH:
159                val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
160                val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
161                break;
162        case IRQ_TYPE_LEVEL_LOW:
163                val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
164                val |= VINT_LEVEL_LOW << VINT_OFFSET(virq);
165                break;
166        default:
167                return -EINVAL;
168        }
169        vlynq_reg_write(dev->remote->int_device[virq >> 2], val);
170        return 0;
171}
172
173static void vlynq_local_ack(unsigned int irq)
174{
175        struct vlynq_device *dev = get_irq_chip_data(irq);
176        u32 status = vlynq_reg_read(dev->local->status);
177        if (printk_ratelimit())
178                printk(KERN_DEBUG "%s: local status: 0x%08x\n",
179                       dev->dev.bus_id, status);
180        vlynq_reg_write(dev->local->status, status);
181}
182
183static void vlynq_remote_ack(unsigned int irq)
184{
185        struct vlynq_device *dev = get_irq_chip_data(irq);
186        u32 status = vlynq_reg_read(dev->remote->status);
187        if (printk_ratelimit())
188                printk(KERN_DEBUG "%s: remote status: 0x%08x\n",
189                       dev->dev.bus_id, status);
190        vlynq_reg_write(dev->remote->status, status);
191}
192
193static irqreturn_t vlynq_irq(int irq, void *dev_id)
194{
195        struct vlynq_device *dev = dev_id;
196        u32 status;
197        int virq = 0;
198
199        status = vlynq_reg_read(dev->local->int_status);
200        vlynq_reg_write(dev->local->int_status, status);
201
202        if (unlikely(!status))
203                spurious_interrupt();
204
205        while (status) {
206                if (status & 1)
207                        do_IRQ(dev->irq_start + virq);
208                status >>= 1;
209                virq++;
210        }
211
212        return IRQ_HANDLED;
213}
214
215static struct irq_chip vlynq_irq_chip = {
216        .name = "vlynq",
217        .unmask = vlynq_irq_unmask,
218        .mask = vlynq_irq_mask,
219        .set_type = vlynq_irq_type,
220};
221
222static struct irq_chip vlynq_local_chip = {
223        .name = "vlynq local error",
224        .unmask = vlynq_irq_unmask,
225        .mask = vlynq_irq_mask,
226        .ack = vlynq_local_ack,
227};
228
229static struct irq_chip vlynq_remote_chip = {
230        .name = "vlynq local error",
231        .unmask = vlynq_irq_unmask,
232        .mask = vlynq_irq_mask,
233        .ack = vlynq_remote_ack,
234};
235
236static int vlynq_setup_irq(struct vlynq_device *dev)
237{
238        u32 val;
239        int i, virq;
240
241        if (dev->local_irq == dev->remote_irq) {
242                printk(KERN_ERR
243                       "%s: local vlynq irq should be different from remote\n",
244                       dev->dev.bus_id);
245                return -EINVAL;
246        }
247
248        /* Clear local and remote error bits */
249        vlynq_reg_write(dev->local->status, vlynq_reg_read(dev->local->status));
250        vlynq_reg_write(dev->remote->status,
251                        vlynq_reg_read(dev->remote->status));
252
253        /* Now setup interrupts */
254        val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq);
255        val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
256                VLYNQ_CTRL_INT2CFG;
257        val |= vlynq_reg_read(dev->local->control);
258        vlynq_reg_write(dev->local->int_ptr, VLYNQ_INT_OFFSET);
259        vlynq_reg_write(dev->local->control, val);
260
261        val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
262        val |= VLYNQ_CTRL_INT_ENABLE;
263        val |= vlynq_reg_read(dev->remote->control);
264        vlynq_reg_write(dev->remote->int_ptr, VLYNQ_INT_OFFSET);
265        vlynq_reg_write(dev->remote->control, val);
266
267        for (i = dev->irq_start; i <= dev->irq_end; i++) {
268                virq = i - dev->irq_start;
269                if (virq == dev->local_irq) {
270                        set_irq_chip_and_handler(i, &vlynq_local_chip,
271                                                 handle_level_irq);
272                        set_irq_chip_data(i, dev);
273                } else if (virq == dev->remote_irq) {
274                        set_irq_chip_and_handler(i, &vlynq_remote_chip,
275                                                 handle_level_irq);
276                        set_irq_chip_data(i, dev);
277                } else {
278                        set_irq_chip_and_handler(i, &vlynq_irq_chip,
279                                                 handle_simple_irq);
280                        set_irq_chip_data(i, dev);
281                        vlynq_reg_write(dev->remote->int_device[virq >> 2], 0);
282                }
283        }
284
285        if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) {
286                printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id);
287                return -EAGAIN;
288        }
289
290        return 0;
291}
292
293static void vlynq_device_release(struct device *dev)
294{
295        struct vlynq_device *vdev = to_vlynq_device(dev);
296        kfree(vdev);
297}
298
299static int vlynq_device_match(struct device *dev,
300                              struct device_driver *drv)
301{
302        struct vlynq_device *vdev = to_vlynq_device(dev);
303        struct vlynq_driver *vdrv = to_vlynq_driver(drv);
304        struct plat_vlynq_ops *ops = dev->platform_data;
305        struct vlynq_device_id *ids = vdrv->id_table;
306        u32 id = 0;
307        int result;
308
309        while (ids->id) {
310                vdev->divisor = ids->divisor;
311                result = __vlynq_enable_device(vdev);
312                if (result == 0) {
313                        id = vlynq_reg_read(vdev->remote->chip);
314                        ops->off(vdev);
315                        if (ids->id == id) {
316                                vlynq_set_drvdata(vdev, ids);
317                                return 1;
318                        }
319                }
320                ids++;
321        }
322        return 0;
323}
324
325static int vlynq_device_probe(struct device *dev)
326{
327        struct vlynq_device *vdev = to_vlynq_device(dev);
328        struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
329        struct vlynq_device_id *id = vlynq_get_drvdata(vdev);
330        int result = -ENODEV;
331
332        get_device(dev);
333        if (drv && drv->probe)
334                result = drv->probe(vdev, id);
335        if (result)
336                put_device(dev);
337        return result;
338}
339
340static int vlynq_device_remove(struct device *dev)
341{
342        struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
343        if (drv && drv->remove)
344                drv->remove(to_vlynq_device(dev));
345        put_device(dev);
346        return 0;
347}
348
349int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner)
350{
351        driver->driver.name = driver->name;
352        driver->driver.bus = &vlynq_bus_type;
353        return driver_register(&driver->driver);
354}
355EXPORT_SYMBOL(__vlynq_register_driver);
356
357void vlynq_unregister_driver(struct vlynq_driver *driver)
358{
359        driver_unregister(&driver->driver);
360}
361EXPORT_SYMBOL(vlynq_unregister_driver);
362
363static int __vlynq_enable_device(struct vlynq_device *dev)
364{
365        int i, result;
366        struct plat_vlynq_ops *ops = dev->dev.platform_data;
367
368        result = ops->on(dev);
369        if (result)
370                return result;
371
372        switch (dev->divisor) {
373        case vlynq_div_auto:
374                /* Only try locally supplied clock, others cause problems */
375                vlynq_reg_write(dev->remote->control, 0);
376                for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) {
377                        vlynq_reg_write(dev->local->control,
378                                        VLYNQ_CTRL_CLOCK_INT |
379                                        VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1));
380                        if (vlynq_linked(dev)) {
381                                printk(KERN_DEBUG
382                                       "%s: using local clock divisor %d\n",
383                                       dev->dev.bus_id, i - vlynq_ldiv1 + 1);
384                                dev->divisor = i;
385                                return 0;
386                        }
387                }
388        case vlynq_ldiv1: case vlynq_ldiv2: case vlynq_ldiv3: case vlynq_ldiv4:
389        case vlynq_ldiv5: case vlynq_ldiv6: case vlynq_ldiv7: case vlynq_ldiv8:
390                vlynq_reg_write(dev->remote->control, 0);
391                vlynq_reg_write(dev->local->control,
392                                VLYNQ_CTRL_CLOCK_INT |
393                                VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
394                                                     vlynq_ldiv1));
395                if (vlynq_linked(dev)) {
396                        printk(KERN_DEBUG
397                               "%s: using local clock divisor %d\n",
398                               dev->dev.bus_id, dev->divisor - vlynq_ldiv1 + 1);
399                        return 0;
400                }
401                break;
402        case vlynq_rdiv1: case vlynq_rdiv2: case vlynq_rdiv3: case vlynq_rdiv4:
403        case vlynq_rdiv5: case vlynq_rdiv6: case vlynq_rdiv7: case vlynq_rdiv8:
404                vlynq_reg_write(dev->local->control, 0);
405                vlynq_reg_write(dev->remote->control,
406                                VLYNQ_CTRL_CLOCK_INT |
407                                VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
408                                                     vlynq_rdiv1));
409                if (vlynq_linked(dev)) {
410                        printk(KERN_DEBUG
411                               "%s: using remote clock divisor %d\n",
412                               dev->dev.bus_id, dev->divisor - vlynq_rdiv1 + 1);
413                        return 0;
414                }
415                break;
416        case vlynq_div_external:
417                vlynq_reg_write(dev->local->control, 0);
418                vlynq_reg_write(dev->remote->control, 0);
419                if (vlynq_linked(dev)) {
420                        printk(KERN_DEBUG "%s: using external clock\n",
421                               dev->dev.bus_id);
422                        return 0;
423                }
424                break;
425        }
426
427        ops->off(dev);
428        return -ENODEV;
429}
430
431int vlynq_enable_device(struct vlynq_device *dev)
432{
433        struct plat_vlynq_ops *ops = dev->dev.platform_data;
434        int result = -ENODEV;
435
436        result = __vlynq_enable_device(dev);
437        if (result)
438                return result;
439
440        result = vlynq_setup_irq(dev);
441        if (result)
442                ops->off(dev);
443
444        dev->enabled = !result;
445        return result;
446}
447EXPORT_SYMBOL(vlynq_enable_device);
448
449
450void vlynq_disable_device(struct vlynq_device *dev)
451{
452        struct plat_vlynq_ops *ops = dev->dev.platform_data;
453
454        dev->enabled = 0;
455        free_irq(dev->irq, dev);
456        ops->off(dev);
457}
458EXPORT_SYMBOL(vlynq_disable_device);
459
460int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset,
461                            struct vlynq_mapping *mapping)
462{
463        int i;
464
465        if (!dev->enabled)
466                return -ENXIO;
467
468        vlynq_reg_write(dev->local->tx_offset, tx_offset);
469        for (i = 0; i < 4; i++) {
470                vlynq_reg_write(dev->local->rx_mapping[i].offset,
471                                                        mapping[i].offset);
472                vlynq_reg_write(dev->local->rx_mapping[i].size,
473                                                        mapping[i].size);
474        }
475        return 0;
476}
477EXPORT_SYMBOL(vlynq_set_local_mapping);
478
479int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset,
480                             struct vlynq_mapping *mapping)
481{
482        int i;
483
484        if (!dev->enabled)
485                return -ENXIO;
486
487        vlynq_reg_write(dev->remote->tx_offset, tx_offset);
488        for (i = 0; i < 4; i++) {
489                vlynq_reg_write(dev->remote->rx_mapping[i].offset,
490                                                        mapping[i].offset);
491                vlynq_reg_write(dev->remote->rx_mapping[i].size,
492                                                        mapping[i].size);
493        }
494        return 0;
495}
496EXPORT_SYMBOL(vlynq_set_remote_mapping);
497
498int vlynq_set_local_irq(struct vlynq_device *dev, int virq)
499{
500        int irq = dev->irq_start + virq;
501        if (dev->enabled)
502                return -EBUSY;
503
504        if ((irq < dev->irq_start) || (irq > dev->irq_end))
505                return -EINVAL;
506
507        if (virq == dev->remote_irq)
508                return -EINVAL;
509
510        dev->local_irq = virq;
511
512        return 0;
513}
514EXPORT_SYMBOL(vlynq_set_local_irq);
515
516int vlynq_set_remote_irq(struct vlynq_device *dev, int virq)
517{
518        int irq = dev->irq_start + virq;
519        if (dev->enabled)
520                return -EBUSY;
521
522        if ((irq < dev->irq_start) || (irq > dev->irq_end))
523                return -EINVAL;
524
525        if (virq == dev->local_irq)
526                return -EINVAL;
527
528        dev->remote_irq = virq;
529
530        return 0;
531}
532EXPORT_SYMBOL(vlynq_set_remote_irq);
533
534static int vlynq_probe(struct platform_device *pdev)
535{
536        struct vlynq_device *dev;
537        struct resource *regs_res, *mem_res, *irq_res;
538        int len, result;
539
540        regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
541        if (!regs_res)
542                return -ENODEV;
543
544        mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
545        if (!mem_res)
546                return -ENODEV;
547
548        irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq");
549        if (!irq_res)
550                return -ENODEV;
551
552        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
553        if (!dev) {
554                printk(KERN_ERR
555                       "vlynq: failed to allocate device structure\n");
556                return -ENOMEM;
557        }
558
559        dev->id = pdev->id;
560        dev->dev.bus = &vlynq_bus_type;
561        dev->dev.parent = &pdev->dev;
562        snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id);
563        dev->dev.bus_id[BUS_ID_SIZE - 1] = 0;
564        dev->dev.platform_data = pdev->dev.platform_data;
565        dev->dev.release = vlynq_device_release;
566
567        dev->regs_start = regs_res->start;
568        dev->regs_end = regs_res->end;
569        dev->mem_start = mem_res->start;
570        dev->mem_end = mem_res->end;
571
572        len = regs_res->end - regs_res->start;
573        if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) {
574                printk(KERN_ERR "%s: Can't request vlynq registers\n",
575                       dev->dev.bus_id);
576                result = -ENXIO;
577                goto fail_request;
578        }
579
580        dev->local = ioremap(regs_res->start, len);
581        if (!dev->local) {
582                printk(KERN_ERR "%s: Can't remap vlynq registers\n",
583                       dev->dev.bus_id);
584                result = -ENXIO;
585                goto fail_remap;
586        }
587
588        dev->remote = (struct vlynq_regs *)((void *)dev->local +
589                                            VLYNQ_REMOTE_OFFSET);
590
591        dev->irq = platform_get_irq_byname(pdev, "irq");
592        dev->irq_start = irq_res->start;
593        dev->irq_end = irq_res->end;
594        dev->local_irq = dev->irq_end - dev->irq_start;
595        dev->remote_irq = dev->local_irq - 1;
596
597        if (device_register(&dev->dev))
598                goto fail_register;
599        platform_set_drvdata(pdev, dev);
600
601        printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n",
602               dev->dev.bus_id, (void *)dev->regs_start, dev->irq,
603               (void *)dev->mem_start);
604
605        return 0;
606
607fail_register:
608        iounmap(dev->local);
609fail_remap:
610fail_request:
611        release_mem_region(regs_res->start, len);
612        kfree(dev);
613        return result;
614}
615
616static int vlynq_remove(struct platform_device *pdev)
617{
618        struct vlynq_device *dev = platform_get_drvdata(pdev);
619
620        device_unregister(&dev->dev);
621        iounmap(dev->local);
622        release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start);
623
624        kfree(dev);
625
626        return 0;
627}
628
629static struct platform_driver vlynq_driver = {
630        .driver.name = "vlynq",
631        .probe = vlynq_probe,
632        .remove = __devexit_p(vlynq_remove),
633};
634
635struct bus_type vlynq_bus_type = {
636        .name = "vlynq",
637        .match = vlynq_device_match,
638        .probe = vlynq_device_probe,
639        .remove = vlynq_device_remove,
640};
641EXPORT_SYMBOL(vlynq_bus_type);
642
643static int __devinit vlynq_init(void)
644{
645        int res = 0;
646
647        res = bus_register(&vlynq_bus_type);
648        if (res)
649                goto fail_bus;
650
651        res = platform_driver_register(&vlynq_driver);
652        if (res)
653                goto fail_platform;
654
655        return 0;
656
657fail_platform:
658        bus_unregister(&vlynq_bus_type);
659fail_bus:
660        return res;
661}
662
663static void __devexit vlynq_exit(void)
664{
665        platform_driver_unregister(&vlynq_driver);
666        bus_unregister(&vlynq_bus_type);
667}
668
669module_init(vlynq_init);
670module_exit(vlynq_exit);
Note: See TracBrowser for help on using the repository browser.