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