linux/drivers/vlynq/vlynq.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006, 2007 Eugene Konev <ejka@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 * Parts of the VLYNQ specification can be found here:
  19 * http://www.ti.com/litv/pdf/sprue36a
  20 */
  21
  22#include <linux/init.h>
  23#include <linux/types.h>
  24#include <linux/kernel.h>
  25#include <linux/string.h>
  26#include <linux/device.h>
  27#include <linux/module.h>
  28#include <linux/errno.h>
  29#include <linux/platform_device.h>
  30#include <linux/interrupt.h>
  31#include <linux/delay.h>
  32#include <linux/io.h>
  33#include <linux/slab.h>
  34
  35#include <linux/vlynq.h>
  36
  37#define VLYNQ_CTRL_PM_ENABLE            0x80000000
  38#define VLYNQ_CTRL_CLOCK_INT            0x00008000
  39#define VLYNQ_CTRL_CLOCK_DIV(x)         (((x) & 7) << 16)
  40#define VLYNQ_CTRL_INT_LOCAL            0x00004000
  41#define VLYNQ_CTRL_INT_ENABLE           0x00002000
  42#define VLYNQ_CTRL_INT_VECTOR(x)        (((x) & 0x1f) << 8)
  43#define VLYNQ_CTRL_INT2CFG              0x00000080
  44#define VLYNQ_CTRL_RESET                0x00000001
  45
  46#define VLYNQ_CTRL_CLOCK_MASK          (0x7 << 16)
  47
  48#define VLYNQ_INT_OFFSET                0x00000014
  49#define VLYNQ_REMOTE_OFFSET             0x00000080
  50
  51#define VLYNQ_STATUS_LINK               0x00000001
  52#define VLYNQ_STATUS_LERROR             0x00000080
  53#define VLYNQ_STATUS_RERROR             0x00000100
  54
  55#define VINT_ENABLE                     0x00000100
  56#define VINT_TYPE_EDGE                  0x00000080
  57#define VINT_LEVEL_LOW                  0x00000040
  58#define VINT_VECTOR(x)                  ((x) & 0x1f)
  59#define VINT_OFFSET(irq)                (8 * ((irq) % 4))
  60
  61#define VLYNQ_AUTONEGO_V2               0x00010000
  62
  63struct vlynq_regs {
  64        u32 revision;
  65        u32 control;
  66        u32 status;
  67        u32 int_prio;
  68        u32 int_status;
  69        u32 int_pending;
  70        u32 int_ptr;
  71        u32 tx_offset;
  72        struct vlynq_mapping rx_mapping[4];
  73        u32 chip;
  74        u32 autonego;
  75        u32 unused[6];
  76        u32 int_device[8];
  77};
  78
  79#ifdef CONFIG_VLYNQ_DEBUG
  80static void vlynq_dump_regs(struct vlynq_device *dev)
  81{
  82        int i;
  83
  84        printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n",
  85                        dev->local, dev->remote);
  86        for (i = 0; i < 32; i++) {
  87                printk(KERN_DEBUG "VLYNQ: local %d: %08x\n",
  88                        i + 1, ((u32 *)dev->local)[i]);
  89                printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n",
  90                        i + 1, ((u32 *)dev->remote)[i]);
  91        }
  92}
  93
  94static void vlynq_dump_mem(u32 *base, int count)
  95{
  96        int i;
  97
  98        for (i = 0; i < (count + 3) / 4; i++) {
  99                if (i % 4 == 0)
 100                        printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4);
 101                printk(KERN_DEBUG " 0x%08x", *(base + i));
 102        }
 103        printk(KERN_DEBUG "\n");
 104}
 105#endif
 106
 107/* Check the VLYNQ link status with a given device */
 108static int vlynq_linked(struct vlynq_device *dev)
 109{
 110        int i;
 111
 112        for (i = 0; i < 100; i++)
 113                if (readl(&dev->local->status) & VLYNQ_STATUS_LINK)
 114                        return 1;
 115                else
 116                        cpu_relax();
 117
 118        return 0;
 119}
 120
 121static void vlynq_reset(struct vlynq_device *dev)
 122{
 123        writel(readl(&dev->local->control) | VLYNQ_CTRL_RESET,
 124                        &dev->local->control);
 125
 126        /* Wait for the devices to finish resetting */
 127        msleep(5);
 128
 129        /* Remove reset bit */
 130        writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET,
 131                        &dev->local->control);
 132
 133        /* Give some time for the devices to settle */
 134        msleep(5);
 135}
 136
 137static void vlynq_irq_unmask(unsigned int irq)
 138{
 139        u32 val;
 140        struct vlynq_device *dev = get_irq_chip_data(irq);
 141        int virq;
 142
 143        BUG_ON(!dev);
 144        virq = irq - dev->irq_start;
 145        val = readl(&dev->remote->int_device[virq >> 2]);
 146        val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq);
 147        writel(val, &dev->remote->int_device[virq >> 2]);
 148}
 149
 150static void vlynq_irq_mask(unsigned int irq)
 151{
 152        u32 val;
 153        struct vlynq_device *dev = get_irq_chip_data(irq);
 154        int virq;
 155
 156        BUG_ON(!dev);
 157        virq = irq - dev->irq_start;
 158        val = readl(&dev->remote->int_device[virq >> 2]);
 159        val &= ~(VINT_ENABLE << VINT_OFFSET(virq));
 160        writel(val, &dev->remote->int_device[virq >> 2]);
 161}
 162
 163static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
 164{
 165        u32 val;
 166        struct vlynq_device *dev = get_irq_chip_data(irq);
 167        int virq;
 168
 169        BUG_ON(!dev);
 170        virq = irq - dev->irq_start;
 171        val = readl(&dev->remote->int_device[virq >> 2]);
 172        switch (flow_type & IRQ_TYPE_SENSE_MASK) {
 173        case IRQ_TYPE_EDGE_RISING:
 174        case IRQ_TYPE_EDGE_FALLING:
 175        case IRQ_TYPE_EDGE_BOTH:
 176                val |= VINT_TYPE_EDGE << VINT_OFFSET(virq);
 177                val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
 178                break;
 179        case IRQ_TYPE_LEVEL_HIGH:
 180                val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
 181                val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
 182                break;
 183        case IRQ_TYPE_LEVEL_LOW:
 184                val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
 185                val |= VINT_LEVEL_LOW << VINT_OFFSET(virq);
 186                break;
 187        default:
 188                return -EINVAL;
 189        }
 190        writel(val, &dev->remote->int_device[virq >> 2]);
 191        return 0;
 192}
 193
 194static void vlynq_local_ack(unsigned int irq)
 195{
 196        struct vlynq_device *dev = get_irq_chip_data(irq);
 197
 198        u32 status = readl(&dev->local->status);
 199
 200        pr_debug("%s: local status: 0x%08x\n",
 201                       dev_name(&dev->dev), status);
 202        writel(status, &dev->local->status);
 203}
 204
 205static void vlynq_remote_ack(unsigned int irq)
 206{
 207        struct vlynq_device *dev = get_irq_chip_data(irq);
 208
 209        u32 status = readl(&dev->remote->status);
 210
 211        pr_debug("%s: remote status: 0x%08x\n",
 212                       dev_name(&dev->dev), status);
 213        writel(status, &dev->remote->status);
 214}
 215
 216static irqreturn_t vlynq_irq(int irq, void *dev_id)
 217{
 218        struct vlynq_device *dev = dev_id;
 219        u32 status;
 220        int virq = 0;
 221
 222        status = readl(&dev->local->int_status);
 223        writel(status, &dev->local->int_status);
 224
 225        if (unlikely(!status))
 226                spurious_interrupt();
 227
 228        while (status) {
 229                if (status & 1)
 230                        do_IRQ(dev->irq_start + virq);
 231                status >>= 1;
 232                virq++;
 233        }
 234
 235        return IRQ_HANDLED;
 236}
 237
 238static struct irq_chip vlynq_irq_chip = {
 239        .name = "vlynq",
 240        .unmask = vlynq_irq_unmask,
 241        .mask = vlynq_irq_mask,
 242        .set_type = vlynq_irq_type,
 243};
 244
 245static struct irq_chip vlynq_local_chip = {
 246        .name = "vlynq local error",
 247        .unmask = vlynq_irq_unmask,
 248        .mask = vlynq_irq_mask,
 249        .ack = vlynq_local_ack,
 250};
 251
 252static struct irq_chip vlynq_remote_chip = {
 253        .name = "vlynq local error",
 254        .unmask = vlynq_irq_unmask,
 255        .mask = vlynq_irq_mask,
 256        .ack = vlynq_remote_ack,
 257};
 258
 259static int vlynq_setup_irq(struct vlynq_device *dev)
 260{
 261        u32 val;
 262        int i, virq;
 263
 264        if (dev->local_irq == dev->remote_irq) {
 265                printk(KERN_ERR
 266                       "%s: local vlynq irq should be different from remote\n",
 267                       dev_name(&dev->dev));
 268                return -EINVAL;
 269        }
 270
 271        /* Clear local and remote error bits */
 272        writel(readl(&dev->local->status), &dev->local->status);
 273        writel(readl(&dev->remote->status), &dev->remote->status);
 274
 275        /* Now setup interrupts */
 276        val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq);
 277        val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
 278                VLYNQ_CTRL_INT2CFG;
 279        val |= readl(&dev->local->control);
 280        writel(VLYNQ_INT_OFFSET, &dev->local->int_ptr);
 281        writel(val, &dev->local->control);
 282
 283        val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
 284        val |= VLYNQ_CTRL_INT_ENABLE;
 285        val |= readl(&dev->remote->control);
 286        writel(VLYNQ_INT_OFFSET, &dev->remote->int_ptr);
 287        writel(val, &dev->remote->int_ptr);
 288        writel(val, &dev->remote->control);
 289
 290        for (i = dev->irq_start; i <= dev->irq_end; i++) {
 291                virq = i - dev->irq_start;
 292                if (virq == dev->local_irq) {
 293                        set_irq_chip_and_handler(i, &vlynq_local_chip,
 294                                                 handle_level_irq);
 295                        set_irq_chip_data(i, dev);
 296                } else if (virq == dev->remote_irq) {
 297                        set_irq_chip_and_handler(i, &vlynq_remote_chip,
 298                                                 handle_level_irq);
 299                        set_irq_chip_data(i, dev);
 300                } else {
 301                        set_irq_chip_and_handler(i, &vlynq_irq_chip,
 302                                                 handle_simple_irq);
 303                        set_irq_chip_data(i, dev);
 304                        writel(0, &dev->remote->int_device[virq >> 2]);
 305                }
 306        }
 307
 308        if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) {
 309                printk(KERN_ERR "%s: request_irq failed\n",
 310                                        dev_name(&dev->dev));
 311                return -EAGAIN;
 312        }
 313
 314        return 0;
 315}
 316
 317static void vlynq_device_release(struct device *dev)
 318{
 319        struct vlynq_device *vdev = to_vlynq_device(dev);
 320        kfree(vdev);
 321}
 322
 323static int vlynq_device_match(struct device *dev,
 324                              struct device_driver *drv)
 325{
 326        struct vlynq_device *vdev = to_vlynq_device(dev);
 327        struct vlynq_driver *vdrv = to_vlynq_driver(drv);
 328        struct vlynq_device_id *ids = vdrv->id_table;
 329
 330        while (ids->id) {
 331                if (ids->id == vdev->dev_id) {
 332                        vdev->divisor = ids->divisor;
 333                        vlynq_set_drvdata(vdev, ids);
 334                        printk(KERN_INFO "Driver found for VLYNQ "
 335                                "device: %08x\n", vdev->dev_id);
 336                        return 1;
 337                }
 338                printk(KERN_DEBUG "Not using the %08x VLYNQ device's driver"
 339                        " for VLYNQ device: %08x\n", ids->id, vdev->dev_id);
 340                ids++;
 341        }
 342        return 0;
 343}
 344
 345static int vlynq_device_probe(struct device *dev)
 346{
 347        struct vlynq_device *vdev = to_vlynq_device(dev);
 348        struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
 349        struct vlynq_device_id *id = vlynq_get_drvdata(vdev);
 350        int result = -ENODEV;
 351
 352        if (drv->probe)
 353                result = drv->probe(vdev, id);
 354        if (result)
 355                put_device(dev);
 356        return result;
 357}
 358
 359static int vlynq_device_remove(struct device *dev)
 360{
 361        struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
 362
 363        if (drv->remove)
 364                drv->remove(to_vlynq_device(dev));
 365
 366        return 0;
 367}
 368
 369int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner)
 370{
 371        driver->driver.name = driver->name;
 372        driver->driver.bus = &vlynq_bus_type;
 373        return driver_register(&driver->driver);
 374}
 375EXPORT_SYMBOL(__vlynq_register_driver);
 376
 377void vlynq_unregister_driver(struct vlynq_driver *driver)
 378{
 379        driver_unregister(&driver->driver);
 380}
 381EXPORT_SYMBOL(vlynq_unregister_driver);
 382
 383/*
 384 * A VLYNQ remote device can clock the VLYNQ bus master
 385 * using a dedicated clock line. In that case, both the
 386 * remove device and the bus master should have the same
 387 * serial clock dividers configured. Iterate through the
 388 * 8 possible dividers until we actually link with the
 389 * device.
 390 */
 391static int __vlynq_try_remote(struct vlynq_device *dev)
 392{
 393        int i;
 394
 395        vlynq_reset(dev);
 396        for (i = dev->dev_id ? vlynq_rdiv2 : vlynq_rdiv8; dev->dev_id ?
 397                        i <= vlynq_rdiv8 : i >= vlynq_rdiv2;
 398                dev->dev_id ? i++ : i--) {
 399
 400                if (!vlynq_linked(dev))
 401                        break;
 402
 403                writel((readl(&dev->remote->control) &
 404                                ~VLYNQ_CTRL_CLOCK_MASK) |
 405                                VLYNQ_CTRL_CLOCK_INT |
 406                                VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
 407                                &dev->remote->control);
 408                writel((readl(&dev->local->control)
 409                                & ~(VLYNQ_CTRL_CLOCK_INT |
 410                                VLYNQ_CTRL_CLOCK_MASK)) |
 411                                VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
 412                                &dev->local->control);
 413
 414                if (vlynq_linked(dev)) {
 415                        printk(KERN_DEBUG
 416                                "%s: using remote clock divisor %d\n",
 417                                dev_name(&dev->dev), i - vlynq_rdiv1 + 1);
 418                        dev->divisor = i;
 419                        return 0;
 420                } else {
 421                        vlynq_reset(dev);
 422                }
 423        }
 424
 425        return -ENODEV;
 426}
 427
 428/*
 429 * A VLYNQ remote device can be clocked by the VLYNQ bus
 430 * master using a dedicated clock line. In that case, only
 431 * the bus master configures the serial clock divider.
 432 * Iterate through the 8 possible dividers until we
 433 * actually get a link with the device.
 434 */
 435static int __vlynq_try_local(struct vlynq_device *dev)
 436{
 437        int i;
 438
 439        vlynq_reset(dev);
 440
 441        for (i = dev->dev_id ? vlynq_ldiv2 : vlynq_ldiv8; dev->dev_id ?
 442                        i <= vlynq_ldiv8 : i >= vlynq_ldiv2;
 443                dev->dev_id ? i++ : i--) {
 444
 445                writel((readl(&dev->local->control) &
 446                                ~VLYNQ_CTRL_CLOCK_MASK) |
 447                                VLYNQ_CTRL_CLOCK_INT |
 448                                VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1),
 449                                &dev->local->control);
 450
 451                if (vlynq_linked(dev)) {
 452                        printk(KERN_DEBUG
 453                                "%s: using local clock divisor %d\n",
 454                                dev_name(&dev->dev), i - vlynq_ldiv1 + 1);
 455                        dev->divisor = i;
 456                        return 0;
 457                } else {
 458                        vlynq_reset(dev);
 459                }
 460        }
 461
 462        return -ENODEV;
 463}
 464
 465/*
 466 * When using external clocking method, serial clock
 467 * is supplied by an external oscillator, therefore we
 468 * should mask the local clock bit in the clock control
 469 * register for both the bus master and the remote device.
 470 */
 471static int __vlynq_try_external(struct vlynq_device *dev)
 472{
 473        vlynq_reset(dev);
 474        if (!vlynq_linked(dev))
 475                return -ENODEV;
 476
 477        writel((readl(&dev->remote->control) &
 478                        ~VLYNQ_CTRL_CLOCK_INT),
 479                        &dev->remote->control);
 480
 481        writel((readl(&dev->local->control) &
 482                        ~VLYNQ_CTRL_CLOCK_INT),
 483                        &dev->local->control);
 484
 485        if (vlynq_linked(dev)) {
 486                printk(KERN_DEBUG "%s: using external clock\n",
 487                        dev_name(&dev->dev));
 488                        dev->divisor = vlynq_div_external;
 489                return 0;
 490        }
 491
 492        return -ENODEV;
 493}
 494
 495static int __vlynq_enable_device(struct vlynq_device *dev)
 496{
 497        int result;
 498        struct plat_vlynq_ops *ops = dev->dev.platform_data;
 499
 500        result = ops->on(dev);
 501        if (result)
 502                return result;
 503
 504        switch (dev->divisor) {
 505        case vlynq_div_external:
 506        case vlynq_div_auto:
 507                /* When the device is brought from reset it should have clock
 508                 * generation negotiated by hardware.
 509                 * Check which device is generating clocks and perform setup
 510                 * accordingly */
 511                if (vlynq_linked(dev) && readl(&dev->remote->control) &
 512                   VLYNQ_CTRL_CLOCK_INT) {
 513                        if (!__vlynq_try_remote(dev) ||
 514                                !__vlynq_try_local(dev)  ||
 515                                !__vlynq_try_external(dev))
 516                                return 0;
 517                } else {
 518                        if (!__vlynq_try_external(dev) ||
 519                                !__vlynq_try_local(dev)    ||
 520                                !__vlynq_try_remote(dev))
 521                                return 0;
 522                }
 523                break;
 524        case vlynq_ldiv1:
 525        case vlynq_ldiv2:
 526        case vlynq_ldiv3:
 527        case vlynq_ldiv4:
 528        case vlynq_ldiv5:
 529        case vlynq_ldiv6:
 530        case vlynq_ldiv7:
 531        case vlynq_ldiv8:
 532                writel(VLYNQ_CTRL_CLOCK_INT |
 533                        VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
 534                        vlynq_ldiv1), &dev->local->control);
 535                writel(0, &dev->remote->control);
 536                if (vlynq_linked(dev)) {
 537                        printk(KERN_DEBUG
 538                                "%s: using local clock divisor %d\n",
 539                                dev_name(&dev->dev),
 540                                dev->divisor - vlynq_ldiv1 + 1);
 541                        return 0;
 542                }
 543                break;
 544        case vlynq_rdiv1:
 545        case vlynq_rdiv2:
 546        case vlynq_rdiv3:
 547        case vlynq_rdiv4:
 548        case vlynq_rdiv5:
 549        case vlynq_rdiv6:
 550        case vlynq_rdiv7:
 551        case vlynq_rdiv8:
 552                writel(0, &dev->local->control);
 553                writel(VLYNQ_CTRL_CLOCK_INT |
 554                        VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
 555                        vlynq_rdiv1), &dev->remote->control);
 556                if (vlynq_linked(dev)) {
 557                        printk(KERN_DEBUG
 558                                "%s: using remote clock divisor %d\n",
 559                                dev_name(&dev->dev),
 560                                dev->divisor - vlynq_rdiv1 + 1);
 561                        return 0;
 562                }
 563                break;
 564        }
 565
 566        ops->off(dev);
 567        return -ENODEV;
 568}
 569
 570int vlynq_enable_device(struct vlynq_device *dev)
 571{
 572        struct plat_vlynq_ops *ops = dev->dev.platform_data;
 573        int result = -ENODEV;
 574
 575        result = __vlynq_enable_device(dev);
 576        if (result)
 577                return result;
 578
 579        result = vlynq_setup_irq(dev);
 580        if (result)
 581                ops->off(dev);
 582
 583        dev->enabled = !result;
 584        return result;
 585}
 586EXPORT_SYMBOL(vlynq_enable_device);
 587
 588
 589void vlynq_disable_device(struct vlynq_device *dev)
 590{
 591        struct plat_vlynq_ops *ops = dev->dev.platform_data;
 592
 593        dev->enabled = 0;
 594        free_irq(dev->irq, dev);
 595        ops->off(dev);
 596}
 597EXPORT_SYMBOL(vlynq_disable_device);
 598
 599int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset,
 600                            struct vlynq_mapping *mapping)
 601{
 602        int i;
 603
 604        if (!dev->enabled)
 605                return -ENXIO;
 606
 607        writel(tx_offset, &dev->local->tx_offset);
 608        for (i = 0; i < 4; i++) {
 609                writel(mapping[i].offset, &dev->local->rx_mapping[i].offset);
 610                writel(mapping[i].size, &dev->local->rx_mapping[i].size);
 611        }
 612        return 0;
 613}
 614EXPORT_SYMBOL(vlynq_set_local_mapping);
 615
 616int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset,
 617                             struct vlynq_mapping *mapping)
 618{
 619        int i;
 620
 621        if (!dev->enabled)
 622                return -ENXIO;
 623
 624        writel(tx_offset, &dev->remote->tx_offset);
 625        for (i = 0; i < 4; i++) {
 626                writel(mapping[i].offset, &dev->remote->rx_mapping[i].offset);
 627                writel(mapping[i].size, &dev->remote->rx_mapping[i].size);
 628        }
 629        return 0;
 630}
 631EXPORT_SYMBOL(vlynq_set_remote_mapping);
 632
 633int vlynq_set_local_irq(struct vlynq_device *dev, int virq)
 634{
 635        int irq = dev->irq_start + virq;
 636        if (dev->enabled)
 637                return -EBUSY;
 638
 639        if ((irq < dev->irq_start) || (irq > dev->irq_end))
 640                return -EINVAL;
 641
 642        if (virq == dev->remote_irq)
 643                return -EINVAL;
 644
 645        dev->local_irq = virq;
 646
 647        return 0;
 648}
 649EXPORT_SYMBOL(vlynq_set_local_irq);
 650
 651int vlynq_set_remote_irq(struct vlynq_device *dev, int virq)
 652{
 653        int irq = dev->irq_start + virq;
 654        if (dev->enabled)
 655                return -EBUSY;
 656
 657        if ((irq < dev->irq_start) || (irq > dev->irq_end))
 658                return -EINVAL;
 659
 660        if (virq == dev->local_irq)
 661                return -EINVAL;
 662
 663        dev->remote_irq = virq;
 664
 665        return 0;
 666}
 667EXPORT_SYMBOL(vlynq_set_remote_irq);
 668
 669static int vlynq_probe(struct platform_device *pdev)
 670{
 671        struct vlynq_device *dev;
 672        struct resource *regs_res, *mem_res, *irq_res;
 673        int len, result;
 674
 675        regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
 676        if (!regs_res)
 677                return -ENODEV;
 678
 679        mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
 680        if (!mem_res)
 681                return -ENODEV;
 682
 683        irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq");
 684        if (!irq_res)
 685                return -ENODEV;
 686
 687        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 688        if (!dev) {
 689                printk(KERN_ERR
 690                       "vlynq: failed to allocate device structure\n");
 691                return -ENOMEM;
 692        }
 693
 694        dev->id = pdev->id;
 695        dev->dev.bus = &vlynq_bus_type;
 696        dev->dev.parent = &pdev->dev;
 697        dev_set_name(&dev->dev, "vlynq%d", dev->id);
 698        dev->dev.platform_data = pdev->dev.platform_data;
 699        dev->dev.release = vlynq_device_release;
 700
 701        dev->regs_start = regs_res->start;
 702        dev->regs_end = regs_res->end;
 703        dev->mem_start = mem_res->start;
 704        dev->mem_end = mem_res->end;
 705
 706        len = resource_size(regs_res);
 707        if (!request_mem_region(regs_res->start, len, dev_name(&dev->dev))) {
 708                printk(KERN_ERR "%s: Can't request vlynq registers\n",
 709                       dev_name(&dev->dev));
 710                result = -ENXIO;
 711                goto fail_request;
 712        }
 713
 714        dev->local = ioremap(regs_res->start, len);
 715        if (!dev->local) {
 716                printk(KERN_ERR "%s: Can't remap vlynq registers\n",
 717                       dev_name(&dev->dev));
 718                result = -ENXIO;
 719                goto fail_remap;
 720        }
 721
 722        dev->remote = (struct vlynq_regs *)((void *)dev->local +
 723                                            VLYNQ_REMOTE_OFFSET);
 724
 725        dev->irq = platform_get_irq_byname(pdev, "irq");
 726        dev->irq_start = irq_res->start;
 727        dev->irq_end = irq_res->end;
 728        dev->local_irq = dev->irq_end - dev->irq_start;
 729        dev->remote_irq = dev->local_irq - 1;
 730
 731        if (device_register(&dev->dev))
 732                goto fail_register;
 733        platform_set_drvdata(pdev, dev);
 734
 735        printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n",
 736               dev_name(&dev->dev), (void *)dev->regs_start, dev->irq,
 737               (void *)dev->mem_start);
 738
 739        dev->dev_id = 0;
 740        dev->divisor = vlynq_div_auto;
 741        result = __vlynq_enable_device(dev);
 742        if (result == 0) {
 743                dev->dev_id = readl(&dev->remote->chip);
 744                ((struct plat_vlynq_ops *)(dev->dev.platform_data))->off(dev);
 745        }
 746        if (dev->dev_id)
 747                printk(KERN_INFO "Found a VLYNQ device: %08x\n", dev->dev_id);
 748
 749        return 0;
 750
 751fail_register:
 752        iounmap(dev->local);
 753fail_remap:
 754fail_request:
 755        release_mem_region(regs_res->start, len);
 756        kfree(dev);
 757        return result;
 758}
 759
 760static int vlynq_remove(struct platform_device *pdev)
 761{
 762        struct vlynq_device *dev = platform_get_drvdata(pdev);
 763
 764        device_unregister(&dev->dev);
 765        iounmap(dev->local);
 766        release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start);
 767
 768        kfree(dev);
 769
 770        return 0;
 771}
 772
 773static struct platform_driver vlynq_platform_driver = {
 774        .driver.name = "vlynq",
 775        .probe = vlynq_probe,
 776        .remove = __devexit_p(vlynq_remove),
 777};
 778
 779struct bus_type vlynq_bus_type = {
 780        .name = "vlynq",
 781        .match = vlynq_device_match,
 782        .probe = vlynq_device_probe,
 783        .remove = vlynq_device_remove,
 784};
 785EXPORT_SYMBOL(vlynq_bus_type);
 786
 787static int __devinit vlynq_init(void)
 788{
 789        int res = 0;
 790
 791        res = bus_register(&vlynq_bus_type);
 792        if (res)
 793                goto fail_bus;
 794
 795        res = platform_driver_register(&vlynq_platform_driver);
 796        if (res)
 797                goto fail_platform;
 798
 799        return 0;
 800
 801fail_platform:
 802        bus_unregister(&vlynq_bus_type);
 803fail_bus:
 804        return res;
 805}
 806
 807static void __devexit vlynq_exit(void)
 808{
 809        platform_driver_unregister(&vlynq_platform_driver);
 810        bus_unregister(&vlynq_bus_type);
 811}
 812
 813module_init(vlynq_init);
 814module_exit(vlynq_exit);
 815
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.