Commit 55e331cf7ebe20665253770589cd9eb06048bf25

Authored by Florian Fainelli
Committed by Linus Torvalds
1 parent f324edc85e

drivers: add support for the TI VLYNQ bus

Add support for the TI VLYNQ high-speed, serial and packetized bus.

This bus allows external devices to be connected to the System-on-Chip and
appear in the main system memory just like any memory mapped peripheral.
It is widely used in TI's networking and multimedia SoC, including the AR7
SoC.

Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 7 changed files with 1011 additions and 0 deletions Side-by-side Diff

... ... @@ -6281,6 +6281,14 @@
6281 6281 F: include/linux/if_*vlan.h
6282 6282 F: net/8021q/
6283 6283  
  6284 +VLYNQ BUS
  6285 +P: Florian Fainelli
  6286 +M: florian@openwrt.org
  6287 +L: openwrt-devel@lists.openwrt.org
  6288 +S: Maintained
  6289 +F: drivers/vlynq/vlynq.c
  6290 +F: include/linux/vlynq.h
  6291 +
6284 6292 VOLTAGE AND CURRENT REGULATOR FRAMEWORK
6285 6293 P: Liam Girdwood
6286 6294 M: lrg@slimlogic.co.uk
... ... @@ -104,6 +104,8 @@
104 104  
105 105 source "drivers/uio/Kconfig"
106 106  
  107 +source "drivers/vlynq/Kconfig"
  108 +
107 109 source "drivers/xen/Kconfig"
108 110  
109 111 source "drivers/staging/Kconfig"
... ... @@ -105,6 +105,7 @@
105 105 obj-$(CONFIG_OF) += of/
106 106 obj-$(CONFIG_SSB) += ssb/
107 107 obj-$(CONFIG_VIRTIO) += virtio/
  108 +obj-$(CONFIG_VLYNQ) += vlynq/
108 109 obj-$(CONFIG_STAGING) += staging/
109 110 obj-y += platform/
110 111 obj-y += ieee802154/
drivers/vlynq/Kconfig
  1 +menu "TI VLYNQ"
  2 +
  3 +config VLYNQ
  4 + bool "TI VLYNQ bus support"
  5 + depends on AR7 && EXPERIMENTAL
  6 + help
  7 + Support for Texas Instruments(R) VLYNQ bus.
  8 + The VLYNQ bus is a high-speed, serial and packetized
  9 + data bus which allows external peripherals of a SoC
  10 + to appear into the system's main memory.
  11 +
  12 + If unsure, say N
  13 +
  14 +config VLYNQ_DEBUG
  15 + bool "VLYNQ bus debug"
  16 + depends on VLYNQ && KERNEL_DEBUG
  17 + help
  18 + Turn on VLYNQ bus debugging.
  19 +
  20 +endmenu
drivers/vlynq/Makefile
  1 +#
  2 +# Makefile for kernel vlynq drivers
  3 +#
  4 +
  5 +obj-$(CONFIG_VLYNQ) += vlynq.o
drivers/vlynq/vlynq.c
  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/device.h>
  32 +#include <linux/delay.h>
  33 +#include <linux/io.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 +
  63 +struct 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 VLYNQ_DEBUG
  80 +static 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 +
  94 +static 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 */
  108 +static 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 +
  121 +static 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 +
  137 +static 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 +
  150 +static 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 +
  163 +static 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 +
  194 +static 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 +
  205 +static 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 +
  216 +static 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 +
  238 +static 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 +
  245 +static 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 +
  252 +static 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 +
  259 +static 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 +
  317 +static void vlynq_device_release(struct device *dev)
  318 +{
  319 + struct vlynq_device *vdev = to_vlynq_device(dev);
  320 + kfree(vdev);
  321 +}
  322 +
  323 +static 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 +
  345 +static 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 +
  359 +static 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 +
  369 +int __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 +}
  375 +EXPORT_SYMBOL(__vlynq_register_driver);
  376 +
  377 +void vlynq_unregister_driver(struct vlynq_driver *driver)
  378 +{
  379 + driver_unregister(&driver->driver);
  380 +}
  381 +EXPORT_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 + */
  391 +static 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 + */
  435 +static 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 + */
  471 +static 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 +
  495 +static 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 +
  570 +int 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 +}
  586 +EXPORT_SYMBOL(vlynq_enable_device);
  587 +
  588 +
  589 +void 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 +}
  597 +EXPORT_SYMBOL(vlynq_disable_device);
  598 +
  599 +int 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 +}
  614 +EXPORT_SYMBOL(vlynq_set_local_mapping);
  615 +
  616 +int 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 +}
  631 +EXPORT_SYMBOL(vlynq_set_remote_mapping);
  632 +
  633 +int 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 +}
  649 +EXPORT_SYMBOL(vlynq_set_local_irq);
  650 +
  651 +int 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 +}
  667 +EXPORT_SYMBOL(vlynq_set_remote_irq);
  668 +
  669 +static 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 = regs_res->end - regs_res->start;
  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 +
  751 +fail_register:
  752 + iounmap(dev->local);
  753 +fail_remap:
  754 +fail_request:
  755 + release_mem_region(regs_res->start, len);
  756 + kfree(dev);
  757 + return result;
  758 +}
  759 +
  760 +static 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 +
  773 +static struct platform_driver vlynq_platform_driver = {
  774 + .driver.name = "vlynq",
  775 + .probe = vlynq_probe,
  776 + .remove = __devexit_p(vlynq_remove),
  777 +};
  778 +
  779 +struct bus_type vlynq_bus_type = {
  780 + .name = "vlynq",
  781 + .match = vlynq_device_match,
  782 + .probe = vlynq_device_probe,
  783 + .remove = vlynq_device_remove,
  784 +};
  785 +EXPORT_SYMBOL(vlynq_bus_type);
  786 +
  787 +static 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 +
  801 +fail_platform:
  802 + bus_unregister(&vlynq_bus_type);
  803 +fail_bus:
  804 + return res;
  805 +}
  806 +
  807 +static void __devexit vlynq_exit(void)
  808 +{
  809 + platform_driver_unregister(&vlynq_platform_driver);
  810 + bus_unregister(&vlynq_bus_type);
  811 +}
  812 +
  813 +module_init(vlynq_init);
  814 +module_exit(vlynq_exit);
include/linux/vlynq.h
  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 +
  19 +#ifndef __VLYNQ_H__
  20 +#define __VLYNQ_H__
  21 +
  22 +#include <linux/device.h>
  23 +#include <linux/module.h>
  24 +#include <linux/types.h>
  25 +
  26 +#define VLYNQ_NUM_IRQS 32
  27 +
  28 +struct vlynq_mapping {
  29 + u32 size;
  30 + u32 offset;
  31 +};
  32 +
  33 +enum vlynq_divisor {
  34 + vlynq_div_auto = 0,
  35 + vlynq_ldiv1,
  36 + vlynq_ldiv2,
  37 + vlynq_ldiv3,
  38 + vlynq_ldiv4,
  39 + vlynq_ldiv5,
  40 + vlynq_ldiv6,
  41 + vlynq_ldiv7,
  42 + vlynq_ldiv8,
  43 + vlynq_rdiv1,
  44 + vlynq_rdiv2,
  45 + vlynq_rdiv3,
  46 + vlynq_rdiv4,
  47 + vlynq_rdiv5,
  48 + vlynq_rdiv6,
  49 + vlynq_rdiv7,
  50 + vlynq_rdiv8,
  51 + vlynq_div_external
  52 +};
  53 +
  54 +struct vlynq_device_id {
  55 + u32 id;
  56 + enum vlynq_divisor divisor;
  57 + unsigned long driver_data;
  58 +};
  59 +
  60 +struct vlynq_regs;
  61 +struct vlynq_device {
  62 + u32 id, dev_id;
  63 + int local_irq;
  64 + int remote_irq;
  65 + enum vlynq_divisor divisor;
  66 + u32 regs_start, regs_end;
  67 + u32 mem_start, mem_end;
  68 + u32 irq_start, irq_end;
  69 + int irq;
  70 + int enabled;
  71 + struct vlynq_regs *local;
  72 + struct vlynq_regs *remote;
  73 + struct device dev;
  74 +};
  75 +
  76 +struct vlynq_driver {
  77 + char *name;
  78 + struct vlynq_device_id *id_table;
  79 + int (*probe)(struct vlynq_device *dev, struct vlynq_device_id *id);
  80 + void (*remove)(struct vlynq_device *dev);
  81 + struct device_driver driver;
  82 +};
  83 +
  84 +struct plat_vlynq_ops {
  85 + int (*on)(struct vlynq_device *dev);
  86 + void (*off)(struct vlynq_device *dev);
  87 +};
  88 +
  89 +static inline struct vlynq_driver *to_vlynq_driver(struct device_driver *drv)
  90 +{
  91 + return container_of(drv, struct vlynq_driver, driver);
  92 +}
  93 +
  94 +static inline struct vlynq_device *to_vlynq_device(struct device *device)
  95 +{
  96 + return container_of(device, struct vlynq_device, dev);
  97 +}
  98 +
  99 +extern struct bus_type vlynq_bus_type;
  100 +
  101 +extern int __vlynq_register_driver(struct vlynq_driver *driver,
  102 + struct module *owner);
  103 +
  104 +static inline int vlynq_register_driver(struct vlynq_driver *driver)
  105 +{
  106 + return __vlynq_register_driver(driver, THIS_MODULE);
  107 +}
  108 +
  109 +static inline void *vlynq_get_drvdata(struct vlynq_device *dev)
  110 +{
  111 + return dev_get_drvdata(&dev->dev);
  112 +}
  113 +
  114 +static inline void vlynq_set_drvdata(struct vlynq_device *dev, void *data)
  115 +{
  116 + dev_set_drvdata(&dev->dev, data);
  117 +}
  118 +
  119 +static inline u32 vlynq_mem_start(struct vlynq_device *dev)
  120 +{
  121 + return dev->mem_start;
  122 +}
  123 +
  124 +static inline u32 vlynq_mem_end(struct vlynq_device *dev)
  125 +{
  126 + return dev->mem_end;
  127 +}
  128 +
  129 +static inline u32 vlynq_mem_len(struct vlynq_device *dev)
  130 +{
  131 + return dev->mem_end - dev->mem_start + 1;
  132 +}
  133 +
  134 +static inline int vlynq_virq_to_irq(struct vlynq_device *dev, int virq)
  135 +{
  136 + int irq = dev->irq_start + virq;
  137 + if ((irq < dev->irq_start) || (irq > dev->irq_end))
  138 + return -EINVAL;
  139 +
  140 + return irq;
  141 +}
  142 +
  143 +static inline int vlynq_irq_to_virq(struct vlynq_device *dev, int irq)
  144 +{
  145 + if ((irq < dev->irq_start) || (irq > dev->irq_end))
  146 + return -EINVAL;
  147 +
  148 + return irq - dev->irq_start;
  149 +}
  150 +
  151 +extern void vlynq_unregister_driver(struct vlynq_driver *driver);
  152 +extern int vlynq_enable_device(struct vlynq_device *dev);
  153 +extern void vlynq_disable_device(struct vlynq_device *dev);
  154 +extern int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset,
  155 + struct vlynq_mapping *mapping);
  156 +extern int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset,
  157 + struct vlynq_mapping *mapping);
  158 +extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq);
  159 +extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq);
  160 +
  161 +#endif /* __VLYNQ_H__ */