Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Florian Fainelli and committed by
Linus Torvalds
55e331cf f324edc8

+1011
+8
MAINTAINERS
··· 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
+2
drivers/Kconfig
··· 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"
+1
drivers/Makefile
··· 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/
+20
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
+5
drivers/vlynq/Makefile
··· 1 + # 2 + # Makefile for kernel vlynq drivers 3 + # 4 + 5 + obj-$(CONFIG_VLYNQ) += vlynq.o
+814
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);
+161
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__ */