From 05e5027efc9c0bb6d1d04cde279afbafca0a7929 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Nov 2012 08:14:18 -0800 Subject: Staging: ipack: move out of staging The ipack subsystem is cleaned up enough to now move out of the staging tree, and into drivers/ipack. Cc: Samuel Iglesias Gonsalvez Cc: Jens Taprogge Signed-off-by: Greg Kroah-Hartman --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/ipack/Kconfig | 24 + drivers/ipack/Makefile | 6 + drivers/ipack/carriers/Kconfig | 7 + drivers/ipack/carriers/Makefile | 1 + drivers/ipack/carriers/tpci200.c | 627 ++++++++++++++++++++++++++ drivers/ipack/carriers/tpci200.h | 168 +++++++ drivers/ipack/devices/Kconfig | 6 + drivers/ipack/devices/Makefile | 1 + drivers/ipack/devices/ipoctal.c | 751 +++++++++++++++++++++++++++++++ drivers/ipack/devices/ipoctal.h | 42 ++ drivers/ipack/devices/scc2698.h | 228 ++++++++++ drivers/ipack/ipack.c | 481 ++++++++++++++++++++ drivers/ipack/ipack.h | 215 +++++++++ drivers/ipack/ipack_ids.h | 32 ++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/ipack/Kconfig | 24 - drivers/staging/ipack/Makefile | 6 - drivers/staging/ipack/TODO | 16 - drivers/staging/ipack/carriers/Kconfig | 7 - drivers/staging/ipack/carriers/Makefile | 1 - drivers/staging/ipack/carriers/tpci200.c | 627 -------------------------- drivers/staging/ipack/carriers/tpci200.h | 168 ------- drivers/staging/ipack/devices/Kconfig | 6 - drivers/staging/ipack/devices/Makefile | 1 - drivers/staging/ipack/devices/ipoctal.c | 751 ------------------------------- drivers/staging/ipack/devices/ipoctal.h | 42 -- drivers/staging/ipack/devices/scc2698.h | 228 ---------- drivers/staging/ipack/ipack.c | 481 -------------------- drivers/staging/ipack/ipack.h | 215 --------- drivers/staging/ipack/ipack_ids.h | 32 -- 33 files changed, 2592 insertions(+), 2608 deletions(-) create mode 100644 drivers/ipack/Kconfig create mode 100644 drivers/ipack/Makefile create mode 100644 drivers/ipack/carriers/Kconfig create mode 100644 drivers/ipack/carriers/Makefile create mode 100644 drivers/ipack/carriers/tpci200.c create mode 100644 drivers/ipack/carriers/tpci200.h create mode 100644 drivers/ipack/devices/Kconfig create mode 100644 drivers/ipack/devices/Makefile create mode 100644 drivers/ipack/devices/ipoctal.c create mode 100644 drivers/ipack/devices/ipoctal.h create mode 100644 drivers/ipack/devices/scc2698.h create mode 100644 drivers/ipack/ipack.c create mode 100644 drivers/ipack/ipack.h create mode 100644 drivers/ipack/ipack_ids.h delete mode 100644 drivers/staging/ipack/Kconfig delete mode 100644 drivers/staging/ipack/Makefile delete mode 100644 drivers/staging/ipack/TODO delete mode 100644 drivers/staging/ipack/carriers/Kconfig delete mode 100644 drivers/staging/ipack/carriers/Makefile delete mode 100644 drivers/staging/ipack/carriers/tpci200.c delete mode 100644 drivers/staging/ipack/carriers/tpci200.h delete mode 100644 drivers/staging/ipack/devices/Kconfig delete mode 100644 drivers/staging/ipack/devices/Makefile delete mode 100644 drivers/staging/ipack/devices/ipoctal.c delete mode 100644 drivers/staging/ipack/devices/ipoctal.h delete mode 100644 drivers/staging/ipack/devices/scc2698.h delete mode 100644 drivers/staging/ipack/ipack.c delete mode 100644 drivers/staging/ipack/ipack.h delete mode 100644 drivers/staging/ipack/ipack_ids.h diff --git a/drivers/Kconfig b/drivers/Kconfig index dbdefa3fe775..f5fb0722a63a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -156,4 +156,6 @@ source "drivers/pwm/Kconfig" source "drivers/irqchip/Kconfig" +source "drivers/ipack/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index a16a8d001ae0..7863b9fee50b 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -145,3 +145,4 @@ obj-$(CONFIG_EXTCON) += extcon/ obj-$(CONFIG_MEMORY) += memory/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ +obj-$(CONFIG_IPACK_BUS) += ipack/ diff --git a/drivers/ipack/Kconfig b/drivers/ipack/Kconfig new file mode 100644 index 000000000000..3949e5589560 --- /dev/null +++ b/drivers/ipack/Kconfig @@ -0,0 +1,24 @@ +# +# IPACK configuration. +# + +menuconfig IPACK_BUS + tristate "IndustryPack bus support" + depends on HAS_IOMEM + ---help--- + This option provides support for the IndustryPack framework. There + are IndustryPack carrier boards, which interface another bus (such as + PCI) to an IndustryPack bus, and IndustryPack modules, that are + hosted on these buses. While IndustryPack modules can provide a + large variety of functionality, they are most often found in + industrial control applications. + + Say N if unsure. + +if IPACK_BUS + +source "drivers/ipack/carriers/Kconfig" + +source "drivers/ipack/devices/Kconfig" + +endif # IPACK diff --git a/drivers/ipack/Makefile b/drivers/ipack/Makefile new file mode 100644 index 000000000000..6f14ade0f8f3 --- /dev/null +++ b/drivers/ipack/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the IPACK bridge device drivers. +# +obj-$(CONFIG_IPACK_BUS) += ipack.o +obj-y += devices/ +obj-y += carriers/ diff --git a/drivers/ipack/carriers/Kconfig b/drivers/ipack/carriers/Kconfig new file mode 100644 index 000000000000..922ff5c35acc --- /dev/null +++ b/drivers/ipack/carriers/Kconfig @@ -0,0 +1,7 @@ +config BOARD_TPCI200 + tristate "Support for the TEWS TPCI-200 IndustryPack carrier board" + depends on IPACK_BUS + depends on PCI + help + This driver adds support for the TEWS TPCI200 IndustryPack carrier board. + default n diff --git a/drivers/ipack/carriers/Makefile b/drivers/ipack/carriers/Makefile new file mode 100644 index 000000000000..d8b76459300f --- /dev/null +++ b/drivers/ipack/carriers/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_BOARD_TPCI200) += tpci200.o diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c new file mode 100644 index 000000000000..c1a19b274c23 --- /dev/null +++ b/drivers/ipack/carriers/tpci200.c @@ -0,0 +1,627 @@ +/** + * tpci200.c + * + * driver for the TEWS TPCI-200 device + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include +#include +#include "tpci200.h" + +static const u16 tpci200_status_timeout[] = { + TPCI200_A_TIMEOUT, + TPCI200_B_TIMEOUT, + TPCI200_C_TIMEOUT, + TPCI200_D_TIMEOUT, +}; + +static const u16 tpci200_status_error[] = { + TPCI200_A_ERROR, + TPCI200_B_ERROR, + TPCI200_C_ERROR, + TPCI200_D_ERROR, +}; + +static const size_t tpci200_space_size[IPACK_SPACE_COUNT] = { + [IPACK_IO_SPACE] = TPCI200_IO_SPACE_SIZE, + [IPACK_ID_SPACE] = TPCI200_ID_SPACE_SIZE, + [IPACK_INT_SPACE] = TPCI200_INT_SPACE_SIZE, + [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_SIZE, + [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE, +}; + +static const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = { + [IPACK_IO_SPACE] = TPCI200_IO_SPACE_INTERVAL, + [IPACK_ID_SPACE] = TPCI200_ID_SPACE_INTERVAL, + [IPACK_INT_SPACE] = TPCI200_INT_SPACE_INTERVAL, + [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_INTERVAL, + [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL, +}; + +static struct tpci200_board *check_slot(struct ipack_device *dev) +{ + struct tpci200_board *tpci200; + + if (dev == NULL) + return NULL; + + + tpci200 = dev_get_drvdata(dev->bus->parent); + + if (tpci200 == NULL) { + dev_info(&dev->dev, "carrier board not found\n"); + return NULL; + } + + if (dev->slot >= TPCI200_NB_SLOT) { + dev_info(&dev->dev, + "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n", + dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1); + return NULL; + } + + return tpci200; +} + +static void tpci200_clear_mask(struct tpci200_board *tpci200, + __le16 __iomem *addr, u16 mask) +{ + unsigned long flags; + spin_lock_irqsave(&tpci200->regs_lock, flags); + iowrite16(ioread16(addr) & (~mask), addr); + spin_unlock_irqrestore(&tpci200->regs_lock, flags); +} + +static void tpci200_set_mask(struct tpci200_board *tpci200, + __le16 __iomem *addr, u16 mask) +{ + unsigned long flags; + spin_lock_irqsave(&tpci200->regs_lock, flags); + iowrite16(ioread16(addr) | mask, addr); + spin_unlock_irqrestore(&tpci200->regs_lock, flags); +} + +static void tpci200_unregister(struct tpci200_board *tpci200) +{ + free_irq(tpci200->info->pdev->irq, (void *) tpci200); + + pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); + pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); + + pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); + pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); + pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); + pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); + pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); + + pci_disable_device(tpci200->info->pdev); + pci_dev_put(tpci200->info->pdev); +} + +static void tpci200_enable_irq(struct tpci200_board *tpci200, + int islot) +{ + tpci200_set_mask(tpci200, + &tpci200->info->interface_regs->control[islot], + TPCI200_INT0_EN | TPCI200_INT1_EN); +} + +static void tpci200_disable_irq(struct tpci200_board *tpci200, + int islot) +{ + tpci200_clear_mask(tpci200, + &tpci200->info->interface_regs->control[islot], + TPCI200_INT0_EN | TPCI200_INT1_EN); +} + +static irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq) +{ + irqreturn_t ret; + + if (!slot_irq) + return -ENODEV; + ret = slot_irq->handler(slot_irq->arg); + + return ret; +} + +static irqreturn_t tpci200_interrupt(int irq, void *dev_id) +{ + struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id; + struct slot_irq *slot_irq; + irqreturn_t ret; + u16 status_reg; + int i; + + /* Read status register */ + status_reg = ioread16(&tpci200->info->interface_regs->status); + + /* Did we cause the interrupt? */ + if (!(status_reg & TPCI200_SLOT_INT_MASK)) + return IRQ_NONE; + + /* callback to the IRQ handler for the corresponding slot */ + rcu_read_lock(); + for (i = 0; i < TPCI200_NB_SLOT; i++) { + if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i)))) + continue; + slot_irq = rcu_dereference(tpci200->slots[i].irq); + ret = tpci200_slot_irq(slot_irq); + if (ret == -ENODEV) { + dev_info(&tpci200->info->pdev->dev, + "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n", + tpci200->number, i); + tpci200_disable_irq(tpci200, i); + } + } + rcu_read_unlock(); + + return IRQ_HANDLED; +} + +static int tpci200_free_irq(struct ipack_device *dev) +{ + struct slot_irq *slot_irq; + struct tpci200_board *tpci200; + + tpci200 = check_slot(dev); + if (tpci200 == NULL) + return -EINVAL; + + if (mutex_lock_interruptible(&tpci200->mutex)) + return -ERESTARTSYS; + + if (tpci200->slots[dev->slot].irq == NULL) { + mutex_unlock(&tpci200->mutex); + return -EINVAL; + } + + tpci200_disable_irq(tpci200, dev->slot); + slot_irq = tpci200->slots[dev->slot].irq; + /* uninstall handler */ + RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL); + synchronize_rcu(); + kfree(slot_irq); + mutex_unlock(&tpci200->mutex); + return 0; +} + +static int tpci200_request_irq(struct ipack_device *dev, + irqreturn_t (*handler)(void *), void *arg) +{ + int res = 0; + struct slot_irq *slot_irq; + struct tpci200_board *tpci200; + + tpci200 = check_slot(dev); + if (tpci200 == NULL) + return -EINVAL; + + if (mutex_lock_interruptible(&tpci200->mutex)) + return -ERESTARTSYS; + + if (tpci200->slots[dev->slot].irq != NULL) { + dev_err(&dev->dev, + "Slot [%d:%d] IRQ already registered !\n", + dev->bus->bus_nr, + dev->slot); + res = -EINVAL; + goto out_unlock; + } + + slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL); + if (slot_irq == NULL) { + dev_err(&dev->dev, + "Slot [%d:%d] unable to allocate memory for IRQ !\n", + dev->bus->bus_nr, dev->slot); + res = -ENOMEM; + goto out_unlock; + } + + /* + * WARNING: Setup Interrupt Vector in the IndustryPack device + * before an IRQ request. + * Read the User Manual of your IndustryPack device to know + * where to write the vector in memory. + */ + slot_irq->handler = handler; + slot_irq->arg = arg; + slot_irq->holder = dev; + + rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq); + tpci200_enable_irq(tpci200, dev->slot); + +out_unlock: + mutex_unlock(&tpci200->mutex); + return res; +} + +static int tpci200_register(struct tpci200_board *tpci200) +{ + int i; + int res; + phys_addr_t ioidint_base; + unsigned short slot_ctrl; + + if (pci_enable_device(tpci200->info->pdev) < 0) + return -ENODEV; + + /* Request IP interface register (Bar 2) */ + res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR, + "Carrier IP interface registers"); + if (res) { + dev_err(&tpci200->info->pdev->dev, + "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !", + tpci200->info->pdev->bus->number, + tpci200->info->pdev->devfn); + goto out_disable_pci; + } + + /* Request IO ID INT space (Bar 3) */ + res = pci_request_region(tpci200->info->pdev, + TPCI200_IO_ID_INT_SPACES_BAR, + "Carrier IO ID INT space"); + if (res) { + dev_err(&tpci200->info->pdev->dev, + "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !", + tpci200->info->pdev->bus->number, + tpci200->info->pdev->devfn); + goto out_release_ip_space; + } + + /* Request MEM8 space (Bar 5) */ + res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR, + "Carrier MEM8 space"); + if (res) { + dev_err(&tpci200->info->pdev->dev, + "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!", + tpci200->info->pdev->bus->number, + tpci200->info->pdev->devfn); + goto out_release_ioid_int_space; + } + + /* Request MEM16 space (Bar 4) */ + res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR, + "Carrier MEM16 space"); + if (res) { + dev_err(&tpci200->info->pdev->dev, + "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!", + tpci200->info->pdev->bus->number, + tpci200->info->pdev->devfn); + goto out_release_mem8_space; + } + + /* Map internal tpci200 driver user space */ + tpci200->info->interface_regs = + ioremap_nocache(pci_resource_start(tpci200->info->pdev, + TPCI200_IP_INTERFACE_BAR), + TPCI200_IFACE_SIZE); + + /* Initialize lock that protects interface_regs */ + spin_lock_init(&tpci200->regs_lock); + + ioidint_base = pci_resource_start(tpci200->info->pdev, + TPCI200_IO_ID_INT_SPACES_BAR); + tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF; + tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF; + tpci200->mod_mem[IPACK_INT_SPACE] = + ioidint_base + TPCI200_INT_SPACE_OFF; + tpci200->mod_mem[IPACK_MEM8_SPACE] = + pci_resource_start(tpci200->info->pdev, + TPCI200_MEM8_SPACE_BAR); + tpci200->mod_mem[IPACK_MEM16_SPACE] = + pci_resource_start(tpci200->info->pdev, + TPCI200_MEM16_SPACE_BAR); + + /* Set the default parameters of the slot + * INT0 disabled, level sensitive + * INT1 disabled, level sensitive + * error interrupt disabled + * timeout interrupt disabled + * recover time disabled + * clock rate 8 MHz + */ + slot_ctrl = 0; + for (i = 0; i < TPCI200_NB_SLOT; i++) + writew(slot_ctrl, &tpci200->info->interface_regs->control[i]); + + res = request_irq(tpci200->info->pdev->irq, + tpci200_interrupt, IRQF_SHARED, + KBUILD_MODNAME, (void *) tpci200); + if (res) { + dev_err(&tpci200->info->pdev->dev, + "(bn 0x%X, sn 0x%X) unable to register IRQ !", + tpci200->info->pdev->bus->number, + tpci200->info->pdev->devfn); + goto out_release_ioid_int_space; + } + + return 0; + +out_release_mem8_space: + pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); +out_release_ioid_int_space: + pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); +out_release_ip_space: + pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); +out_disable_pci: + pci_disable_device(tpci200->info->pdev); + return res; +} + +static int tpci200_get_clockrate(struct ipack_device *dev) +{ + struct tpci200_board *tpci200 = check_slot(dev); + __le16 __iomem *addr; + + if (!tpci200) + return -ENODEV; + + addr = &tpci200->info->interface_regs->control[dev->slot]; + return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8; +} + +static int tpci200_set_clockrate(struct ipack_device *dev, int mherz) +{ + struct tpci200_board *tpci200 = check_slot(dev); + __le16 __iomem *addr; + + if (!tpci200) + return -ENODEV; + + addr = &tpci200->info->interface_regs->control[dev->slot]; + + switch (mherz) { + case 8: + tpci200_clear_mask(tpci200, addr, TPCI200_CLK32); + break; + case 32: + tpci200_set_mask(tpci200, addr, TPCI200_CLK32); + break; + default: + return -EINVAL; + } + return 0; +} + +static int tpci200_get_error(struct ipack_device *dev) +{ + struct tpci200_board *tpci200 = check_slot(dev); + __le16 __iomem *addr; + u16 mask; + + if (!tpci200) + return -ENODEV; + + addr = &tpci200->info->interface_regs->status; + mask = tpci200_status_error[dev->slot]; + return (ioread16(addr) & mask) ? 1 : 0; +} + +static int tpci200_get_timeout(struct ipack_device *dev) +{ + struct tpci200_board *tpci200 = check_slot(dev); + __le16 __iomem *addr; + u16 mask; + + if (!tpci200) + return -ENODEV; + + addr = &tpci200->info->interface_regs->status; + mask = tpci200_status_timeout[dev->slot]; + + return (ioread16(addr) & mask) ? 1 : 0; +} + +static int tpci200_reset_timeout(struct ipack_device *dev) +{ + struct tpci200_board *tpci200 = check_slot(dev); + __le16 __iomem *addr; + u16 mask; + + if (!tpci200) + return -ENODEV; + + addr = &tpci200->info->interface_regs->status; + mask = tpci200_status_timeout[dev->slot]; + + iowrite16(mask, addr); + return 0; +} + +static void tpci200_uninstall(struct tpci200_board *tpci200) +{ + tpci200_unregister(tpci200); + kfree(tpci200->slots); +} + +static const struct ipack_bus_ops tpci200_bus_ops = { + .request_irq = tpci200_request_irq, + .free_irq = tpci200_free_irq, + .get_clockrate = tpci200_get_clockrate, + .set_clockrate = tpci200_set_clockrate, + .get_error = tpci200_get_error, + .get_timeout = tpci200_get_timeout, + .reset_timeout = tpci200_reset_timeout, +}; + +static int tpci200_install(struct tpci200_board *tpci200) +{ + int res; + + tpci200->slots = kzalloc( + TPCI200_NB_SLOT * sizeof(struct tpci200_slot), GFP_KERNEL); + if (tpci200->slots == NULL) + return -ENOMEM; + + res = tpci200_register(tpci200); + if (res) { + kfree(tpci200->slots); + tpci200->slots = NULL; + return res; + } + + mutex_init(&tpci200->mutex); + return 0; +} + +static void tpci200_release_device(struct ipack_device *dev) +{ + kfree(dev); +} + +static int tpci200_create_device(struct tpci200_board *tpci200, int i) +{ + enum ipack_space space; + struct ipack_device *dev = + kzalloc(sizeof(struct ipack_device), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->slot = i; + dev->bus = tpci200->info->ipack_bus; + dev->release = tpci200_release_device; + + for (space = 0; space < IPACK_SPACE_COUNT; space++) { + dev->region[space].start = + tpci200->mod_mem[space] + + tpci200_space_interval[space] * i; + dev->region[space].size = tpci200_space_size[space]; + } + return ipack_device_register(dev); +} + +static int tpci200_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret, i; + struct tpci200_board *tpci200; + u32 reg32; + + tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL); + if (!tpci200) + return -ENOMEM; + + tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL); + if (!tpci200->info) { + ret = -ENOMEM; + goto out_err_info; + } + + pci_dev_get(pdev); + + /* Obtain a mapping of the carrier's PCI configuration registers */ + ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR, + KBUILD_MODNAME " Configuration Memory"); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory"); + ret = -EBUSY; + goto out_err_pci_request; + } + tpci200->info->cfg_regs = ioremap_nocache( + pci_resource_start(pdev, TPCI200_CFG_MEM_BAR), + pci_resource_len(pdev, TPCI200_CFG_MEM_BAR)); + if (!tpci200->info->cfg_regs) { + dev_err(&pdev->dev, "Failed to map PCI Configuration Memory"); + ret = -EFAULT; + goto out_err_ioremap; + } + + /* Disable byte swapping for 16 bit IP module access. This will ensure + * that the Industrypack big endian byte order is preserved by the + * carrier. */ + reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC); + reg32 |= 1 << LAS_BIT_BIGENDIAN; + iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC); + + reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC); + reg32 |= 1 << LAS_BIT_BIGENDIAN; + iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC); + + /* Save struct pci_dev pointer */ + tpci200->info->pdev = pdev; + tpci200->info->id_table = (struct pci_device_id *)id; + + /* register the device and initialize it */ + ret = tpci200_install(tpci200); + if (ret) { + dev_err(&pdev->dev, "error during tpci200 install\n"); + ret = -ENODEV; + goto out_err_install; + } + + /* Register the carrier in the industry pack bus driver */ + tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, + TPCI200_NB_SLOT, + &tpci200_bus_ops); + if (!tpci200->info->ipack_bus) { + dev_err(&pdev->dev, + "error registering the carrier on ipack driver\n"); + ret = -EFAULT; + goto out_err_bus_register; + } + + /* save the bus number given by ipack to logging purpose */ + tpci200->number = tpci200->info->ipack_bus->bus_nr; + dev_set_drvdata(&pdev->dev, tpci200); + + for (i = 0; i < TPCI200_NB_SLOT; i++) + tpci200_create_device(tpci200, i); + return 0; + +out_err_bus_register: + tpci200_uninstall(tpci200); +out_err_install: + iounmap(tpci200->info->cfg_regs); +out_err_ioremap: + pci_release_region(pdev, TPCI200_CFG_MEM_BAR); +out_err_pci_request: + pci_dev_put(pdev); + kfree(tpci200->info); +out_err_info: + kfree(tpci200); + return ret; +} + +static void __tpci200_pci_remove(struct tpci200_board *tpci200) +{ + ipack_bus_unregister(tpci200->info->ipack_bus); + tpci200_uninstall(tpci200); + + kfree(tpci200->info); + kfree(tpci200); +} + +static void __devexit tpci200_pci_remove(struct pci_dev *dev) +{ + struct tpci200_board *tpci200 = pci_get_drvdata(dev); + + __tpci200_pci_remove(tpci200); +} + +static DEFINE_PCI_DEVICE_TABLE(tpci200_idtable) = { + { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, + TPCI200_SUBDEVICE_ID }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, tpci200_idtable); + +static struct pci_driver tpci200_pci_drv = { + .name = "tpci200", + .id_table = tpci200_idtable, + .probe = tpci200_pci_probe, + .remove = __devexit_p(tpci200_pci_remove), +}; + +module_pci_driver(tpci200_pci_drv); + +MODULE_DESCRIPTION("TEWS TPCI-200 device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ipack/carriers/tpci200.h b/drivers/ipack/carriers/tpci200.h new file mode 100644 index 000000000000..8d9be277b34d --- /dev/null +++ b/drivers/ipack/carriers/tpci200.h @@ -0,0 +1,168 @@ +/** + * tpci200.h + * + * driver for the carrier TEWS TPCI-200 + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#ifndef _TPCI200_H_ +#define _TPCI200_H_ + +#include +#include +#include +#include +#include + +#include "../ipack.h" + +#define TPCI200_NB_SLOT 0x4 +#define TPCI200_NB_BAR 0x6 + +#define TPCI200_VENDOR_ID 0x1498 +#define TPCI200_DEVICE_ID 0x30C8 +#define TPCI200_SUBVENDOR_ID 0x1498 +#define TPCI200_SUBDEVICE_ID 0x300A + +#define TPCI200_CFG_MEM_BAR 0 +#define TPCI200_IP_INTERFACE_BAR 2 +#define TPCI200_IO_ID_INT_SPACES_BAR 3 +#define TPCI200_MEM16_SPACE_BAR 4 +#define TPCI200_MEM8_SPACE_BAR 5 + +struct tpci200_regs { + __le16 revision; + /* writes to control should occur with the mutex held to protect + * read-modify-write operations */ + __le16 control[4]; + __le16 reset; + __le16 status; + u8 reserved[242]; +} __packed; + +#define TPCI200_IFACE_SIZE 0x100 + +#define TPCI200_IO_SPACE_OFF 0x0000 +#define TPCI200_IO_SPACE_INTERVAL 0x0100 +#define TPCI200_IO_SPACE_SIZE 0x0080 +#define TPCI200_ID_SPACE_OFF 0x0080 +#define TPCI200_ID_SPACE_INTERVAL 0x0100 +#define TPCI200_ID_SPACE_SIZE 0x0040 +#define TPCI200_INT_SPACE_OFF 0x00C0 +#define TPCI200_INT_SPACE_INTERVAL 0x0100 +#define TPCI200_INT_SPACE_SIZE 0x0040 +#define TPCI200_IOIDINT_SIZE 0x0400 + +#define TPCI200_MEM8_SPACE_INTERVAL 0x00400000 +#define TPCI200_MEM8_SPACE_SIZE 0x00400000 +#define TPCI200_MEM16_SPACE_INTERVAL 0x00800000 +#define TPCI200_MEM16_SPACE_SIZE 0x00800000 + +/* control field in tpci200_regs */ +#define TPCI200_INT0_EN 0x0040 +#define TPCI200_INT1_EN 0x0080 +#define TPCI200_INT0_EDGE 0x0010 +#define TPCI200_INT1_EDGE 0x0020 +#define TPCI200_ERR_INT_EN 0x0008 +#define TPCI200_TIME_INT_EN 0x0004 +#define TPCI200_RECOVER_EN 0x0002 +#define TPCI200_CLK32 0x0001 + +/* reset field in tpci200_regs */ +#define TPCI200_A_RESET 0x0001 +#define TPCI200_B_RESET 0x0002 +#define TPCI200_C_RESET 0x0004 +#define TPCI200_D_RESET 0x0008 + +/* status field in tpci200_regs */ +#define TPCI200_A_TIMEOUT 0x1000 +#define TPCI200_B_TIMEOUT 0x2000 +#define TPCI200_C_TIMEOUT 0x4000 +#define TPCI200_D_TIMEOUT 0x8000 + +#define TPCI200_A_ERROR 0x0100 +#define TPCI200_B_ERROR 0x0200 +#define TPCI200_C_ERROR 0x0400 +#define TPCI200_D_ERROR 0x0800 + +#define TPCI200_A_INT0 0x0001 +#define TPCI200_A_INT1 0x0002 +#define TPCI200_B_INT0 0x0004 +#define TPCI200_B_INT1 0x0008 +#define TPCI200_C_INT0 0x0010 +#define TPCI200_C_INT1 0x0020 +#define TPCI200_D_INT0 0x0040 +#define TPCI200_D_INT1 0x0080 + +#define TPCI200_SLOT_INT_MASK 0x00FF + +/* PCI Configuration registers. The PCI bridge is a PLX Technology PCI9030. */ +#define LAS1_DESC 0x2C +#define LAS2_DESC 0x30 + +/* Bits in the LAS?_DESC registers */ +#define LAS_BIT_BIGENDIAN 24 + +#define VME_IOID_SPACE "IOID" +#define VME_MEM_SPACE "MEM" + +/** + * struct slot_irq - slot IRQ definition. + * @vector Vector number + * @handler Handler called when IRQ arrives + * @arg Handler argument + * + */ +struct slot_irq { + struct ipack_device *holder; + int vector; + irqreturn_t (*handler)(void *); + void *arg; +}; + +/** + * struct tpci200_slot - data specific to the tpci200 slot. + * @slot_id Slot identification gived to external interface + * @irq Slot IRQ infos + * @io_phys IO physical base address register of the slot + * @id_phys ID physical base address register of the slot + * @int_phys INT physical base address register of the slot + * @mem_phys MEM physical base address register of the slot + * + */ +struct tpci200_slot { + struct slot_irq *irq; +}; + +/** + * struct tpci200_infos - informations specific of the TPCI200 tpci200. + * @pci_dev PCI device + * @interface_regs Pointer to IP interface space (Bar 2) + * @ioidint_space Pointer to IP ID, IO and INT space (Bar 3) + * @mem8_space Pointer to MEM space (Bar 4) + * + */ +struct tpci200_infos { + struct pci_dev *pdev; + struct pci_device_id *id_table; + struct tpci200_regs __iomem *interface_regs; + void __iomem *cfg_regs; + struct ipack_bus_device *ipack_bus; +}; +struct tpci200_board { + unsigned int number; + struct mutex mutex; + spinlock_t regs_lock; + struct tpci200_slot *slots; + struct tpci200_infos *info; + phys_addr_t mod_mem[IPACK_SPACE_COUNT]; +}; + +#endif /* _TPCI200_H_ */ diff --git a/drivers/ipack/devices/Kconfig b/drivers/ipack/devices/Kconfig new file mode 100644 index 000000000000..0b82fdc198c0 --- /dev/null +++ b/drivers/ipack/devices/Kconfig @@ -0,0 +1,6 @@ +config SERIAL_IPOCTAL + tristate "IndustryPack IP-OCTAL uart support" + depends on IPACK_BUS + help + This driver supports the IPOCTAL serial port device for the IndustryPack bus. + default n diff --git a/drivers/ipack/devices/Makefile b/drivers/ipack/devices/Makefile new file mode 100644 index 000000000000..6de18bda4a9a --- /dev/null +++ b/drivers/ipack/devices/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SERIAL_IPOCTAL) += ipoctal.o diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c new file mode 100644 index 000000000000..783f120338d1 --- /dev/null +++ b/drivers/ipack/devices/ipoctal.c @@ -0,0 +1,751 @@ +/** + * ipoctal.c + * + * driver for the GE IP-OCTAL boards + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ipack.h" +#include "ipoctal.h" +#include "scc2698.h" + +#define IP_OCTAL_ID_SPACE_VECTOR 0x41 +#define IP_OCTAL_NB_BLOCKS 4 + +static const struct tty_operations ipoctal_fops; + +struct ipoctal_channel { + struct ipoctal_stats stats; + unsigned int nb_bytes; + wait_queue_head_t queue; + spinlock_t lock; + unsigned int pointer_read; + unsigned int pointer_write; + atomic_t open; + struct tty_port tty_port; + union scc2698_channel __iomem *regs; + union scc2698_block __iomem *block_regs; + unsigned int board_id; + unsigned char *board_write; + u8 isr_rx_rdy_mask; + u8 isr_tx_rdy_mask; +}; + +struct ipoctal { + struct ipack_device *dev; + unsigned int board_id; + struct ipoctal_channel channel[NR_CHANNELS]; + unsigned char write; + struct tty_driver *tty_drv; + u8 __iomem *mem8_space; + u8 __iomem *int_space; +}; + +static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct ipoctal_channel *channel; + + channel = dev_get_drvdata(tty->dev); + + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); + return 0; +} + +static int ipoctal_open(struct tty_struct *tty, struct file *file) +{ + int res; + struct ipoctal_channel *channel; + + channel = dev_get_drvdata(tty->dev); + + if (atomic_read(&channel->open)) + return -EBUSY; + + tty->driver_data = channel; + + res = tty_port_open(&channel->tty_port, tty, file); + if (res) + return res; + + atomic_inc(&channel->open); + return 0; +} + +static void ipoctal_reset_stats(struct ipoctal_stats *stats) +{ + stats->tx = 0; + stats->rx = 0; + stats->rcv_break = 0; + stats->framing_err = 0; + stats->overrun_err = 0; + stats->parity_err = 0; +} + +static void ipoctal_free_channel(struct ipoctal_channel *channel) +{ + ipoctal_reset_stats(&channel->stats); + channel->pointer_read = 0; + channel->pointer_write = 0; + channel->nb_bytes = 0; +} + +static void ipoctal_close(struct tty_struct *tty, struct file *filp) +{ + struct ipoctal_channel *channel = tty->driver_data; + + tty_port_close(&channel->tty_port, tty, filp); + + if (atomic_dec_and_test(&channel->open)) + ipoctal_free_channel(channel); +} + +static int ipoctal_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct ipoctal_channel *channel = tty->driver_data; + + icount->cts = 0; + icount->dsr = 0; + icount->rng = 0; + icount->dcd = 0; + icount->rx = channel->stats.rx; + icount->tx = channel->stats.tx; + icount->frame = channel->stats.framing_err; + icount->parity = channel->stats.parity_err; + icount->brk = channel->stats.rcv_break; + return 0; +} + +static void ipoctal_irq_rx(struct ipoctal_channel *channel, + struct tty_struct *tty, u8 sr) +{ + unsigned char value; + unsigned char flag = TTY_NORMAL; + u8 isr; + + do { + value = ioread8(&channel->regs->r.rhr); + /* Error: count statistics */ + if (sr & SR_ERROR) { + iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); + + if (sr & SR_OVERRUN_ERROR) { + channel->stats.overrun_err++; + /* Overrun doesn't affect the current character*/ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + if (sr & SR_PARITY_ERROR) { + channel->stats.parity_err++; + flag = TTY_PARITY; + } + if (sr & SR_FRAMING_ERROR) { + channel->stats.framing_err++; + flag = TTY_FRAME; + } + if (sr & SR_RECEIVED_BREAK) { + iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr); + channel->stats.rcv_break++; + flag = TTY_BREAK; + } + } + tty_insert_flip_char(tty, value, flag); + + /* Check if there are more characters in RX FIFO + * If there are more, the isr register for this channel + * has enabled the RxRDY|FFULL bit. + */ + isr = ioread8(&channel->block_regs->r.isr); + sr = ioread8(&channel->regs->r.sr); + } while (isr & channel->isr_rx_rdy_mask); + + tty_flip_buffer_push(tty); +} + +static void ipoctal_irq_tx(struct ipoctal_channel *channel) +{ + unsigned char value; + unsigned int *pointer_write = &channel->pointer_write; + + if (channel->nb_bytes <= 0) { + channel->nb_bytes = 0; + return; + } + + value = channel->tty_port.xmit_buf[*pointer_write]; + iowrite8(value, &channel->regs->w.thr); + channel->stats.tx++; + (*pointer_write)++; + *pointer_write = *pointer_write % PAGE_SIZE; + channel->nb_bytes--; + + if ((channel->nb_bytes == 0) && + (waitqueue_active(&channel->queue))) { + + if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) { + *channel->board_write = 1; + wake_up_interruptible(&channel->queue); + } + } +} + +static void ipoctal_irq_channel(struct ipoctal_channel *channel) +{ + u8 isr, sr; + struct tty_struct *tty; + + /* If there is no client, skip the check */ + if (!atomic_read(&channel->open)) + return; + + tty = tty_port_tty_get(&channel->tty_port); + if (!tty) + return; + /* The HW is organized in pair of channels. See which register we need + * to read from */ + isr = ioread8(&channel->block_regs->r.isr); + sr = ioread8(&channel->regs->r.sr); + + /* In case of RS-485, change from TX to RX when finishing TX. + * Half-duplex. */ + if ((channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) && + (sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) { + iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr); + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); + *channel->board_write = 1; + wake_up_interruptible(&channel->queue); + } + + /* RX data */ + if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY)) + ipoctal_irq_rx(channel, tty, sr); + + /* TX of each character */ + if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) + ipoctal_irq_tx(channel); + + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static irqreturn_t ipoctal_irq_handler(void *arg) +{ + unsigned int i; + struct ipoctal *ipoctal = (struct ipoctal *) arg; + + /* Check all channels */ + for (i = 0; i < NR_CHANNELS; i++) + ipoctal_irq_channel(&ipoctal->channel[i]); + + /* Clear the IPack device interrupt */ + readw(ipoctal->int_space + ACK_INT_REQ0); + readw(ipoctal->int_space + ACK_INT_REQ1); + + return IRQ_HANDLED; +} + +static const struct tty_port_operations ipoctal_tty_port_ops = { + .dtr_rts = NULL, + .activate = ipoctal_port_activate, +}; + +static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, + unsigned int slot) +{ + int res; + int i; + struct tty_driver *tty; + char name[20]; + struct ipoctal_channel *channel; + struct ipack_region *region; + void __iomem *addr; + union scc2698_channel __iomem *chan_regs; + union scc2698_block __iomem *block_regs; + + ipoctal->board_id = ipoctal->dev->id_device; + + region = &ipoctal->dev->region[IPACK_IO_SPACE]; + addr = devm_ioremap_nocache(&ipoctal->dev->dev, + region->start, region->size); + if (!addr) { + dev_err(&ipoctal->dev->dev, + "Unable to map slot [%d:%d] IO space!\n", + bus_nr, slot); + return -EADDRNOTAVAIL; + } + /* Save the virtual address to access the registers easily */ + chan_regs = + (union scc2698_channel __iomem *) addr; + block_regs = + (union scc2698_block __iomem *) addr; + + region = &ipoctal->dev->region[IPACK_INT_SPACE]; + ipoctal->int_space = + devm_ioremap_nocache(&ipoctal->dev->dev, + region->start, region->size); + if (!ipoctal->int_space) { + dev_err(&ipoctal->dev->dev, + "Unable to map slot [%d:%d] INT space!\n", + bus_nr, slot); + return -EADDRNOTAVAIL; + } + + region = &ipoctal->dev->region[IPACK_MEM8_SPACE]; + ipoctal->mem8_space = + devm_ioremap_nocache(&ipoctal->dev->dev, + region->start, 0x8000); + if (!addr) { + dev_err(&ipoctal->dev->dev, + "Unable to map slot [%d:%d] MEM8 space!\n", + bus_nr, slot); + return -EADDRNOTAVAIL; + } + + + /* Disable RX and TX before touching anything */ + for (i = 0; i < NR_CHANNELS ; i++) { + struct ipoctal_channel *channel = &ipoctal->channel[i]; + channel->regs = chan_regs + i; + channel->block_regs = block_regs + (i >> 1); + channel->board_write = &ipoctal->write; + channel->board_id = ipoctal->board_id; + if (i & 1) { + channel->isr_tx_rdy_mask = ISR_TxRDY_B; + channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B; + } else { + channel->isr_tx_rdy_mask = ISR_TxRDY_A; + channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A; + } + + iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); + iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY, + &channel->regs->w.mr); /* mr1 */ + iowrite8(0, &channel->regs->w.mr); /* mr2 */ + iowrite8(TX_CLK_9600 | RX_CLK_9600, &channel->regs->w.csr); + } + + for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) { + iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr); + iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN, + &block_regs[i].w.opcr); + iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A | + IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B, + &block_regs[i].w.imr); + } + + /* + * IP-OCTAL has different addresses to copy its IRQ vector. + * Depending of the carrier these addresses are accesible or not. + * More info in the datasheet. + */ + ipoctal->dev->bus->ops->request_irq(ipoctal->dev, + ipoctal_irq_handler, ipoctal); + /* Dummy write */ + iowrite8(1, ipoctal->mem8_space + 1); + + /* Register the TTY device */ + + /* Each IP-OCTAL channel is a TTY port */ + tty = alloc_tty_driver(NR_CHANNELS); + + if (!tty) + return -ENOMEM; + + /* Fill struct tty_driver with ipoctal data */ + tty->owner = THIS_MODULE; + tty->driver_name = KBUILD_MODNAME; + sprintf(name, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); + tty->name = name; + tty->major = 0; + + tty->minor_start = 0; + tty->type = TTY_DRIVER_TYPE_SERIAL; + tty->subtype = SERIAL_TYPE_NORMAL; + tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty->init_termios = tty_std_termios; + tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty->init_termios.c_ispeed = 9600; + tty->init_termios.c_ospeed = 9600; + + tty_set_operations(tty, &ipoctal_fops); + res = tty_register_driver(tty); + if (res) { + dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); + put_tty_driver(tty); + return res; + } + + /* Save struct tty_driver for use it when uninstalling the device */ + ipoctal->tty_drv = tty; + + for (i = 0; i < NR_CHANNELS; i++) { + struct device *tty_dev; + + channel = &ipoctal->channel[i]; + tty_port_init(&channel->tty_port); + tty_port_alloc_xmit_buf(&channel->tty_port); + channel->tty_port.ops = &ipoctal_tty_port_ops; + + ipoctal_reset_stats(&channel->stats); + channel->nb_bytes = 0; + init_waitqueue_head(&channel->queue); + + spin_lock_init(&channel->lock); + channel->pointer_read = 0; + channel->pointer_write = 0; + tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); + if (IS_ERR(tty_dev)) { + dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); + continue; + } + dev_set_drvdata(tty_dev, channel); + + /* + * Enable again the RX. TX will be enabled when + * there is something to send + */ + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); + } + + return 0; +} + +static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, + const unsigned char *buf, + int count) +{ + unsigned long flags; + int i; + unsigned int *pointer_read = &channel->pointer_read; + + /* Copy the bytes from the user buffer to the internal one */ + for (i = 0; i < count; i++) { + if (i <= (PAGE_SIZE - channel->nb_bytes)) { + spin_lock_irqsave(&channel->lock, flags); + channel->tty_port.xmit_buf[*pointer_read] = buf[i]; + *pointer_read = (*pointer_read + 1) % PAGE_SIZE; + channel->nb_bytes++; + spin_unlock_irqrestore(&channel->lock, flags); + } else { + break; + } + } + return i; +} + +static int ipoctal_write_tty(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct ipoctal_channel *channel = tty->driver_data; + unsigned int char_copied; + + char_copied = ipoctal_copy_write_buffer(channel, buf, count); + + /* As the IP-OCTAL 485 only supports half duplex, do it manually */ + if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) { + iowrite8(CR_DISABLE_RX, &channel->regs->w.cr); + iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr); + } + + /* + * Send a packet and then disable TX to avoid failure after several send + * operations + */ + iowrite8(CR_ENABLE_TX, &channel->regs->w.cr); + wait_event_interruptible(channel->queue, *channel->board_write); + iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); + + *channel->board_write = 0; + return char_copied; +} + +static int ipoctal_write_room(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + + return PAGE_SIZE - channel->nb_bytes; +} + +static int ipoctal_chars_in_buffer(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + + return channel->nb_bytes; +} + +static void ipoctal_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + unsigned int cflag; + unsigned char mr1 = 0; + unsigned char mr2 = 0; + unsigned char csr = 0; + struct ipoctal_channel *channel = tty->driver_data; + speed_t baud; + + cflag = tty->termios.c_cflag; + + /* Disable and reset everything before change the setup */ + iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); + + /* Set Bits per chars */ + switch (cflag & CSIZE) { + case CS6: + mr1 |= MR1_CHRL_6_BITS; + break; + case CS7: + mr1 |= MR1_CHRL_7_BITS; + break; + case CS8: + default: + mr1 |= MR1_CHRL_8_BITS; + /* By default, select CS8 */ + tty->termios.c_cflag = (cflag & ~CSIZE) | CS8; + break; + } + + /* Set Parity */ + if (cflag & PARENB) + if (cflag & PARODD) + mr1 |= MR1_PARITY_ON | MR1_PARITY_ODD; + else + mr1 |= MR1_PARITY_ON | MR1_PARITY_EVEN; + else + mr1 |= MR1_PARITY_OFF; + + /* Mark or space parity is not supported */ + tty->termios.c_cflag &= ~CMSPAR; + + /* Set stop bits */ + if (cflag & CSTOPB) + mr2 |= MR2_STOP_BITS_LENGTH_2; + else + mr2 |= MR2_STOP_BITS_LENGTH_1; + + /* Set the flow control */ + switch (channel->board_id) { + case IPACK1_DEVICE_ID_SBS_OCTAL_232: + if (cflag & CRTSCTS) { + mr1 |= MR1_RxRTS_CONTROL_ON; + mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON; + } else { + mr1 |= MR1_RxRTS_CONTROL_OFF; + mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; + } + break; + case IPACK1_DEVICE_ID_SBS_OCTAL_422: + mr1 |= MR1_RxRTS_CONTROL_OFF; + mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; + break; + case IPACK1_DEVICE_ID_SBS_OCTAL_485: + mr1 |= MR1_RxRTS_CONTROL_OFF; + mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF; + break; + default: + return; + break; + } + + baud = tty_get_baud_rate(tty); + tty_termios_encode_baud_rate(&tty->termios, baud, baud); + + /* Set baud rate */ + switch (baud) { + case 75: + csr |= TX_CLK_75 | RX_CLK_75; + break; + case 110: + csr |= TX_CLK_110 | RX_CLK_110; + break; + case 150: + csr |= TX_CLK_150 | RX_CLK_150; + break; + case 300: + csr |= TX_CLK_300 | RX_CLK_300; + break; + case 600: + csr |= TX_CLK_600 | RX_CLK_600; + break; + case 1200: + csr |= TX_CLK_1200 | RX_CLK_1200; + break; + case 1800: + csr |= TX_CLK_1800 | RX_CLK_1800; + break; + case 2000: + csr |= TX_CLK_2000 | RX_CLK_2000; + break; + case 2400: + csr |= TX_CLK_2400 | RX_CLK_2400; + break; + case 4800: + csr |= TX_CLK_4800 | RX_CLK_4800; + break; + case 9600: + csr |= TX_CLK_9600 | RX_CLK_9600; + break; + case 19200: + csr |= TX_CLK_19200 | RX_CLK_19200; + break; + case 38400: + default: + csr |= TX_CLK_38400 | RX_CLK_38400; + /* In case of default, we establish 38400 bps */ + tty_termios_encode_baud_rate(&tty->termios, 38400, 38400); + break; + } + + mr1 |= MR1_ERROR_CHAR; + mr1 |= MR1_RxINT_RxRDY; + + /* Write the control registers */ + iowrite8(mr1, &channel->regs->w.mr); + iowrite8(mr2, &channel->regs->w.mr); + iowrite8(csr, &channel->regs->w.csr); + + /* Enable again the RX */ + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); +} + +static void ipoctal_hangup(struct tty_struct *tty) +{ + unsigned long flags; + struct ipoctal_channel *channel = tty->driver_data; + + if (channel == NULL) + return; + + spin_lock_irqsave(&channel->lock, flags); + channel->nb_bytes = 0; + channel->pointer_read = 0; + channel->pointer_write = 0; + spin_unlock_irqrestore(&channel->lock, flags); + + tty_port_hangup(&channel->tty_port); + + iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); + + clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags); + wake_up_interruptible(&channel->tty_port.open_wait); +} + +static const struct tty_operations ipoctal_fops = { + .ioctl = NULL, + .open = ipoctal_open, + .close = ipoctal_close, + .write = ipoctal_write_tty, + .set_termios = ipoctal_set_termios, + .write_room = ipoctal_write_room, + .chars_in_buffer = ipoctal_chars_in_buffer, + .get_icount = ipoctal_get_icount, + .hangup = ipoctal_hangup, +}; + +static int ipoctal_probe(struct ipack_device *dev) +{ + int res; + struct ipoctal *ipoctal; + + ipoctal = kzalloc(sizeof(struct ipoctal), GFP_KERNEL); + if (ipoctal == NULL) + return -ENOMEM; + + ipoctal->dev = dev; + res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); + if (res) + goto out_uninst; + + dev_set_drvdata(&dev->dev, ipoctal); + return 0; + +out_uninst: + kfree(ipoctal); + return res; +} + +static void __ipoctal_remove(struct ipoctal *ipoctal) +{ + int i; + + ipoctal->dev->bus->ops->free_irq(ipoctal->dev); + + for (i = 0; i < NR_CHANNELS; i++) { + struct ipoctal_channel *channel = &ipoctal->channel[i]; + tty_unregister_device(ipoctal->tty_drv, i); + tty_port_free_xmit_buf(&channel->tty_port); + } + + tty_unregister_driver(ipoctal->tty_drv); + put_tty_driver(ipoctal->tty_drv); + kfree(ipoctal); +} + +static void ipoctal_remove(struct ipack_device *idev) +{ + __ipoctal_remove(dev_get_drvdata(&idev->dev)); +} + +static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = { + { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, + IPACK1_DEVICE_ID_SBS_OCTAL_232) }, + { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, + IPACK1_DEVICE_ID_SBS_OCTAL_422) }, + { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, + IPACK1_DEVICE_ID_SBS_OCTAL_485) }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(ipack, ipoctal_ids); + +static const struct ipack_driver_ops ipoctal_drv_ops = { + .probe = ipoctal_probe, + .remove = ipoctal_remove, +}; + +static struct ipack_driver driver = { + .ops = &ipoctal_drv_ops, + .id_table = ipoctal_ids, +}; + +static int __init ipoctal_init(void) +{ + return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME); +} + +static void __exit ipoctal_exit(void) +{ + ipack_driver_unregister(&driver); +} + +MODULE_DESCRIPTION("IP-Octal 232, 422 and 485 device driver"); +MODULE_LICENSE("GPL"); + +module_init(ipoctal_init); +module_exit(ipoctal_exit); diff --git a/drivers/ipack/devices/ipoctal.h b/drivers/ipack/devices/ipoctal.h new file mode 100644 index 000000000000..28f1c4233154 --- /dev/null +++ b/drivers/ipack/devices/ipoctal.h @@ -0,0 +1,42 @@ +/** + * ipoctal.h + * + * driver for the IPOCTAL boards + + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#ifndef _IPOCTAL_H +#define _IPOCTAL_H_ + +#define NR_CHANNELS 8 +#define IPOCTAL_MAX_BOARDS 16 +#define MAX_DEVICES (NR_CHANNELS * IPOCTAL_MAX_BOARDS) +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +/** + * struct ipoctal_stats -- Stats since last reset + * + * @tx: Number of transmitted bytes + * @rx: Number of received bytes + * @overrun: Number of overrun errors + * @parity_err: Number of parity errors + * @framing_err: Number of framing errors + * @rcv_break: Number of break received + */ +struct ipoctal_stats { + unsigned long tx; + unsigned long rx; + unsigned long overrun_err; + unsigned long parity_err; + unsigned long framing_err; + unsigned long rcv_break; +}; + +#endif /* _IPOCTAL_H_ */ diff --git a/drivers/ipack/devices/scc2698.h b/drivers/ipack/devices/scc2698.h new file mode 100644 index 000000000000..2ad6acd513fa --- /dev/null +++ b/drivers/ipack/devices/scc2698.h @@ -0,0 +1,228 @@ +/* + * scc2698.h + * + * driver for the IPOCTAL boards + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#ifndef SCC2698_H_ +#define SCC2698_H_ + +/* + * union scc2698_channel - Channel access to scc2698 IO + * + * dn value are only spacer. + * + */ +union scc2698_channel { + struct { + u8 d0, mr; /* Mode register 1/2*/ + u8 d1, sr; /* Status register */ + u8 d2, r1; /* reserved */ + u8 d3, rhr; /* Receive holding register (R) */ + u8 junk[8]; /* other crap for block control */ + } __packed r; /* Read access */ + struct { + u8 d0, mr; /* Mode register 1/2 */ + u8 d1, csr; /* Clock select register */ + u8 d2, cr; /* Command register */ + u8 d3, thr; /* Transmit holding register */ + u8 junk[8]; /* other crap for block control */ + } __packed w; /* Write access */ +}; + +/* + * union scc2698_block - Block access to scc2698 IO + * + * The scc2698 contain 4 block. + * Each block containt two channel a and b. + * dn value are only spacer. + * + */ +union scc2698_block { + struct { + u8 d0, mra; /* Mode register 1/2 (a) */ + u8 d1, sra; /* Status register (a) */ + u8 d2, r1; /* reserved */ + u8 d3, rhra; /* Receive holding register (a) */ + u8 d4, ipcr; /* Input port change register of block */ + u8 d5, isr; /* Interrupt status register of block */ + u8 d6, ctur; /* Counter timer upper register of block */ + u8 d7, ctlr; /* Counter timer lower register of block */ + u8 d8, mrb; /* Mode register 1/2 (b) */ + u8 d9, srb; /* Status register (b) */ + u8 da, r2; /* reserved */ + u8 db, rhrb; /* Receive holding register (b) */ + u8 dc, r3; /* reserved */ + u8 dd, ip; /* Input port register of block */ + u8 de, ctg; /* Start counter timer of block */ + u8 df, cts; /* Stop counter timer of block */ + } __packed r; /* Read access */ + struct { + u8 d0, mra; /* Mode register 1/2 (a) */ + u8 d1, csra; /* Clock select register (a) */ + u8 d2, cra; /* Command register (a) */ + u8 d3, thra; /* Transmit holding register (a) */ + u8 d4, acr; /* Auxiliary control register of block */ + u8 d5, imr; /* Interrupt mask register of block */ + u8 d6, ctu; /* Counter timer upper register of block */ + u8 d7, ctl; /* Counter timer lower register of block */ + u8 d8, mrb; /* Mode register 1/2 (b) */ + u8 d9, csrb; /* Clock select register (a) */ + u8 da, crb; /* Command register (b) */ + u8 db, thrb; /* Transmit holding register (b) */ + u8 dc, r1; /* reserved */ + u8 dd, opcr; /* Output port configuration register of block */ + u8 de, r2; /* reserved */ + u8 df, r3; /* reserved */ + } __packed w; /* Write access */ +}; + +#define MR1_CHRL_5_BITS (0x0 << 0) +#define MR1_CHRL_6_BITS (0x1 << 0) +#define MR1_CHRL_7_BITS (0x2 << 0) +#define MR1_CHRL_8_BITS (0x3 << 0) +#define MR1_PARITY_EVEN (0x1 << 2) +#define MR1_PARITY_ODD (0x0 << 2) +#define MR1_PARITY_ON (0x0 << 3) +#define MR1_PARITY_FORCE (0x1 << 3) +#define MR1_PARITY_OFF (0x2 << 3) +#define MR1_PARITY_SPECIAL (0x3 << 3) +#define MR1_ERROR_CHAR (0x0 << 5) +#define MR1_ERROR_BLOCK (0x1 << 5) +#define MR1_RxINT_RxRDY (0x0 << 6) +#define MR1_RxINT_FFULL (0x1 << 6) +#define MR1_RxRTS_CONTROL_ON (0x1 << 7) +#define MR1_RxRTS_CONTROL_OFF (0x0 << 7) + +#define MR2_STOP_BITS_LENGTH_1 (0x7 << 0) +#define MR2_STOP_BITS_LENGTH_2 (0xF << 0) +#define MR2_CTS_ENABLE_TX_ON (0x1 << 4) +#define MR2_CTS_ENABLE_TX_OFF (0x0 << 4) +#define MR2_TxRTS_CONTROL_ON (0x1 << 5) +#define MR2_TxRTS_CONTROL_OFF (0x0 << 5) +#define MR2_CH_MODE_NORMAL (0x0 << 6) +#define MR2_CH_MODE_ECHO (0x1 << 6) +#define MR2_CH_MODE_LOCAL (0x2 << 6) +#define MR2_CH_MODE_REMOTE (0x3 << 6) + +#define CR_ENABLE_RX (0x1 << 0) +#define CR_DISABLE_RX (0x1 << 1) +#define CR_ENABLE_TX (0x1 << 2) +#define CR_DISABLE_TX (0x1 << 3) +#define CR_CMD_RESET_MR (0x1 << 4) +#define CR_CMD_RESET_RX (0x2 << 4) +#define CR_CMD_RESET_TX (0x3 << 4) +#define CR_CMD_RESET_ERR_STATUS (0x4 << 4) +#define CR_CMD_RESET_BREAK_CHANGE (0x5 << 4) +#define CR_CMD_START_BREAK (0x6 << 4) +#define CR_CMD_STOP_BREAK (0x7 << 4) +#define CR_CMD_ASSERT_RTSN (0x8 << 4) +#define CR_CMD_NEGATE_RTSN (0x9 << 4) +#define CR_CMD_SET_TIMEOUT_MODE (0xA << 4) +#define CR_CMD_DISABLE_TIMEOUT_MODE (0xC << 4) + +#define SR_RX_READY (0x1 << 0) +#define SR_FIFO_FULL (0x1 << 1) +#define SR_TX_READY (0x1 << 2) +#define SR_TX_EMPTY (0x1 << 3) +#define SR_OVERRUN_ERROR (0x1 << 4) +#define SR_PARITY_ERROR (0x1 << 5) +#define SR_FRAMING_ERROR (0x1 << 6) +#define SR_RECEIVED_BREAK (0x1 << 7) + +#define SR_ERROR (0xF0) + +#define ACR_DELTA_IP0_IRQ_EN (0x1 << 0) +#define ACR_DELTA_IP1_IRQ_EN (0x1 << 1) +#define ACR_DELTA_IP2_IRQ_EN (0x1 << 2) +#define ACR_DELTA_IP3_IRQ_EN (0x1 << 3) +#define ACR_CT_Mask (0x7 << 4) +#define ACR_CExt (0x0 << 4) +#define ACR_CTxCA (0x1 << 4) +#define ACR_CTxCB (0x2 << 4) +#define ACR_CClk16 (0x3 << 4) +#define ACR_TExt (0x4 << 4) +#define ACR_TExt16 (0x5 << 4) +#define ACR_TClk (0x6 << 4) +#define ACR_TClk16 (0x7 << 4) +#define ACR_BRG_SET1 (0x0 << 7) +#define ACR_BRG_SET2 (0x1 << 7) + +#define TX_CLK_75 (0x0 << 0) +#define TX_CLK_110 (0x1 << 0) +#define TX_CLK_38400 (0x2 << 0) +#define TX_CLK_150 (0x3 << 0) +#define TX_CLK_300 (0x4 << 0) +#define TX_CLK_600 (0x5 << 0) +#define TX_CLK_1200 (0x6 << 0) +#define TX_CLK_2000 (0x7 << 0) +#define TX_CLK_2400 (0x8 << 0) +#define TX_CLK_4800 (0x9 << 0) +#define TX_CLK_1800 (0xA << 0) +#define TX_CLK_9600 (0xB << 0) +#define TX_CLK_19200 (0xC << 0) +#define RX_CLK_75 (0x0 << 4) +#define RX_CLK_110 (0x1 << 4) +#define RX_CLK_38400 (0x2 << 4) +#define RX_CLK_150 (0x3 << 4) +#define RX_CLK_300 (0x4 << 4) +#define RX_CLK_600 (0x5 << 4) +#define RX_CLK_1200 (0x6 << 4) +#define RX_CLK_2000 (0x7 << 4) +#define RX_CLK_2400 (0x8 << 4) +#define RX_CLK_4800 (0x9 << 4) +#define RX_CLK_1800 (0xA << 4) +#define RX_CLK_9600 (0xB << 4) +#define RX_CLK_19200 (0xC << 4) + +#define OPCR_MPOa_RTSN (0x0 << 0) +#define OPCR_MPOa_C_TO (0x1 << 0) +#define OPCR_MPOa_TxC1X (0x2 << 0) +#define OPCR_MPOa_TxC16X (0x3 << 0) +#define OPCR_MPOa_RxC1X (0x4 << 0) +#define OPCR_MPOa_RxC16X (0x5 << 0) +#define OPCR_MPOa_TxRDY (0x6 << 0) +#define OPCR_MPOa_RxRDY_FF (0x7 << 0) + +#define OPCR_MPOb_RTSN (0x0 << 4) +#define OPCR_MPOb_C_TO (0x1 << 4) +#define OPCR_MPOb_TxC1X (0x2 << 4) +#define OPCR_MPOb_TxC16X (0x3 << 4) +#define OPCR_MPOb_RxC1X (0x4 << 4) +#define OPCR_MPOb_RxC16X (0x5 << 4) +#define OPCR_MPOb_TxRDY (0x6 << 4) +#define OPCR_MPOb_RxRDY_FF (0x7 << 4) + +#define OPCR_MPP_INPUT (0x0 << 7) +#define OPCR_MPP_OUTPUT (0x1 << 7) + +#define IMR_TxRDY_A (0x1 << 0) +#define IMR_RxRDY_FFULL_A (0x1 << 1) +#define IMR_DELTA_BREAK_A (0x1 << 2) +#define IMR_COUNTER_READY (0x1 << 3) +#define IMR_TxRDY_B (0x1 << 4) +#define IMR_RxRDY_FFULL_B (0x1 << 5) +#define IMR_DELTA_BREAK_B (0x1 << 6) +#define IMR_INPUT_PORT_CHANGE (0x1 << 7) + +#define ISR_TxRDY_A (0x1 << 0) +#define ISR_RxRDY_FFULL_A (0x1 << 1) +#define ISR_DELTA_BREAK_A (0x1 << 2) +#define ISR_COUNTER_READY (0x1 << 3) +#define ISR_TxRDY_B (0x1 << 4) +#define ISR_RxRDY_FFULL_B (0x1 << 5) +#define ISR_DELTA_BREAK_B (0x1 << 6) +#define ISR_INPUT_PORT_CHANGE (0x1 << 7) + +#define ACK_INT_REQ0 0 +#define ACK_INT_REQ1 2 + +#endif /* SCC2698_H_ */ diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c new file mode 100644 index 000000000000..6d5079de52b9 --- /dev/null +++ b/drivers/ipack/ipack.c @@ -0,0 +1,481 @@ +/* + * Industry-pack bus support functions. + * + * Copyright (C) 2011-2012 CERN (www.cern.ch) + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include "ipack.h" + +#define to_ipack_dev(device) container_of(device, struct ipack_device, dev) +#define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver) + +static DEFINE_IDA(ipack_ida); + +static void ipack_device_release(struct device *dev) +{ + struct ipack_device *device = to_ipack_dev(dev); + kfree(device->id); + device->release(device); +} + +static inline const struct ipack_device_id * +ipack_match_one_device(const struct ipack_device_id *id, + const struct ipack_device *device) +{ + if ((id->format == IPACK_ANY_FORMAT || + id->format == device->id_format) && + (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) && + (id->device == IPACK_ANY_ID || id->device == device->id_device)) + return id; + return NULL; +} + +static const struct ipack_device_id * +ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev) +{ + if (ids) { + while (ids->vendor || ids->device) { + if (ipack_match_one_device(ids, idev)) + return ids; + ids++; + } + } + return NULL; +} + +static int ipack_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ipack_device *idev = to_ipack_dev(dev); + struct ipack_driver *idrv = to_ipack_driver(drv); + const struct ipack_device_id *found_id; + + found_id = ipack_match_id(idrv->id_table, idev); + return found_id ? 1 : 0; +} + +static int ipack_bus_probe(struct device *device) +{ + struct ipack_device *dev = to_ipack_dev(device); + struct ipack_driver *drv = to_ipack_driver(device->driver); + + if (!drv->ops->probe) + return -EINVAL; + + return drv->ops->probe(dev); +} + +static int ipack_bus_remove(struct device *device) +{ + struct ipack_device *dev = to_ipack_dev(device); + struct ipack_driver *drv = to_ipack_driver(device->driver); + + if (!drv->ops->remove) + return -EINVAL; + + drv->ops->remove(dev); + return 0; +} + +static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct ipack_device *idev; + + if (!dev) + return -ENODEV; + + idev = to_ipack_dev(dev); + + if (add_uevent_var(env, + "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format, + idev->id_vendor, idev->id_device)) + return -ENOMEM; + + return 0; +} + +#define ipack_device_attr(field, format_string) \ +static ssize_t \ +field##_show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct ipack_device *idev = to_ipack_dev(dev); \ + return sprintf(buf, format_string, idev->field); \ +} + +static ssize_t id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int i, c, l, s; + struct ipack_device *idev = to_ipack_dev(dev); + + + switch (idev->id_format) { + case IPACK_ID_VERSION_1: + l = 0x7; s = 1; break; + case IPACK_ID_VERSION_2: + l = 0xf; s = 2; break; + default: + return -EIO; + } + c = 0; + for (i = 0; i < idev->id_avail; i++) { + if (i > 0) { + if ((i & l) == 0) + buf[c++] = '\n'; + else if ((i & s) == 0) + buf[c++] = ' '; + } + sprintf(&buf[c], "%02x", idev->id[i]); + c += 2; + } + buf[c++] = '\n'; + return c; +} + +static ssize_t +id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ipack_device *idev = to_ipack_dev(dev); + switch (idev->id_format) { + case IPACK_ID_VERSION_1: + return sprintf(buf, "0x%02x\n", idev->id_vendor); + case IPACK_ID_VERSION_2: + return sprintf(buf, "0x%06x\n", idev->id_vendor); + default: + return -EIO; + } +} + +static ssize_t +id_device_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ipack_device *idev = to_ipack_dev(dev); + switch (idev->id_format) { + case IPACK_ID_VERSION_1: + return sprintf(buf, "0x%02x\n", idev->id_device); + case IPACK_ID_VERSION_2: + return sprintf(buf, "0x%04x\n", idev->id_device); + default: + return -EIO; + } +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ipack_device *idev = to_ipack_dev(dev); + + return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format, + idev->id_vendor, idev->id_device); +} + +ipack_device_attr(id_format, "0x%hhu\n"); + +static struct device_attribute ipack_dev_attrs[] = { + __ATTR_RO(id), + __ATTR_RO(id_device), + __ATTR_RO(id_format), + __ATTR_RO(id_vendor), + __ATTR_RO(modalias), +}; + +static struct bus_type ipack_bus_type = { + .name = "ipack", + .probe = ipack_bus_probe, + .match = ipack_bus_match, + .remove = ipack_bus_remove, + .dev_attrs = ipack_dev_attrs, + .uevent = ipack_uevent, +}; + +struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, + const struct ipack_bus_ops *ops) +{ + int bus_nr; + struct ipack_bus_device *bus; + + bus = kzalloc(sizeof(struct ipack_bus_device), GFP_KERNEL); + if (!bus) + return NULL; + + bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL); + if (bus_nr < 0) { + kfree(bus); + return NULL; + } + + bus->bus_nr = bus_nr; + bus->parent = parent; + bus->slots = slots; + bus->ops = ops; + return bus; +} +EXPORT_SYMBOL_GPL(ipack_bus_register); + +static int ipack_unregister_bus_member(struct device *dev, void *data) +{ + struct ipack_device *idev = to_ipack_dev(dev); + struct ipack_bus_device *bus = data; + + if (idev->bus == bus) + ipack_device_unregister(idev); + + return 1; +} + +int ipack_bus_unregister(struct ipack_bus_device *bus) +{ + bus_for_each_dev(&ipack_bus_type, NULL, bus, + ipack_unregister_bus_member); + ida_simple_remove(&ipack_ida, bus->bus_nr); + kfree(bus); + return 0; +} +EXPORT_SYMBOL_GPL(ipack_bus_unregister); + +int ipack_driver_register(struct ipack_driver *edrv, struct module *owner, + const char *name) +{ + edrv->driver.owner = owner; + edrv->driver.name = name; + edrv->driver.bus = &ipack_bus_type; + return driver_register(&edrv->driver); +} +EXPORT_SYMBOL_GPL(ipack_driver_register); + +void ipack_driver_unregister(struct ipack_driver *edrv) +{ + driver_unregister(&edrv->driver); +} +EXPORT_SYMBOL_GPL(ipack_driver_unregister); + +static u16 ipack_crc_byte(u16 crc, u8 c) +{ + int i; + + crc ^= c << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0); + return crc; +} + +/* + * The algorithm in lib/crc-ccitt.c does not seem to apply since it uses the + * opposite bit ordering. + */ +static u8 ipack_calc_crc1(struct ipack_device *dev) +{ + u8 c; + u16 crc; + unsigned int i; + + crc = 0xffff; + for (i = 0; i < dev->id_avail; i++) { + c = (i != 11) ? dev->id[i] : 0; + crc = ipack_crc_byte(crc, c); + } + crc = ~crc; + return crc & 0xff; +} + +static u16 ipack_calc_crc2(struct ipack_device *dev) +{ + u8 c; + u16 crc; + unsigned int i; + + crc = 0xffff; + for (i = 0; i < dev->id_avail; i++) { + c = ((i != 0x18) && (i != 0x19)) ? dev->id[i] : 0; + crc = ipack_crc_byte(crc, c); + } + crc = ~crc; + return crc; +} + +static void ipack_parse_id1(struct ipack_device *dev) +{ + u8 *id = dev->id; + u8 crc; + + dev->id_vendor = id[4]; + dev->id_device = id[5]; + dev->speed_8mhz = 1; + dev->speed_32mhz = (id[7] == 'H'); + crc = ipack_calc_crc1(dev); + dev->id_crc_correct = (crc == id[11]); + if (!dev->id_crc_correct) { + dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n", + id[11], crc); + } +} + +static void ipack_parse_id2(struct ipack_device *dev) +{ + __be16 *id = (__be16 *) dev->id; + u16 flags, crc; + + dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16) + + be16_to_cpu(id[4]); + dev->id_device = be16_to_cpu(id[5]); + flags = be16_to_cpu(id[10]); + dev->speed_8mhz = !!(flags & 2); + dev->speed_32mhz = !!(flags & 4); + crc = ipack_calc_crc2(dev); + dev->id_crc_correct = (crc == be16_to_cpu(id[12])); + if (!dev->id_crc_correct) { + dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n", + id[11], crc); + } +} + +static int ipack_device_read_id(struct ipack_device *dev) +{ + u8 __iomem *idmem; + int i; + int ret = 0; + + idmem = ioremap(dev->region[IPACK_ID_SPACE].start, + dev->region[IPACK_ID_SPACE].size); + if (!idmem) { + dev_err(&dev->dev, "error mapping memory\n"); + return -ENOMEM; + } + + /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH" + * we are dealing with a IndustryPack format 1 device. If we detect + * "VITA4 " (16 bit big endian formatted) we are dealing with a + * IndustryPack format 2 device */ + if ((ioread8(idmem + 1) == 'I') && + (ioread8(idmem + 3) == 'P') && + (ioread8(idmem + 5) == 'A') && + ((ioread8(idmem + 7) == 'C') || + (ioread8(idmem + 7) == 'H'))) { + dev->id_format = IPACK_ID_VERSION_1; + dev->id_avail = ioread8(idmem + 0x15); + if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) { + dev_warn(&dev->dev, "invalid id size"); + dev->id_avail = 0x0c; + } + } else if ((ioread8(idmem + 0) == 'I') && + (ioread8(idmem + 1) == 'V') && + (ioread8(idmem + 2) == 'A') && + (ioread8(idmem + 3) == 'T') && + (ioread8(idmem + 4) == ' ') && + (ioread8(idmem + 5) == '4')) { + dev->id_format = IPACK_ID_VERSION_2; + dev->id_avail = ioread16be(idmem + 0x16); + if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) { + dev_warn(&dev->dev, "invalid id size"); + dev->id_avail = 0x1a; + } + } else { + dev->id_format = IPACK_ID_VERSION_INVALID; + dev->id_avail = 0; + } + + if (!dev->id_avail) { + ret = -ENODEV; + goto out; + } + + /* Obtain the amount of memory required to store a copy of the complete + * ID ROM contents */ + dev->id = kmalloc(dev->id_avail, GFP_KERNEL); + if (!dev->id) { + dev_err(&dev->dev, "dev->id alloc failed.\n"); + ret = -ENOMEM; + goto out; + } + for (i = 0; i < dev->id_avail; i++) { + if (dev->id_format == IPACK_ID_VERSION_1) + dev->id[i] = ioread8(idmem + (i << 1) + 1); + else + dev->id[i] = ioread8(idmem + i); + } + + /* now we can finally work with the copy */ + switch (dev->id_format) { + case IPACK_ID_VERSION_1: + ipack_parse_id1(dev); + break; + case IPACK_ID_VERSION_2: + ipack_parse_id2(dev); + break; + } + +out: + iounmap(idmem); + + return ret; +} + +int ipack_device_register(struct ipack_device *dev) +{ + int ret; + + dev->dev.bus = &ipack_bus_type; + dev->dev.release = ipack_device_release; + dev->dev.parent = dev->bus->parent; + dev_set_name(&dev->dev, + "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); + + if (dev->bus->ops->set_clockrate(dev, 8)) + dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n"); + if (dev->bus->ops->reset_timeout(dev)) + dev_warn(&dev->dev, "failed to reset potential timeout."); + + ret = ipack_device_read_id(dev); + if (ret < 0) { + dev_err(&dev->dev, "error reading device id section.\n"); + return ret; + } + + /* if the device supports 32 MHz operation, use it. */ + if (dev->speed_32mhz) { + ret = dev->bus->ops->set_clockrate(dev, 32); + if (ret < 0) + dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n"); + } + + ret = device_register(&dev->dev); + if (ret < 0) + kfree(dev->id); + + return ret; +} +EXPORT_SYMBOL_GPL(ipack_device_register); + +void ipack_device_unregister(struct ipack_device *dev) +{ + device_unregister(&dev->dev); +} +EXPORT_SYMBOL_GPL(ipack_device_unregister); + +static int __init ipack_init(void) +{ + ida_init(&ipack_ida); + return bus_register(&ipack_bus_type); +} + +static void __exit ipack_exit(void) +{ + bus_unregister(&ipack_bus_type); + ida_destroy(&ipack_ida); +} + +module_init(ipack_init); +module_exit(ipack_exit); + +MODULE_AUTHOR("Samuel Iglesias Gonsalvez "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Industry-pack bus core"); diff --git a/drivers/ipack/ipack.h b/drivers/ipack/ipack.h new file mode 100644 index 000000000000..6760bfaf0ac4 --- /dev/null +++ b/drivers/ipack/ipack.h @@ -0,0 +1,215 @@ +/* + * Industry-pack bus. + * + * Copyright (C) 2011-2012 CERN (www.cern.ch) + * Author: Samuel Iglesias Gonsalvez + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include +#include +#include + +#include "ipack_ids.h" + +#define IPACK_IDPROM_OFFSET_I 0x01 +#define IPACK_IDPROM_OFFSET_P 0x03 +#define IPACK_IDPROM_OFFSET_A 0x05 +#define IPACK_IDPROM_OFFSET_C 0x07 +#define IPACK_IDPROM_OFFSET_MANUFACTURER_ID 0x09 +#define IPACK_IDPROM_OFFSET_MODEL 0x0B +#define IPACK_IDPROM_OFFSET_REVISION 0x0D +#define IPACK_IDPROM_OFFSET_RESERVED 0x0F +#define IPACK_IDPROM_OFFSET_DRIVER_ID_L 0x11 +#define IPACK_IDPROM_OFFSET_DRIVER_ID_H 0x13 +#define IPACK_IDPROM_OFFSET_NUM_BYTES 0x15 +#define IPACK_IDPROM_OFFSET_CRC 0x17 + +struct ipack_bus_ops; +struct ipack_driver; + +enum ipack_space { + IPACK_IO_SPACE = 0, + IPACK_ID_SPACE, + IPACK_INT_SPACE, + IPACK_MEM8_SPACE, + IPACK_MEM16_SPACE, + /* Dummy for counting the number of entries. Must remain the last + * entry */ + IPACK_SPACE_COUNT, +}; + +/** + */ +struct ipack_region { + phys_addr_t start; + size_t size; +}; + +/** + * struct ipack_device + * + * @slot: Slot where the device is plugged in the carrier board + * @bus: ipack_bus_device where the device is plugged to. + * @id_space: Virtual address to ID space. + * @io_space: Virtual address to IO space. + * @mem_space: Virtual address to MEM space. + * @dev: device in kernel representation. + * + * Warning: Direct access to mapped memory is possible but the endianness + * is not the same with PCI carrier or VME carrier. The endianness is managed + * by the carrier board throught bus->ops. + */ +struct ipack_device { + unsigned int slot; + struct ipack_bus_device *bus; + struct device dev; + void (*release) (struct ipack_device *dev); + struct ipack_region region[IPACK_SPACE_COUNT]; + u8 *id; + size_t id_avail; + u32 id_vendor; + u32 id_device; + u8 id_format; + unsigned int id_crc_correct:1; + unsigned int speed_8mhz:1; + unsigned int speed_32mhz:1; +}; + +/** + * struct ipack_driver_ops -- Callbacks to IPack device driver + * + * @probe: Probe function + * @remove: Prepare imminent removal of the device. Services provided by the + * device should be revoked. + */ + +struct ipack_driver_ops { + int (*probe) (struct ipack_device *dev); + void (*remove) (struct ipack_device *dev); +}; + +/** + * struct ipack_driver -- Specific data to each ipack device driver + * + * @driver: Device driver kernel representation + * @ops: Callbacks provided by the IPack device driver + */ +struct ipack_driver { + struct device_driver driver; + const struct ipack_device_id *id_table; + const struct ipack_driver_ops *ops; +}; + +/** + * struct ipack_bus_ops - available operations on a bridge module + * + * @map_space: map IP address space + * @unmap_space: unmap IP address space + * @request_irq: request IRQ + * @free_irq: free IRQ + * @get_clockrate: Returns the clockrate the carrier is currently + * communicating with the device at. + * @set_clockrate: Sets the clock-rate for carrier / module communication. + * Should return -EINVAL if the requested speed is not supported. + * @get_error: Returns the error state for the slot the device is attached + * to. + * @get_timeout: Returns 1 if the communication with the device has + * previously timed out. + * @reset_timeout: Resets the state returned by get_timeout. + */ +struct ipack_bus_ops { + int (*request_irq) (struct ipack_device *dev, + irqreturn_t (*handler)(void *), void *arg); + int (*free_irq) (struct ipack_device *dev); + int (*get_clockrate) (struct ipack_device *dev); + int (*set_clockrate) (struct ipack_device *dev, int mherz); + int (*get_error) (struct ipack_device *dev); + int (*get_timeout) (struct ipack_device *dev); + int (*reset_timeout) (struct ipack_device *dev); +}; + +/** + * struct ipack_bus_device + * + * @dev: pointer to carrier device + * @slots: number of slots available + * @bus_nr: ipack bus number + * @ops: bus operations for the mezzanine drivers + */ +struct ipack_bus_device { + struct device *parent; + int slots; + int bus_nr; + const struct ipack_bus_ops *ops; +}; + +/** + * ipack_bus_register -- register a new ipack bus + * + * @parent: pointer to the parent device, if any. + * @slots: number of slots available in the bus device. + * @ops: bus operations for the mezzanine drivers. + * + * The carrier board device should call this function to register itself as + * available bus device in ipack. + */ +struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, + const struct ipack_bus_ops *ops); + +/** + * ipack_bus_unregister -- unregister an ipack bus + */ +int ipack_bus_unregister(struct ipack_bus_device *bus); + +/** + * ipack_driver_register -- Register a new ipack device driver + * + * Called by a ipack driver to register itself as a driver + * that can manage ipack devices. + */ +int ipack_driver_register(struct ipack_driver *edrv, struct module *owner, + const char *name); +void ipack_driver_unregister(struct ipack_driver *edrv); + +/** + * ipack_device_register -- register an IPack device with the kernel + * @dev: the new device to register. + * + * Register a new IPack device ("module" in IndustryPack jargon). The call + * is done by the carrier driver. The carrier should populate the fields + * bus and slot as well as the region array of @dev prior to calling this + * function. The rest of the fields will be allocated and populated + * during registration. + * + * Return zero on success or error code on failure. + */ +int ipack_device_register(struct ipack_device *dev); +void ipack_device_unregister(struct ipack_device *dev); + +/** + * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table + * @_table: device table name + * + * This macro is used to create a struct ipack_device_id array (a device table) + * in a generic manner. + */ +#define DEFINE_IPACK_DEVICE_TABLE(_table) \ + const struct ipack_device_id _table[] __devinitconst + +/** + * IPACK_DEVICE - macro used to describe a specific IndustryPack device + * @_format: the format version (currently either 1 or 2, 8 bit value) + * @vend: the 8 or 24 bit IndustryPack Vendor ID + * @dev: the 8 or 16 bit IndustryPack Device ID + * + * This macro is used to create a struct ipack_device_id that matches a specific + * device. + */ +#define IPACK_DEVICE(_format, vend, dev) \ + .format = (_format), \ + .vendor = (vend), \ + .device = (dev) diff --git a/drivers/ipack/ipack_ids.h b/drivers/ipack/ipack_ids.h new file mode 100644 index 000000000000..8153fee3f2f7 --- /dev/null +++ b/drivers/ipack/ipack_ids.h @@ -0,0 +1,32 @@ +/* + * IndustryPack Fromat, Vendor and Device IDs. + */ + +/* ID section format versions */ +#define IPACK_ID_VERSION_INVALID 0x00 +#define IPACK_ID_VERSION_1 0x01 +#define IPACK_ID_VERSION_2 0x02 + +/* Vendors and devices. Sort key: vendor first, device next. */ +#define IPACK1_VENDOR_ID_RESERVED1 0x00 +#define IPACK1_VENDOR_ID_RESERVED2 0xFF +#define IPACK1_VENDOR_ID_UNREGISTRED01 0x01 +#define IPACK1_VENDOR_ID_UNREGISTRED02 0x02 +#define IPACK1_VENDOR_ID_UNREGISTRED03 0x03 +#define IPACK1_VENDOR_ID_UNREGISTRED04 0x04 +#define IPACK1_VENDOR_ID_UNREGISTRED05 0x05 +#define IPACK1_VENDOR_ID_UNREGISTRED06 0x06 +#define IPACK1_VENDOR_ID_UNREGISTRED07 0x07 +#define IPACK1_VENDOR_ID_UNREGISTRED08 0x08 +#define IPACK1_VENDOR_ID_UNREGISTRED09 0x09 +#define IPACK1_VENDOR_ID_UNREGISTRED10 0x0A +#define IPACK1_VENDOR_ID_UNREGISTRED11 0x0B +#define IPACK1_VENDOR_ID_UNREGISTRED12 0x0C +#define IPACK1_VENDOR_ID_UNREGISTRED13 0x0D +#define IPACK1_VENDOR_ID_UNREGISTRED14 0x0E +#define IPACK1_VENDOR_ID_UNREGISTRED15 0x0F + +#define IPACK1_VENDOR_ID_SBS 0xF0 +#define IPACK1_DEVICE_ID_SBS_OCTAL_232 0x22 +#define IPACK1_DEVICE_ID_SBS_OCTAL_422 0x2A +#define IPACK1_DEVICE_ID_SBS_OCTAL_485 0x48 diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 12a6f2e0aee5..943ca607200c 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -122,8 +122,6 @@ source "drivers/staging/ozwpan/Kconfig" source "drivers/staging/ccg/Kconfig" -source "drivers/staging/ipack/Kconfig" - source "drivers/staging/gdm72xx/Kconfig" source "drivers/staging/csr/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 6d16f822e27e..20c764d7ab33 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -28,7 +28,6 @@ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ obj-$(CONFIG_VME_BUS) += vme/ -obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_ZRAM) += zram/ diff --git a/drivers/staging/ipack/Kconfig b/drivers/staging/ipack/Kconfig deleted file mode 100644 index 5cf43b3364eb..000000000000 --- a/drivers/staging/ipack/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -# -# IPACK configuration. -# - -menuconfig IPACK_BUS - tristate "IndustryPack bus support" - depends on HAS_IOMEM - ---help--- - This option provides support for the IndustryPack framework. There - are IndustryPack carrier boards, which interface another bus (such as - PCI) to an IndustryPack bus, and IndustryPack modules, that are - hosted on these buses. While IndustryPack modules can provide a - large variety of functionality, they are most often found in - industrial control applications. - - Say N if unsure. - -if IPACK_BUS - -source "drivers/staging/ipack/carriers/Kconfig" - -source "drivers/staging/ipack/devices/Kconfig" - -endif # IPACK diff --git a/drivers/staging/ipack/Makefile b/drivers/staging/ipack/Makefile deleted file mode 100644 index 6f14ade0f8f3..000000000000 --- a/drivers/staging/ipack/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# -# Makefile for the IPACK bridge device drivers. -# -obj-$(CONFIG_IPACK_BUS) += ipack.o -obj-y += devices/ -obj-y += carriers/ diff --git a/drivers/staging/ipack/TODO b/drivers/staging/ipack/TODO deleted file mode 100644 index e667acf5d332..000000000000 --- a/drivers/staging/ipack/TODO +++ /dev/null @@ -1,16 +0,0 @@ - TODO - ==== -Introduction -============ - -These drivers add support for IndustryPack devices: carrier and IP module -boards. - -The ipack driver is just an abstraction of the bus providing the common -operations between the two kind of boards. - -Contact -======= - -Contact: Samuel Iglesias Gonsalvez -Mailing List: industrypack-devel@lists.sourceforge.net diff --git a/drivers/staging/ipack/carriers/Kconfig b/drivers/staging/ipack/carriers/Kconfig deleted file mode 100644 index 922ff5c35acc..000000000000 --- a/drivers/staging/ipack/carriers/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config BOARD_TPCI200 - tristate "Support for the TEWS TPCI-200 IndustryPack carrier board" - depends on IPACK_BUS - depends on PCI - help - This driver adds support for the TEWS TPCI200 IndustryPack carrier board. - default n diff --git a/drivers/staging/ipack/carriers/Makefile b/drivers/staging/ipack/carriers/Makefile deleted file mode 100644 index d8b76459300f..000000000000 --- a/drivers/staging/ipack/carriers/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_BOARD_TPCI200) += tpci200.o diff --git a/drivers/staging/ipack/carriers/tpci200.c b/drivers/staging/ipack/carriers/tpci200.c deleted file mode 100644 index c1a19b274c23..000000000000 --- a/drivers/staging/ipack/carriers/tpci200.c +++ /dev/null @@ -1,627 +0,0 @@ -/** - * tpci200.c - * - * driver for the TEWS TPCI-200 device - * - * Copyright (C) 2009-2012 CERN (www.cern.ch) - * Author: Nicolas Serafini, EIC2 SA - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#include -#include -#include "tpci200.h" - -static const u16 tpci200_status_timeout[] = { - TPCI200_A_TIMEOUT, - TPCI200_B_TIMEOUT, - TPCI200_C_TIMEOUT, - TPCI200_D_TIMEOUT, -}; - -static const u16 tpci200_status_error[] = { - TPCI200_A_ERROR, - TPCI200_B_ERROR, - TPCI200_C_ERROR, - TPCI200_D_ERROR, -}; - -static const size_t tpci200_space_size[IPACK_SPACE_COUNT] = { - [IPACK_IO_SPACE] = TPCI200_IO_SPACE_SIZE, - [IPACK_ID_SPACE] = TPCI200_ID_SPACE_SIZE, - [IPACK_INT_SPACE] = TPCI200_INT_SPACE_SIZE, - [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_SIZE, - [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE, -}; - -static const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = { - [IPACK_IO_SPACE] = TPCI200_IO_SPACE_INTERVAL, - [IPACK_ID_SPACE] = TPCI200_ID_SPACE_INTERVAL, - [IPACK_INT_SPACE] = TPCI200_INT_SPACE_INTERVAL, - [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_INTERVAL, - [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL, -}; - -static struct tpci200_board *check_slot(struct ipack_device *dev) -{ - struct tpci200_board *tpci200; - - if (dev == NULL) - return NULL; - - - tpci200 = dev_get_drvdata(dev->bus->parent); - - if (tpci200 == NULL) { - dev_info(&dev->dev, "carrier board not found\n"); - return NULL; - } - - if (dev->slot >= TPCI200_NB_SLOT) { - dev_info(&dev->dev, - "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n", - dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1); - return NULL; - } - - return tpci200; -} - -static void tpci200_clear_mask(struct tpci200_board *tpci200, - __le16 __iomem *addr, u16 mask) -{ - unsigned long flags; - spin_lock_irqsave(&tpci200->regs_lock, flags); - iowrite16(ioread16(addr) & (~mask), addr); - spin_unlock_irqrestore(&tpci200->regs_lock, flags); -} - -static void tpci200_set_mask(struct tpci200_board *tpci200, - __le16 __iomem *addr, u16 mask) -{ - unsigned long flags; - spin_lock_irqsave(&tpci200->regs_lock, flags); - iowrite16(ioread16(addr) | mask, addr); - spin_unlock_irqrestore(&tpci200->regs_lock, flags); -} - -static void tpci200_unregister(struct tpci200_board *tpci200) -{ - free_irq(tpci200->info->pdev->irq, (void *) tpci200); - - pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); - pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); - - pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); - pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); - pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); - pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); - pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); - - pci_disable_device(tpci200->info->pdev); - pci_dev_put(tpci200->info->pdev); -} - -static void tpci200_enable_irq(struct tpci200_board *tpci200, - int islot) -{ - tpci200_set_mask(tpci200, - &tpci200->info->interface_regs->control[islot], - TPCI200_INT0_EN | TPCI200_INT1_EN); -} - -static void tpci200_disable_irq(struct tpci200_board *tpci200, - int islot) -{ - tpci200_clear_mask(tpci200, - &tpci200->info->interface_regs->control[islot], - TPCI200_INT0_EN | TPCI200_INT1_EN); -} - -static irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq) -{ - irqreturn_t ret; - - if (!slot_irq) - return -ENODEV; - ret = slot_irq->handler(slot_irq->arg); - - return ret; -} - -static irqreturn_t tpci200_interrupt(int irq, void *dev_id) -{ - struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id; - struct slot_irq *slot_irq; - irqreturn_t ret; - u16 status_reg; - int i; - - /* Read status register */ - status_reg = ioread16(&tpci200->info->interface_regs->status); - - /* Did we cause the interrupt? */ - if (!(status_reg & TPCI200_SLOT_INT_MASK)) - return IRQ_NONE; - - /* callback to the IRQ handler for the corresponding slot */ - rcu_read_lock(); - for (i = 0; i < TPCI200_NB_SLOT; i++) { - if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i)))) - continue; - slot_irq = rcu_dereference(tpci200->slots[i].irq); - ret = tpci200_slot_irq(slot_irq); - if (ret == -ENODEV) { - dev_info(&tpci200->info->pdev->dev, - "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n", - tpci200->number, i); - tpci200_disable_irq(tpci200, i); - } - } - rcu_read_unlock(); - - return IRQ_HANDLED; -} - -static int tpci200_free_irq(struct ipack_device *dev) -{ - struct slot_irq *slot_irq; - struct tpci200_board *tpci200; - - tpci200 = check_slot(dev); - if (tpci200 == NULL) - return -EINVAL; - - if (mutex_lock_interruptible(&tpci200->mutex)) - return -ERESTARTSYS; - - if (tpci200->slots[dev->slot].irq == NULL) { - mutex_unlock(&tpci200->mutex); - return -EINVAL; - } - - tpci200_disable_irq(tpci200, dev->slot); - slot_irq = tpci200->slots[dev->slot].irq; - /* uninstall handler */ - RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL); - synchronize_rcu(); - kfree(slot_irq); - mutex_unlock(&tpci200->mutex); - return 0; -} - -static int tpci200_request_irq(struct ipack_device *dev, - irqreturn_t (*handler)(void *), void *arg) -{ - int res = 0; - struct slot_irq *slot_irq; - struct tpci200_board *tpci200; - - tpci200 = check_slot(dev); - if (tpci200 == NULL) - return -EINVAL; - - if (mutex_lock_interruptible(&tpci200->mutex)) - return -ERESTARTSYS; - - if (tpci200->slots[dev->slot].irq != NULL) { - dev_err(&dev->dev, - "Slot [%d:%d] IRQ already registered !\n", - dev->bus->bus_nr, - dev->slot); - res = -EINVAL; - goto out_unlock; - } - - slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL); - if (slot_irq == NULL) { - dev_err(&dev->dev, - "Slot [%d:%d] unable to allocate memory for IRQ !\n", - dev->bus->bus_nr, dev->slot); - res = -ENOMEM; - goto out_unlock; - } - - /* - * WARNING: Setup Interrupt Vector in the IndustryPack device - * before an IRQ request. - * Read the User Manual of your IndustryPack device to know - * where to write the vector in memory. - */ - slot_irq->handler = handler; - slot_irq->arg = arg; - slot_irq->holder = dev; - - rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq); - tpci200_enable_irq(tpci200, dev->slot); - -out_unlock: - mutex_unlock(&tpci200->mutex); - return res; -} - -static int tpci200_register(struct tpci200_board *tpci200) -{ - int i; - int res; - phys_addr_t ioidint_base; - unsigned short slot_ctrl; - - if (pci_enable_device(tpci200->info->pdev) < 0) - return -ENODEV; - - /* Request IP interface register (Bar 2) */ - res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR, - "Carrier IP interface registers"); - if (res) { - dev_err(&tpci200->info->pdev->dev, - "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !", - tpci200->info->pdev->bus->number, - tpci200->info->pdev->devfn); - goto out_disable_pci; - } - - /* Request IO ID INT space (Bar 3) */ - res = pci_request_region(tpci200->info->pdev, - TPCI200_IO_ID_INT_SPACES_BAR, - "Carrier IO ID INT space"); - if (res) { - dev_err(&tpci200->info->pdev->dev, - "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !", - tpci200->info->pdev->bus->number, - tpci200->info->pdev->devfn); - goto out_release_ip_space; - } - - /* Request MEM8 space (Bar 5) */ - res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR, - "Carrier MEM8 space"); - if (res) { - dev_err(&tpci200->info->pdev->dev, - "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!", - tpci200->info->pdev->bus->number, - tpci200->info->pdev->devfn); - goto out_release_ioid_int_space; - } - - /* Request MEM16 space (Bar 4) */ - res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR, - "Carrier MEM16 space"); - if (res) { - dev_err(&tpci200->info->pdev->dev, - "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!", - tpci200->info->pdev->bus->number, - tpci200->info->pdev->devfn); - goto out_release_mem8_space; - } - - /* Map internal tpci200 driver user space */ - tpci200->info->interface_regs = - ioremap_nocache(pci_resource_start(tpci200->info->pdev, - TPCI200_IP_INTERFACE_BAR), - TPCI200_IFACE_SIZE); - - /* Initialize lock that protects interface_regs */ - spin_lock_init(&tpci200->regs_lock); - - ioidint_base = pci_resource_start(tpci200->info->pdev, - TPCI200_IO_ID_INT_SPACES_BAR); - tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF; - tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF; - tpci200->mod_mem[IPACK_INT_SPACE] = - ioidint_base + TPCI200_INT_SPACE_OFF; - tpci200->mod_mem[IPACK_MEM8_SPACE] = - pci_resource_start(tpci200->info->pdev, - TPCI200_MEM8_SPACE_BAR); - tpci200->mod_mem[IPACK_MEM16_SPACE] = - pci_resource_start(tpci200->info->pdev, - TPCI200_MEM16_SPACE_BAR); - - /* Set the default parameters of the slot - * INT0 disabled, level sensitive - * INT1 disabled, level sensitive - * error interrupt disabled - * timeout interrupt disabled - * recover time disabled - * clock rate 8 MHz - */ - slot_ctrl = 0; - for (i = 0; i < TPCI200_NB_SLOT; i++) - writew(slot_ctrl, &tpci200->info->interface_regs->control[i]); - - res = request_irq(tpci200->info->pdev->irq, - tpci200_interrupt, IRQF_SHARED, - KBUILD_MODNAME, (void *) tpci200); - if (res) { - dev_err(&tpci200->info->pdev->dev, - "(bn 0x%X, sn 0x%X) unable to register IRQ !", - tpci200->info->pdev->bus->number, - tpci200->info->pdev->devfn); - goto out_release_ioid_int_space; - } - - return 0; - -out_release_mem8_space: - pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); -out_release_ioid_int_space: - pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); -out_release_ip_space: - pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); -out_disable_pci: - pci_disable_device(tpci200->info->pdev); - return res; -} - -static int tpci200_get_clockrate(struct ipack_device *dev) -{ - struct tpci200_board *tpci200 = check_slot(dev); - __le16 __iomem *addr; - - if (!tpci200) - return -ENODEV; - - addr = &tpci200->info->interface_regs->control[dev->slot]; - return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8; -} - -static int tpci200_set_clockrate(struct ipack_device *dev, int mherz) -{ - struct tpci200_board *tpci200 = check_slot(dev); - __le16 __iomem *addr; - - if (!tpci200) - return -ENODEV; - - addr = &tpci200->info->interface_regs->control[dev->slot]; - - switch (mherz) { - case 8: - tpci200_clear_mask(tpci200, addr, TPCI200_CLK32); - break; - case 32: - tpci200_set_mask(tpci200, addr, TPCI200_CLK32); - break; - default: - return -EINVAL; - } - return 0; -} - -static int tpci200_get_error(struct ipack_device *dev) -{ - struct tpci200_board *tpci200 = check_slot(dev); - __le16 __iomem *addr; - u16 mask; - - if (!tpci200) - return -ENODEV; - - addr = &tpci200->info->interface_regs->status; - mask = tpci200_status_error[dev->slot]; - return (ioread16(addr) & mask) ? 1 : 0; -} - -static int tpci200_get_timeout(struct ipack_device *dev) -{ - struct tpci200_board *tpci200 = check_slot(dev); - __le16 __iomem *addr; - u16 mask; - - if (!tpci200) - return -ENODEV; - - addr = &tpci200->info->interface_regs->status; - mask = tpci200_status_timeout[dev->slot]; - - return (ioread16(addr) & mask) ? 1 : 0; -} - -static int tpci200_reset_timeout(struct ipack_device *dev) -{ - struct tpci200_board *tpci200 = check_slot(dev); - __le16 __iomem *addr; - u16 mask; - - if (!tpci200) - return -ENODEV; - - addr = &tpci200->info->interface_regs->status; - mask = tpci200_status_timeout[dev->slot]; - - iowrite16(mask, addr); - return 0; -} - -static void tpci200_uninstall(struct tpci200_board *tpci200) -{ - tpci200_unregister(tpci200); - kfree(tpci200->slots); -} - -static const struct ipack_bus_ops tpci200_bus_ops = { - .request_irq = tpci200_request_irq, - .free_irq = tpci200_free_irq, - .get_clockrate = tpci200_get_clockrate, - .set_clockrate = tpci200_set_clockrate, - .get_error = tpci200_get_error, - .get_timeout = tpci200_get_timeout, - .reset_timeout = tpci200_reset_timeout, -}; - -static int tpci200_install(struct tpci200_board *tpci200) -{ - int res; - - tpci200->slots = kzalloc( - TPCI200_NB_SLOT * sizeof(struct tpci200_slot), GFP_KERNEL); - if (tpci200->slots == NULL) - return -ENOMEM; - - res = tpci200_register(tpci200); - if (res) { - kfree(tpci200->slots); - tpci200->slots = NULL; - return res; - } - - mutex_init(&tpci200->mutex); - return 0; -} - -static void tpci200_release_device(struct ipack_device *dev) -{ - kfree(dev); -} - -static int tpci200_create_device(struct tpci200_board *tpci200, int i) -{ - enum ipack_space space; - struct ipack_device *dev = - kzalloc(sizeof(struct ipack_device), GFP_KERNEL); - if (!dev) - return -ENOMEM; - dev->slot = i; - dev->bus = tpci200->info->ipack_bus; - dev->release = tpci200_release_device; - - for (space = 0; space < IPACK_SPACE_COUNT; space++) { - dev->region[space].start = - tpci200->mod_mem[space] - + tpci200_space_interval[space] * i; - dev->region[space].size = tpci200_space_size[space]; - } - return ipack_device_register(dev); -} - -static int tpci200_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - int ret, i; - struct tpci200_board *tpci200; - u32 reg32; - - tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL); - if (!tpci200) - return -ENOMEM; - - tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL); - if (!tpci200->info) { - ret = -ENOMEM; - goto out_err_info; - } - - pci_dev_get(pdev); - - /* Obtain a mapping of the carrier's PCI configuration registers */ - ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR, - KBUILD_MODNAME " Configuration Memory"); - if (ret) { - dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory"); - ret = -EBUSY; - goto out_err_pci_request; - } - tpci200->info->cfg_regs = ioremap_nocache( - pci_resource_start(pdev, TPCI200_CFG_MEM_BAR), - pci_resource_len(pdev, TPCI200_CFG_MEM_BAR)); - if (!tpci200->info->cfg_regs) { - dev_err(&pdev->dev, "Failed to map PCI Configuration Memory"); - ret = -EFAULT; - goto out_err_ioremap; - } - - /* Disable byte swapping for 16 bit IP module access. This will ensure - * that the Industrypack big endian byte order is preserved by the - * carrier. */ - reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC); - reg32 |= 1 << LAS_BIT_BIGENDIAN; - iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC); - - reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC); - reg32 |= 1 << LAS_BIT_BIGENDIAN; - iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC); - - /* Save struct pci_dev pointer */ - tpci200->info->pdev = pdev; - tpci200->info->id_table = (struct pci_device_id *)id; - - /* register the device and initialize it */ - ret = tpci200_install(tpci200); - if (ret) { - dev_err(&pdev->dev, "error during tpci200 install\n"); - ret = -ENODEV; - goto out_err_install; - } - - /* Register the carrier in the industry pack bus driver */ - tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, - TPCI200_NB_SLOT, - &tpci200_bus_ops); - if (!tpci200->info->ipack_bus) { - dev_err(&pdev->dev, - "error registering the carrier on ipack driver\n"); - ret = -EFAULT; - goto out_err_bus_register; - } - - /* save the bus number given by ipack to logging purpose */ - tpci200->number = tpci200->info->ipack_bus->bus_nr; - dev_set_drvdata(&pdev->dev, tpci200); - - for (i = 0; i < TPCI200_NB_SLOT; i++) - tpci200_create_device(tpci200, i); - return 0; - -out_err_bus_register: - tpci200_uninstall(tpci200); -out_err_install: - iounmap(tpci200->info->cfg_regs); -out_err_ioremap: - pci_release_region(pdev, TPCI200_CFG_MEM_BAR); -out_err_pci_request: - pci_dev_put(pdev); - kfree(tpci200->info); -out_err_info: - kfree(tpci200); - return ret; -} - -static void __tpci200_pci_remove(struct tpci200_board *tpci200) -{ - ipack_bus_unregister(tpci200->info->ipack_bus); - tpci200_uninstall(tpci200); - - kfree(tpci200->info); - kfree(tpci200); -} - -static void __devexit tpci200_pci_remove(struct pci_dev *dev) -{ - struct tpci200_board *tpci200 = pci_get_drvdata(dev); - - __tpci200_pci_remove(tpci200); -} - -static DEFINE_PCI_DEVICE_TABLE(tpci200_idtable) = { - { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, - TPCI200_SUBDEVICE_ID }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(pci, tpci200_idtable); - -static struct pci_driver tpci200_pci_drv = { - .name = "tpci200", - .id_table = tpci200_idtable, - .probe = tpci200_pci_probe, - .remove = __devexit_p(tpci200_pci_remove), -}; - -module_pci_driver(tpci200_pci_drv); - -MODULE_DESCRIPTION("TEWS TPCI-200 device driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/ipack/carriers/tpci200.h b/drivers/staging/ipack/carriers/tpci200.h deleted file mode 100644 index 8d9be277b34d..000000000000 --- a/drivers/staging/ipack/carriers/tpci200.h +++ /dev/null @@ -1,168 +0,0 @@ -/** - * tpci200.h - * - * driver for the carrier TEWS TPCI-200 - * - * Copyright (C) 2009-2012 CERN (www.cern.ch) - * Author: Nicolas Serafini, EIC2 SA - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#ifndef _TPCI200_H_ -#define _TPCI200_H_ - -#include -#include -#include -#include -#include - -#include "../ipack.h" - -#define TPCI200_NB_SLOT 0x4 -#define TPCI200_NB_BAR 0x6 - -#define TPCI200_VENDOR_ID 0x1498 -#define TPCI200_DEVICE_ID 0x30C8 -#define TPCI200_SUBVENDOR_ID 0x1498 -#define TPCI200_SUBDEVICE_ID 0x300A - -#define TPCI200_CFG_MEM_BAR 0 -#define TPCI200_IP_INTERFACE_BAR 2 -#define TPCI200_IO_ID_INT_SPACES_BAR 3 -#define TPCI200_MEM16_SPACE_BAR 4 -#define TPCI200_MEM8_SPACE_BAR 5 - -struct tpci200_regs { - __le16 revision; - /* writes to control should occur with the mutex held to protect - * read-modify-write operations */ - __le16 control[4]; - __le16 reset; - __le16 status; - u8 reserved[242]; -} __packed; - -#define TPCI200_IFACE_SIZE 0x100 - -#define TPCI200_IO_SPACE_OFF 0x0000 -#define TPCI200_IO_SPACE_INTERVAL 0x0100 -#define TPCI200_IO_SPACE_SIZE 0x0080 -#define TPCI200_ID_SPACE_OFF 0x0080 -#define TPCI200_ID_SPACE_INTERVAL 0x0100 -#define TPCI200_ID_SPACE_SIZE 0x0040 -#define TPCI200_INT_SPACE_OFF 0x00C0 -#define TPCI200_INT_SPACE_INTERVAL 0x0100 -#define TPCI200_INT_SPACE_SIZE 0x0040 -#define TPCI200_IOIDINT_SIZE 0x0400 - -#define TPCI200_MEM8_SPACE_INTERVAL 0x00400000 -#define TPCI200_MEM8_SPACE_SIZE 0x00400000 -#define TPCI200_MEM16_SPACE_INTERVAL 0x00800000 -#define TPCI200_MEM16_SPACE_SIZE 0x00800000 - -/* control field in tpci200_regs */ -#define TPCI200_INT0_EN 0x0040 -#define TPCI200_INT1_EN 0x0080 -#define TPCI200_INT0_EDGE 0x0010 -#define TPCI200_INT1_EDGE 0x0020 -#define TPCI200_ERR_INT_EN 0x0008 -#define TPCI200_TIME_INT_EN 0x0004 -#define TPCI200_RECOVER_EN 0x0002 -#define TPCI200_CLK32 0x0001 - -/* reset field in tpci200_regs */ -#define TPCI200_A_RESET 0x0001 -#define TPCI200_B_RESET 0x0002 -#define TPCI200_C_RESET 0x0004 -#define TPCI200_D_RESET 0x0008 - -/* status field in tpci200_regs */ -#define TPCI200_A_TIMEOUT 0x1000 -#define TPCI200_B_TIMEOUT 0x2000 -#define TPCI200_C_TIMEOUT 0x4000 -#define TPCI200_D_TIMEOUT 0x8000 - -#define TPCI200_A_ERROR 0x0100 -#define TPCI200_B_ERROR 0x0200 -#define TPCI200_C_ERROR 0x0400 -#define TPCI200_D_ERROR 0x0800 - -#define TPCI200_A_INT0 0x0001 -#define TPCI200_A_INT1 0x0002 -#define TPCI200_B_INT0 0x0004 -#define TPCI200_B_INT1 0x0008 -#define TPCI200_C_INT0 0x0010 -#define TPCI200_C_INT1 0x0020 -#define TPCI200_D_INT0 0x0040 -#define TPCI200_D_INT1 0x0080 - -#define TPCI200_SLOT_INT_MASK 0x00FF - -/* PCI Configuration registers. The PCI bridge is a PLX Technology PCI9030. */ -#define LAS1_DESC 0x2C -#define LAS2_DESC 0x30 - -/* Bits in the LAS?_DESC registers */ -#define LAS_BIT_BIGENDIAN 24 - -#define VME_IOID_SPACE "IOID" -#define VME_MEM_SPACE "MEM" - -/** - * struct slot_irq - slot IRQ definition. - * @vector Vector number - * @handler Handler called when IRQ arrives - * @arg Handler argument - * - */ -struct slot_irq { - struct ipack_device *holder; - int vector; - irqreturn_t (*handler)(void *); - void *arg; -}; - -/** - * struct tpci200_slot - data specific to the tpci200 slot. - * @slot_id Slot identification gived to external interface - * @irq Slot IRQ infos - * @io_phys IO physical base address register of the slot - * @id_phys ID physical base address register of the slot - * @int_phys INT physical base address register of the slot - * @mem_phys MEM physical base address register of the slot - * - */ -struct tpci200_slot { - struct slot_irq *irq; -}; - -/** - * struct tpci200_infos - informations specific of the TPCI200 tpci200. - * @pci_dev PCI device - * @interface_regs Pointer to IP interface space (Bar 2) - * @ioidint_space Pointer to IP ID, IO and INT space (Bar 3) - * @mem8_space Pointer to MEM space (Bar 4) - * - */ -struct tpci200_infos { - struct pci_dev *pdev; - struct pci_device_id *id_table; - struct tpci200_regs __iomem *interface_regs; - void __iomem *cfg_regs; - struct ipack_bus_device *ipack_bus; -}; -struct tpci200_board { - unsigned int number; - struct mutex mutex; - spinlock_t regs_lock; - struct tpci200_slot *slots; - struct tpci200_infos *info; - phys_addr_t mod_mem[IPACK_SPACE_COUNT]; -}; - -#endif /* _TPCI200_H_ */ diff --git a/drivers/staging/ipack/devices/Kconfig b/drivers/staging/ipack/devices/Kconfig deleted file mode 100644 index 0b82fdc198c0..000000000000 --- a/drivers/staging/ipack/devices/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config SERIAL_IPOCTAL - tristate "IndustryPack IP-OCTAL uart support" - depends on IPACK_BUS - help - This driver supports the IPOCTAL serial port device for the IndustryPack bus. - default n diff --git a/drivers/staging/ipack/devices/Makefile b/drivers/staging/ipack/devices/Makefile deleted file mode 100644 index 6de18bda4a9a..000000000000 --- a/drivers/staging/ipack/devices/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SERIAL_IPOCTAL) += ipoctal.o diff --git a/drivers/staging/ipack/devices/ipoctal.c b/drivers/staging/ipack/devices/ipoctal.c deleted file mode 100644 index 783f120338d1..000000000000 --- a/drivers/staging/ipack/devices/ipoctal.c +++ /dev/null @@ -1,751 +0,0 @@ -/** - * ipoctal.c - * - * driver for the GE IP-OCTAL boards - * - * Copyright (C) 2009-2012 CERN (www.cern.ch) - * Author: Nicolas Serafini, EIC2 SA - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../ipack.h" -#include "ipoctal.h" -#include "scc2698.h" - -#define IP_OCTAL_ID_SPACE_VECTOR 0x41 -#define IP_OCTAL_NB_BLOCKS 4 - -static const struct tty_operations ipoctal_fops; - -struct ipoctal_channel { - struct ipoctal_stats stats; - unsigned int nb_bytes; - wait_queue_head_t queue; - spinlock_t lock; - unsigned int pointer_read; - unsigned int pointer_write; - atomic_t open; - struct tty_port tty_port; - union scc2698_channel __iomem *regs; - union scc2698_block __iomem *block_regs; - unsigned int board_id; - unsigned char *board_write; - u8 isr_rx_rdy_mask; - u8 isr_tx_rdy_mask; -}; - -struct ipoctal { - struct ipack_device *dev; - unsigned int board_id; - struct ipoctal_channel channel[NR_CHANNELS]; - unsigned char write; - struct tty_driver *tty_drv; - u8 __iomem *mem8_space; - u8 __iomem *int_space; -}; - -static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct ipoctal_channel *channel; - - channel = dev_get_drvdata(tty->dev); - - iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); - return 0; -} - -static int ipoctal_open(struct tty_struct *tty, struct file *file) -{ - int res; - struct ipoctal_channel *channel; - - channel = dev_get_drvdata(tty->dev); - - if (atomic_read(&channel->open)) - return -EBUSY; - - tty->driver_data = channel; - - res = tty_port_open(&channel->tty_port, tty, file); - if (res) - return res; - - atomic_inc(&channel->open); - return 0; -} - -static void ipoctal_reset_stats(struct ipoctal_stats *stats) -{ - stats->tx = 0; - stats->rx = 0; - stats->rcv_break = 0; - stats->framing_err = 0; - stats->overrun_err = 0; - stats->parity_err = 0; -} - -static void ipoctal_free_channel(struct ipoctal_channel *channel) -{ - ipoctal_reset_stats(&channel->stats); - channel->pointer_read = 0; - channel->pointer_write = 0; - channel->nb_bytes = 0; -} - -static void ipoctal_close(struct tty_struct *tty, struct file *filp) -{ - struct ipoctal_channel *channel = tty->driver_data; - - tty_port_close(&channel->tty_port, tty, filp); - - if (atomic_dec_and_test(&channel->open)) - ipoctal_free_channel(channel); -} - -static int ipoctal_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct ipoctal_channel *channel = tty->driver_data; - - icount->cts = 0; - icount->dsr = 0; - icount->rng = 0; - icount->dcd = 0; - icount->rx = channel->stats.rx; - icount->tx = channel->stats.tx; - icount->frame = channel->stats.framing_err; - icount->parity = channel->stats.parity_err; - icount->brk = channel->stats.rcv_break; - return 0; -} - -static void ipoctal_irq_rx(struct ipoctal_channel *channel, - struct tty_struct *tty, u8 sr) -{ - unsigned char value; - unsigned char flag = TTY_NORMAL; - u8 isr; - - do { - value = ioread8(&channel->regs->r.rhr); - /* Error: count statistics */ - if (sr & SR_ERROR) { - iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); - - if (sr & SR_OVERRUN_ERROR) { - channel->stats.overrun_err++; - /* Overrun doesn't affect the current character*/ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - if (sr & SR_PARITY_ERROR) { - channel->stats.parity_err++; - flag = TTY_PARITY; - } - if (sr & SR_FRAMING_ERROR) { - channel->stats.framing_err++; - flag = TTY_FRAME; - } - if (sr & SR_RECEIVED_BREAK) { - iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr); - channel->stats.rcv_break++; - flag = TTY_BREAK; - } - } - tty_insert_flip_char(tty, value, flag); - - /* Check if there are more characters in RX FIFO - * If there are more, the isr register for this channel - * has enabled the RxRDY|FFULL bit. - */ - isr = ioread8(&channel->block_regs->r.isr); - sr = ioread8(&channel->regs->r.sr); - } while (isr & channel->isr_rx_rdy_mask); - - tty_flip_buffer_push(tty); -} - -static void ipoctal_irq_tx(struct ipoctal_channel *channel) -{ - unsigned char value; - unsigned int *pointer_write = &channel->pointer_write; - - if (channel->nb_bytes <= 0) { - channel->nb_bytes = 0; - return; - } - - value = channel->tty_port.xmit_buf[*pointer_write]; - iowrite8(value, &channel->regs->w.thr); - channel->stats.tx++; - (*pointer_write)++; - *pointer_write = *pointer_write % PAGE_SIZE; - channel->nb_bytes--; - - if ((channel->nb_bytes == 0) && - (waitqueue_active(&channel->queue))) { - - if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) { - *channel->board_write = 1; - wake_up_interruptible(&channel->queue); - } - } -} - -static void ipoctal_irq_channel(struct ipoctal_channel *channel) -{ - u8 isr, sr; - struct tty_struct *tty; - - /* If there is no client, skip the check */ - if (!atomic_read(&channel->open)) - return; - - tty = tty_port_tty_get(&channel->tty_port); - if (!tty) - return; - /* The HW is organized in pair of channels. See which register we need - * to read from */ - isr = ioread8(&channel->block_regs->r.isr); - sr = ioread8(&channel->regs->r.sr); - - /* In case of RS-485, change from TX to RX when finishing TX. - * Half-duplex. */ - if ((channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) && - (sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) { - iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr); - iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); - *channel->board_write = 1; - wake_up_interruptible(&channel->queue); - } - - /* RX data */ - if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY)) - ipoctal_irq_rx(channel, tty, sr); - - /* TX of each character */ - if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) - ipoctal_irq_tx(channel); - - tty_flip_buffer_push(tty); - tty_kref_put(tty); -} - -static irqreturn_t ipoctal_irq_handler(void *arg) -{ - unsigned int i; - struct ipoctal *ipoctal = (struct ipoctal *) arg; - - /* Check all channels */ - for (i = 0; i < NR_CHANNELS; i++) - ipoctal_irq_channel(&ipoctal->channel[i]); - - /* Clear the IPack device interrupt */ - readw(ipoctal->int_space + ACK_INT_REQ0); - readw(ipoctal->int_space + ACK_INT_REQ1); - - return IRQ_HANDLED; -} - -static const struct tty_port_operations ipoctal_tty_port_ops = { - .dtr_rts = NULL, - .activate = ipoctal_port_activate, -}; - -static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, - unsigned int slot) -{ - int res; - int i; - struct tty_driver *tty; - char name[20]; - struct ipoctal_channel *channel; - struct ipack_region *region; - void __iomem *addr; - union scc2698_channel __iomem *chan_regs; - union scc2698_block __iomem *block_regs; - - ipoctal->board_id = ipoctal->dev->id_device; - - region = &ipoctal->dev->region[IPACK_IO_SPACE]; - addr = devm_ioremap_nocache(&ipoctal->dev->dev, - region->start, region->size); - if (!addr) { - dev_err(&ipoctal->dev->dev, - "Unable to map slot [%d:%d] IO space!\n", - bus_nr, slot); - return -EADDRNOTAVAIL; - } - /* Save the virtual address to access the registers easily */ - chan_regs = - (union scc2698_channel __iomem *) addr; - block_regs = - (union scc2698_block __iomem *) addr; - - region = &ipoctal->dev->region[IPACK_INT_SPACE]; - ipoctal->int_space = - devm_ioremap_nocache(&ipoctal->dev->dev, - region->start, region->size); - if (!ipoctal->int_space) { - dev_err(&ipoctal->dev->dev, - "Unable to map slot [%d:%d] INT space!\n", - bus_nr, slot); - return -EADDRNOTAVAIL; - } - - region = &ipoctal->dev->region[IPACK_MEM8_SPACE]; - ipoctal->mem8_space = - devm_ioremap_nocache(&ipoctal->dev->dev, - region->start, 0x8000); - if (!addr) { - dev_err(&ipoctal->dev->dev, - "Unable to map slot [%d:%d] MEM8 space!\n", - bus_nr, slot); - return -EADDRNOTAVAIL; - } - - - /* Disable RX and TX before touching anything */ - for (i = 0; i < NR_CHANNELS ; i++) { - struct ipoctal_channel *channel = &ipoctal->channel[i]; - channel->regs = chan_regs + i; - channel->block_regs = block_regs + (i >> 1); - channel->board_write = &ipoctal->write; - channel->board_id = ipoctal->board_id; - if (i & 1) { - channel->isr_tx_rdy_mask = ISR_TxRDY_B; - channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B; - } else { - channel->isr_tx_rdy_mask = ISR_TxRDY_A; - channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A; - } - - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); - iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY, - &channel->regs->w.mr); /* mr1 */ - iowrite8(0, &channel->regs->w.mr); /* mr2 */ - iowrite8(TX_CLK_9600 | RX_CLK_9600, &channel->regs->w.csr); - } - - for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) { - iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr); - iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN, - &block_regs[i].w.opcr); - iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A | - IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B, - &block_regs[i].w.imr); - } - - /* - * IP-OCTAL has different addresses to copy its IRQ vector. - * Depending of the carrier these addresses are accesible or not. - * More info in the datasheet. - */ - ipoctal->dev->bus->ops->request_irq(ipoctal->dev, - ipoctal_irq_handler, ipoctal); - /* Dummy write */ - iowrite8(1, ipoctal->mem8_space + 1); - - /* Register the TTY device */ - - /* Each IP-OCTAL channel is a TTY port */ - tty = alloc_tty_driver(NR_CHANNELS); - - if (!tty) - return -ENOMEM; - - /* Fill struct tty_driver with ipoctal data */ - tty->owner = THIS_MODULE; - tty->driver_name = KBUILD_MODNAME; - sprintf(name, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); - tty->name = name; - tty->major = 0; - - tty->minor_start = 0; - tty->type = TTY_DRIVER_TYPE_SERIAL; - tty->subtype = SERIAL_TYPE_NORMAL; - tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty->init_termios = tty_std_termios; - tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->init_termios.c_ispeed = 9600; - tty->init_termios.c_ospeed = 9600; - - tty_set_operations(tty, &ipoctal_fops); - res = tty_register_driver(tty); - if (res) { - dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); - put_tty_driver(tty); - return res; - } - - /* Save struct tty_driver for use it when uninstalling the device */ - ipoctal->tty_drv = tty; - - for (i = 0; i < NR_CHANNELS; i++) { - struct device *tty_dev; - - channel = &ipoctal->channel[i]; - tty_port_init(&channel->tty_port); - tty_port_alloc_xmit_buf(&channel->tty_port); - channel->tty_port.ops = &ipoctal_tty_port_ops; - - ipoctal_reset_stats(&channel->stats); - channel->nb_bytes = 0; - init_waitqueue_head(&channel->queue); - - spin_lock_init(&channel->lock); - channel->pointer_read = 0; - channel->pointer_write = 0; - tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); - if (IS_ERR(tty_dev)) { - dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); - continue; - } - dev_set_drvdata(tty_dev, channel); - - /* - * Enable again the RX. TX will be enabled when - * there is something to send - */ - iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); - } - - return 0; -} - -static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, - const unsigned char *buf, - int count) -{ - unsigned long flags; - int i; - unsigned int *pointer_read = &channel->pointer_read; - - /* Copy the bytes from the user buffer to the internal one */ - for (i = 0; i < count; i++) { - if (i <= (PAGE_SIZE - channel->nb_bytes)) { - spin_lock_irqsave(&channel->lock, flags); - channel->tty_port.xmit_buf[*pointer_read] = buf[i]; - *pointer_read = (*pointer_read + 1) % PAGE_SIZE; - channel->nb_bytes++; - spin_unlock_irqrestore(&channel->lock, flags); - } else { - break; - } - } - return i; -} - -static int ipoctal_write_tty(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct ipoctal_channel *channel = tty->driver_data; - unsigned int char_copied; - - char_copied = ipoctal_copy_write_buffer(channel, buf, count); - - /* As the IP-OCTAL 485 only supports half duplex, do it manually */ - if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) { - iowrite8(CR_DISABLE_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr); - } - - /* - * Send a packet and then disable TX to avoid failure after several send - * operations - */ - iowrite8(CR_ENABLE_TX, &channel->regs->w.cr); - wait_event_interruptible(channel->queue, *channel->board_write); - iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); - - *channel->board_write = 0; - return char_copied; -} - -static int ipoctal_write_room(struct tty_struct *tty) -{ - struct ipoctal_channel *channel = tty->driver_data; - - return PAGE_SIZE - channel->nb_bytes; -} - -static int ipoctal_chars_in_buffer(struct tty_struct *tty) -{ - struct ipoctal_channel *channel = tty->driver_data; - - return channel->nb_bytes; -} - -static void ipoctal_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - unsigned int cflag; - unsigned char mr1 = 0; - unsigned char mr2 = 0; - unsigned char csr = 0; - struct ipoctal_channel *channel = tty->driver_data; - speed_t baud; - - cflag = tty->termios.c_cflag; - - /* Disable and reset everything before change the setup */ - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); - - /* Set Bits per chars */ - switch (cflag & CSIZE) { - case CS6: - mr1 |= MR1_CHRL_6_BITS; - break; - case CS7: - mr1 |= MR1_CHRL_7_BITS; - break; - case CS8: - default: - mr1 |= MR1_CHRL_8_BITS; - /* By default, select CS8 */ - tty->termios.c_cflag = (cflag & ~CSIZE) | CS8; - break; - } - - /* Set Parity */ - if (cflag & PARENB) - if (cflag & PARODD) - mr1 |= MR1_PARITY_ON | MR1_PARITY_ODD; - else - mr1 |= MR1_PARITY_ON | MR1_PARITY_EVEN; - else - mr1 |= MR1_PARITY_OFF; - - /* Mark or space parity is not supported */ - tty->termios.c_cflag &= ~CMSPAR; - - /* Set stop bits */ - if (cflag & CSTOPB) - mr2 |= MR2_STOP_BITS_LENGTH_2; - else - mr2 |= MR2_STOP_BITS_LENGTH_1; - - /* Set the flow control */ - switch (channel->board_id) { - case IPACK1_DEVICE_ID_SBS_OCTAL_232: - if (cflag & CRTSCTS) { - mr1 |= MR1_RxRTS_CONTROL_ON; - mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON; - } else { - mr1 |= MR1_RxRTS_CONTROL_OFF; - mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; - } - break; - case IPACK1_DEVICE_ID_SBS_OCTAL_422: - mr1 |= MR1_RxRTS_CONTROL_OFF; - mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; - break; - case IPACK1_DEVICE_ID_SBS_OCTAL_485: - mr1 |= MR1_RxRTS_CONTROL_OFF; - mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF; - break; - default: - return; - break; - } - - baud = tty_get_baud_rate(tty); - tty_termios_encode_baud_rate(&tty->termios, baud, baud); - - /* Set baud rate */ - switch (baud) { - case 75: - csr |= TX_CLK_75 | RX_CLK_75; - break; - case 110: - csr |= TX_CLK_110 | RX_CLK_110; - break; - case 150: - csr |= TX_CLK_150 | RX_CLK_150; - break; - case 300: - csr |= TX_CLK_300 | RX_CLK_300; - break; - case 600: - csr |= TX_CLK_600 | RX_CLK_600; - break; - case 1200: - csr |= TX_CLK_1200 | RX_CLK_1200; - break; - case 1800: - csr |= TX_CLK_1800 | RX_CLK_1800; - break; - case 2000: - csr |= TX_CLK_2000 | RX_CLK_2000; - break; - case 2400: - csr |= TX_CLK_2400 | RX_CLK_2400; - break; - case 4800: - csr |= TX_CLK_4800 | RX_CLK_4800; - break; - case 9600: - csr |= TX_CLK_9600 | RX_CLK_9600; - break; - case 19200: - csr |= TX_CLK_19200 | RX_CLK_19200; - break; - case 38400: - default: - csr |= TX_CLK_38400 | RX_CLK_38400; - /* In case of default, we establish 38400 bps */ - tty_termios_encode_baud_rate(&tty->termios, 38400, 38400); - break; - } - - mr1 |= MR1_ERROR_CHAR; - mr1 |= MR1_RxINT_RxRDY; - - /* Write the control registers */ - iowrite8(mr1, &channel->regs->w.mr); - iowrite8(mr2, &channel->regs->w.mr); - iowrite8(csr, &channel->regs->w.csr); - - /* Enable again the RX */ - iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); -} - -static void ipoctal_hangup(struct tty_struct *tty) -{ - unsigned long flags; - struct ipoctal_channel *channel = tty->driver_data; - - if (channel == NULL) - return; - - spin_lock_irqsave(&channel->lock, flags); - channel->nb_bytes = 0; - channel->pointer_read = 0; - channel->pointer_write = 0; - spin_unlock_irqrestore(&channel->lock, flags); - - tty_port_hangup(&channel->tty_port); - - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); - - clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags); - wake_up_interruptible(&channel->tty_port.open_wait); -} - -static const struct tty_operations ipoctal_fops = { - .ioctl = NULL, - .open = ipoctal_open, - .close = ipoctal_close, - .write = ipoctal_write_tty, - .set_termios = ipoctal_set_termios, - .write_room = ipoctal_write_room, - .chars_in_buffer = ipoctal_chars_in_buffer, - .get_icount = ipoctal_get_icount, - .hangup = ipoctal_hangup, -}; - -static int ipoctal_probe(struct ipack_device *dev) -{ - int res; - struct ipoctal *ipoctal; - - ipoctal = kzalloc(sizeof(struct ipoctal), GFP_KERNEL); - if (ipoctal == NULL) - return -ENOMEM; - - ipoctal->dev = dev; - res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); - if (res) - goto out_uninst; - - dev_set_drvdata(&dev->dev, ipoctal); - return 0; - -out_uninst: - kfree(ipoctal); - return res; -} - -static void __ipoctal_remove(struct ipoctal *ipoctal) -{ - int i; - - ipoctal->dev->bus->ops->free_irq(ipoctal->dev); - - for (i = 0; i < NR_CHANNELS; i++) { - struct ipoctal_channel *channel = &ipoctal->channel[i]; - tty_unregister_device(ipoctal->tty_drv, i); - tty_port_free_xmit_buf(&channel->tty_port); - } - - tty_unregister_driver(ipoctal->tty_drv); - put_tty_driver(ipoctal->tty_drv); - kfree(ipoctal); -} - -static void ipoctal_remove(struct ipack_device *idev) -{ - __ipoctal_remove(dev_get_drvdata(&idev->dev)); -} - -static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = { - { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, - IPACK1_DEVICE_ID_SBS_OCTAL_232) }, - { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, - IPACK1_DEVICE_ID_SBS_OCTAL_422) }, - { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, - IPACK1_DEVICE_ID_SBS_OCTAL_485) }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(ipack, ipoctal_ids); - -static const struct ipack_driver_ops ipoctal_drv_ops = { - .probe = ipoctal_probe, - .remove = ipoctal_remove, -}; - -static struct ipack_driver driver = { - .ops = &ipoctal_drv_ops, - .id_table = ipoctal_ids, -}; - -static int __init ipoctal_init(void) -{ - return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME); -} - -static void __exit ipoctal_exit(void) -{ - ipack_driver_unregister(&driver); -} - -MODULE_DESCRIPTION("IP-Octal 232, 422 and 485 device driver"); -MODULE_LICENSE("GPL"); - -module_init(ipoctal_init); -module_exit(ipoctal_exit); diff --git a/drivers/staging/ipack/devices/ipoctal.h b/drivers/staging/ipack/devices/ipoctal.h deleted file mode 100644 index 28f1c4233154..000000000000 --- a/drivers/staging/ipack/devices/ipoctal.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * ipoctal.h - * - * driver for the IPOCTAL boards - - * Copyright (C) 2009-2012 CERN (www.cern.ch) - * Author: Nicolas Serafini, EIC2 SA - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#ifndef _IPOCTAL_H -#define _IPOCTAL_H_ - -#define NR_CHANNELS 8 -#define IPOCTAL_MAX_BOARDS 16 -#define MAX_DEVICES (NR_CHANNELS * IPOCTAL_MAX_BOARDS) -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - -/** - * struct ipoctal_stats -- Stats since last reset - * - * @tx: Number of transmitted bytes - * @rx: Number of received bytes - * @overrun: Number of overrun errors - * @parity_err: Number of parity errors - * @framing_err: Number of framing errors - * @rcv_break: Number of break received - */ -struct ipoctal_stats { - unsigned long tx; - unsigned long rx; - unsigned long overrun_err; - unsigned long parity_err; - unsigned long framing_err; - unsigned long rcv_break; -}; - -#endif /* _IPOCTAL_H_ */ diff --git a/drivers/staging/ipack/devices/scc2698.h b/drivers/staging/ipack/devices/scc2698.h deleted file mode 100644 index 2ad6acd513fa..000000000000 --- a/drivers/staging/ipack/devices/scc2698.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * scc2698.h - * - * driver for the IPOCTAL boards - * - * Copyright (C) 2009-2012 CERN (www.cern.ch) - * Author: Nicolas Serafini, EIC2 SA - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#ifndef SCC2698_H_ -#define SCC2698_H_ - -/* - * union scc2698_channel - Channel access to scc2698 IO - * - * dn value are only spacer. - * - */ -union scc2698_channel { - struct { - u8 d0, mr; /* Mode register 1/2*/ - u8 d1, sr; /* Status register */ - u8 d2, r1; /* reserved */ - u8 d3, rhr; /* Receive holding register (R) */ - u8 junk[8]; /* other crap for block control */ - } __packed r; /* Read access */ - struct { - u8 d0, mr; /* Mode register 1/2 */ - u8 d1, csr; /* Clock select register */ - u8 d2, cr; /* Command register */ - u8 d3, thr; /* Transmit holding register */ - u8 junk[8]; /* other crap for block control */ - } __packed w; /* Write access */ -}; - -/* - * union scc2698_block - Block access to scc2698 IO - * - * The scc2698 contain 4 block. - * Each block containt two channel a and b. - * dn value are only spacer. - * - */ -union scc2698_block { - struct { - u8 d0, mra; /* Mode register 1/2 (a) */ - u8 d1, sra; /* Status register (a) */ - u8 d2, r1; /* reserved */ - u8 d3, rhra; /* Receive holding register (a) */ - u8 d4, ipcr; /* Input port change register of block */ - u8 d5, isr; /* Interrupt status register of block */ - u8 d6, ctur; /* Counter timer upper register of block */ - u8 d7, ctlr; /* Counter timer lower register of block */ - u8 d8, mrb; /* Mode register 1/2 (b) */ - u8 d9, srb; /* Status register (b) */ - u8 da, r2; /* reserved */ - u8 db, rhrb; /* Receive holding register (b) */ - u8 dc, r3; /* reserved */ - u8 dd, ip; /* Input port register of block */ - u8 de, ctg; /* Start counter timer of block */ - u8 df, cts; /* Stop counter timer of block */ - } __packed r; /* Read access */ - struct { - u8 d0, mra; /* Mode register 1/2 (a) */ - u8 d1, csra; /* Clock select register (a) */ - u8 d2, cra; /* Command register (a) */ - u8 d3, thra; /* Transmit holding register (a) */ - u8 d4, acr; /* Auxiliary control register of block */ - u8 d5, imr; /* Interrupt mask register of block */ - u8 d6, ctu; /* Counter timer upper register of block */ - u8 d7, ctl; /* Counter timer lower register of block */ - u8 d8, mrb; /* Mode register 1/2 (b) */ - u8 d9, csrb; /* Clock select register (a) */ - u8 da, crb; /* Command register (b) */ - u8 db, thrb; /* Transmit holding register (b) */ - u8 dc, r1; /* reserved */ - u8 dd, opcr; /* Output port configuration register of block */ - u8 de, r2; /* reserved */ - u8 df, r3; /* reserved */ - } __packed w; /* Write access */ -}; - -#define MR1_CHRL_5_BITS (0x0 << 0) -#define MR1_CHRL_6_BITS (0x1 << 0) -#define MR1_CHRL_7_BITS (0x2 << 0) -#define MR1_CHRL_8_BITS (0x3 << 0) -#define MR1_PARITY_EVEN (0x1 << 2) -#define MR1_PARITY_ODD (0x0 << 2) -#define MR1_PARITY_ON (0x0 << 3) -#define MR1_PARITY_FORCE (0x1 << 3) -#define MR1_PARITY_OFF (0x2 << 3) -#define MR1_PARITY_SPECIAL (0x3 << 3) -#define MR1_ERROR_CHAR (0x0 << 5) -#define MR1_ERROR_BLOCK (0x1 << 5) -#define MR1_RxINT_RxRDY (0x0 << 6) -#define MR1_RxINT_FFULL (0x1 << 6) -#define MR1_RxRTS_CONTROL_ON (0x1 << 7) -#define MR1_RxRTS_CONTROL_OFF (0x0 << 7) - -#define MR2_STOP_BITS_LENGTH_1 (0x7 << 0) -#define MR2_STOP_BITS_LENGTH_2 (0xF << 0) -#define MR2_CTS_ENABLE_TX_ON (0x1 << 4) -#define MR2_CTS_ENABLE_TX_OFF (0x0 << 4) -#define MR2_TxRTS_CONTROL_ON (0x1 << 5) -#define MR2_TxRTS_CONTROL_OFF (0x0 << 5) -#define MR2_CH_MODE_NORMAL (0x0 << 6) -#define MR2_CH_MODE_ECHO (0x1 << 6) -#define MR2_CH_MODE_LOCAL (0x2 << 6) -#define MR2_CH_MODE_REMOTE (0x3 << 6) - -#define CR_ENABLE_RX (0x1 << 0) -#define CR_DISABLE_RX (0x1 << 1) -#define CR_ENABLE_TX (0x1 << 2) -#define CR_DISABLE_TX (0x1 << 3) -#define CR_CMD_RESET_MR (0x1 << 4) -#define CR_CMD_RESET_RX (0x2 << 4) -#define CR_CMD_RESET_TX (0x3 << 4) -#define CR_CMD_RESET_ERR_STATUS (0x4 << 4) -#define CR_CMD_RESET_BREAK_CHANGE (0x5 << 4) -#define CR_CMD_START_BREAK (0x6 << 4) -#define CR_CMD_STOP_BREAK (0x7 << 4) -#define CR_CMD_ASSERT_RTSN (0x8 << 4) -#define CR_CMD_NEGATE_RTSN (0x9 << 4) -#define CR_CMD_SET_TIMEOUT_MODE (0xA << 4) -#define CR_CMD_DISABLE_TIMEOUT_MODE (0xC << 4) - -#define SR_RX_READY (0x1 << 0) -#define SR_FIFO_FULL (0x1 << 1) -#define SR_TX_READY (0x1 << 2) -#define SR_TX_EMPTY (0x1 << 3) -#define SR_OVERRUN_ERROR (0x1 << 4) -#define SR_PARITY_ERROR (0x1 << 5) -#define SR_FRAMING_ERROR (0x1 << 6) -#define SR_RECEIVED_BREAK (0x1 << 7) - -#define SR_ERROR (0xF0) - -#define ACR_DELTA_IP0_IRQ_EN (0x1 << 0) -#define ACR_DELTA_IP1_IRQ_EN (0x1 << 1) -#define ACR_DELTA_IP2_IRQ_EN (0x1 << 2) -#define ACR_DELTA_IP3_IRQ_EN (0x1 << 3) -#define ACR_CT_Mask (0x7 << 4) -#define ACR_CExt (0x0 << 4) -#define ACR_CTxCA (0x1 << 4) -#define ACR_CTxCB (0x2 << 4) -#define ACR_CClk16 (0x3 << 4) -#define ACR_TExt (0x4 << 4) -#define ACR_TExt16 (0x5 << 4) -#define ACR_TClk (0x6 << 4) -#define ACR_TClk16 (0x7 << 4) -#define ACR_BRG_SET1 (0x0 << 7) -#define ACR_BRG_SET2 (0x1 << 7) - -#define TX_CLK_75 (0x0 << 0) -#define TX_CLK_110 (0x1 << 0) -#define TX_CLK_38400 (0x2 << 0) -#define TX_CLK_150 (0x3 << 0) -#define TX_CLK_300 (0x4 << 0) -#define TX_CLK_600 (0x5 << 0) -#define TX_CLK_1200 (0x6 << 0) -#define TX_CLK_2000 (0x7 << 0) -#define TX_CLK_2400 (0x8 << 0) -#define TX_CLK_4800 (0x9 << 0) -#define TX_CLK_1800 (0xA << 0) -#define TX_CLK_9600 (0xB << 0) -#define TX_CLK_19200 (0xC << 0) -#define RX_CLK_75 (0x0 << 4) -#define RX_CLK_110 (0x1 << 4) -#define RX_CLK_38400 (0x2 << 4) -#define RX_CLK_150 (0x3 << 4) -#define RX_CLK_300 (0x4 << 4) -#define RX_CLK_600 (0x5 << 4) -#define RX_CLK_1200 (0x6 << 4) -#define RX_CLK_2000 (0x7 << 4) -#define RX_CLK_2400 (0x8 << 4) -#define RX_CLK_4800 (0x9 << 4) -#define RX_CLK_1800 (0xA << 4) -#define RX_CLK_9600 (0xB << 4) -#define RX_CLK_19200 (0xC << 4) - -#define OPCR_MPOa_RTSN (0x0 << 0) -#define OPCR_MPOa_C_TO (0x1 << 0) -#define OPCR_MPOa_TxC1X (0x2 << 0) -#define OPCR_MPOa_TxC16X (0x3 << 0) -#define OPCR_MPOa_RxC1X (0x4 << 0) -#define OPCR_MPOa_RxC16X (0x5 << 0) -#define OPCR_MPOa_TxRDY (0x6 << 0) -#define OPCR_MPOa_RxRDY_FF (0x7 << 0) - -#define OPCR_MPOb_RTSN (0x0 << 4) -#define OPCR_MPOb_C_TO (0x1 << 4) -#define OPCR_MPOb_TxC1X (0x2 << 4) -#define OPCR_MPOb_TxC16X (0x3 << 4) -#define OPCR_MPOb_RxC1X (0x4 << 4) -#define OPCR_MPOb_RxC16X (0x5 << 4) -#define OPCR_MPOb_TxRDY (0x6 << 4) -#define OPCR_MPOb_RxRDY_FF (0x7 << 4) - -#define OPCR_MPP_INPUT (0x0 << 7) -#define OPCR_MPP_OUTPUT (0x1 << 7) - -#define IMR_TxRDY_A (0x1 << 0) -#define IMR_RxRDY_FFULL_A (0x1 << 1) -#define IMR_DELTA_BREAK_A (0x1 << 2) -#define IMR_COUNTER_READY (0x1 << 3) -#define IMR_TxRDY_B (0x1 << 4) -#define IMR_RxRDY_FFULL_B (0x1 << 5) -#define IMR_DELTA_BREAK_B (0x1 << 6) -#define IMR_INPUT_PORT_CHANGE (0x1 << 7) - -#define ISR_TxRDY_A (0x1 << 0) -#define ISR_RxRDY_FFULL_A (0x1 << 1) -#define ISR_DELTA_BREAK_A (0x1 << 2) -#define ISR_COUNTER_READY (0x1 << 3) -#define ISR_TxRDY_B (0x1 << 4) -#define ISR_RxRDY_FFULL_B (0x1 << 5) -#define ISR_DELTA_BREAK_B (0x1 << 6) -#define ISR_INPUT_PORT_CHANGE (0x1 << 7) - -#define ACK_INT_REQ0 0 -#define ACK_INT_REQ1 2 - -#endif /* SCC2698_H_ */ diff --git a/drivers/staging/ipack/ipack.c b/drivers/staging/ipack/ipack.c deleted file mode 100644 index 6d5079de52b9..000000000000 --- a/drivers/staging/ipack/ipack.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Industry-pack bus support functions. - * - * Copyright (C) 2011-2012 CERN (www.cern.ch) - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#include -#include -#include -#include -#include "ipack.h" - -#define to_ipack_dev(device) container_of(device, struct ipack_device, dev) -#define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver) - -static DEFINE_IDA(ipack_ida); - -static void ipack_device_release(struct device *dev) -{ - struct ipack_device *device = to_ipack_dev(dev); - kfree(device->id); - device->release(device); -} - -static inline const struct ipack_device_id * -ipack_match_one_device(const struct ipack_device_id *id, - const struct ipack_device *device) -{ - if ((id->format == IPACK_ANY_FORMAT || - id->format == device->id_format) && - (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) && - (id->device == IPACK_ANY_ID || id->device == device->id_device)) - return id; - return NULL; -} - -static const struct ipack_device_id * -ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev) -{ - if (ids) { - while (ids->vendor || ids->device) { - if (ipack_match_one_device(ids, idev)) - return ids; - ids++; - } - } - return NULL; -} - -static int ipack_bus_match(struct device *dev, struct device_driver *drv) -{ - struct ipack_device *idev = to_ipack_dev(dev); - struct ipack_driver *idrv = to_ipack_driver(drv); - const struct ipack_device_id *found_id; - - found_id = ipack_match_id(idrv->id_table, idev); - return found_id ? 1 : 0; -} - -static int ipack_bus_probe(struct device *device) -{ - struct ipack_device *dev = to_ipack_dev(device); - struct ipack_driver *drv = to_ipack_driver(device->driver); - - if (!drv->ops->probe) - return -EINVAL; - - return drv->ops->probe(dev); -} - -static int ipack_bus_remove(struct device *device) -{ - struct ipack_device *dev = to_ipack_dev(device); - struct ipack_driver *drv = to_ipack_driver(device->driver); - - if (!drv->ops->remove) - return -EINVAL; - - drv->ops->remove(dev); - return 0; -} - -static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct ipack_device *idev; - - if (!dev) - return -ENODEV; - - idev = to_ipack_dev(dev); - - if (add_uevent_var(env, - "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format, - idev->id_vendor, idev->id_device)) - return -ENOMEM; - - return 0; -} - -#define ipack_device_attr(field, format_string) \ -static ssize_t \ -field##_show(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct ipack_device *idev = to_ipack_dev(dev); \ - return sprintf(buf, format_string, idev->field); \ -} - -static ssize_t id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned int i, c, l, s; - struct ipack_device *idev = to_ipack_dev(dev); - - - switch (idev->id_format) { - case IPACK_ID_VERSION_1: - l = 0x7; s = 1; break; - case IPACK_ID_VERSION_2: - l = 0xf; s = 2; break; - default: - return -EIO; - } - c = 0; - for (i = 0; i < idev->id_avail; i++) { - if (i > 0) { - if ((i & l) == 0) - buf[c++] = '\n'; - else if ((i & s) == 0) - buf[c++] = ' '; - } - sprintf(&buf[c], "%02x", idev->id[i]); - c += 2; - } - buf[c++] = '\n'; - return c; -} - -static ssize_t -id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ipack_device *idev = to_ipack_dev(dev); - switch (idev->id_format) { - case IPACK_ID_VERSION_1: - return sprintf(buf, "0x%02x\n", idev->id_vendor); - case IPACK_ID_VERSION_2: - return sprintf(buf, "0x%06x\n", idev->id_vendor); - default: - return -EIO; - } -} - -static ssize_t -id_device_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ipack_device *idev = to_ipack_dev(dev); - switch (idev->id_format) { - case IPACK_ID_VERSION_1: - return sprintf(buf, "0x%02x\n", idev->id_device); - case IPACK_ID_VERSION_2: - return sprintf(buf, "0x%04x\n", idev->id_device); - default: - return -EIO; - } -} - -static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ipack_device *idev = to_ipack_dev(dev); - - return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format, - idev->id_vendor, idev->id_device); -} - -ipack_device_attr(id_format, "0x%hhu\n"); - -static struct device_attribute ipack_dev_attrs[] = { - __ATTR_RO(id), - __ATTR_RO(id_device), - __ATTR_RO(id_format), - __ATTR_RO(id_vendor), - __ATTR_RO(modalias), -}; - -static struct bus_type ipack_bus_type = { - .name = "ipack", - .probe = ipack_bus_probe, - .match = ipack_bus_match, - .remove = ipack_bus_remove, - .dev_attrs = ipack_dev_attrs, - .uevent = ipack_uevent, -}; - -struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, - const struct ipack_bus_ops *ops) -{ - int bus_nr; - struct ipack_bus_device *bus; - - bus = kzalloc(sizeof(struct ipack_bus_device), GFP_KERNEL); - if (!bus) - return NULL; - - bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL); - if (bus_nr < 0) { - kfree(bus); - return NULL; - } - - bus->bus_nr = bus_nr; - bus->parent = parent; - bus->slots = slots; - bus->ops = ops; - return bus; -} -EXPORT_SYMBOL_GPL(ipack_bus_register); - -static int ipack_unregister_bus_member(struct device *dev, void *data) -{ - struct ipack_device *idev = to_ipack_dev(dev); - struct ipack_bus_device *bus = data; - - if (idev->bus == bus) - ipack_device_unregister(idev); - - return 1; -} - -int ipack_bus_unregister(struct ipack_bus_device *bus) -{ - bus_for_each_dev(&ipack_bus_type, NULL, bus, - ipack_unregister_bus_member); - ida_simple_remove(&ipack_ida, bus->bus_nr); - kfree(bus); - return 0; -} -EXPORT_SYMBOL_GPL(ipack_bus_unregister); - -int ipack_driver_register(struct ipack_driver *edrv, struct module *owner, - const char *name) -{ - edrv->driver.owner = owner; - edrv->driver.name = name; - edrv->driver.bus = &ipack_bus_type; - return driver_register(&edrv->driver); -} -EXPORT_SYMBOL_GPL(ipack_driver_register); - -void ipack_driver_unregister(struct ipack_driver *edrv) -{ - driver_unregister(&edrv->driver); -} -EXPORT_SYMBOL_GPL(ipack_driver_unregister); - -static u16 ipack_crc_byte(u16 crc, u8 c) -{ - int i; - - crc ^= c << 8; - for (i = 0; i < 8; i++) - crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0); - return crc; -} - -/* - * The algorithm in lib/crc-ccitt.c does not seem to apply since it uses the - * opposite bit ordering. - */ -static u8 ipack_calc_crc1(struct ipack_device *dev) -{ - u8 c; - u16 crc; - unsigned int i; - - crc = 0xffff; - for (i = 0; i < dev->id_avail; i++) { - c = (i != 11) ? dev->id[i] : 0; - crc = ipack_crc_byte(crc, c); - } - crc = ~crc; - return crc & 0xff; -} - -static u16 ipack_calc_crc2(struct ipack_device *dev) -{ - u8 c; - u16 crc; - unsigned int i; - - crc = 0xffff; - for (i = 0; i < dev->id_avail; i++) { - c = ((i != 0x18) && (i != 0x19)) ? dev->id[i] : 0; - crc = ipack_crc_byte(crc, c); - } - crc = ~crc; - return crc; -} - -static void ipack_parse_id1(struct ipack_device *dev) -{ - u8 *id = dev->id; - u8 crc; - - dev->id_vendor = id[4]; - dev->id_device = id[5]; - dev->speed_8mhz = 1; - dev->speed_32mhz = (id[7] == 'H'); - crc = ipack_calc_crc1(dev); - dev->id_crc_correct = (crc == id[11]); - if (!dev->id_crc_correct) { - dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n", - id[11], crc); - } -} - -static void ipack_parse_id2(struct ipack_device *dev) -{ - __be16 *id = (__be16 *) dev->id; - u16 flags, crc; - - dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16) - + be16_to_cpu(id[4]); - dev->id_device = be16_to_cpu(id[5]); - flags = be16_to_cpu(id[10]); - dev->speed_8mhz = !!(flags & 2); - dev->speed_32mhz = !!(flags & 4); - crc = ipack_calc_crc2(dev); - dev->id_crc_correct = (crc == be16_to_cpu(id[12])); - if (!dev->id_crc_correct) { - dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n", - id[11], crc); - } -} - -static int ipack_device_read_id(struct ipack_device *dev) -{ - u8 __iomem *idmem; - int i; - int ret = 0; - - idmem = ioremap(dev->region[IPACK_ID_SPACE].start, - dev->region[IPACK_ID_SPACE].size); - if (!idmem) { - dev_err(&dev->dev, "error mapping memory\n"); - return -ENOMEM; - } - - /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH" - * we are dealing with a IndustryPack format 1 device. If we detect - * "VITA4 " (16 bit big endian formatted) we are dealing with a - * IndustryPack format 2 device */ - if ((ioread8(idmem + 1) == 'I') && - (ioread8(idmem + 3) == 'P') && - (ioread8(idmem + 5) == 'A') && - ((ioread8(idmem + 7) == 'C') || - (ioread8(idmem + 7) == 'H'))) { - dev->id_format = IPACK_ID_VERSION_1; - dev->id_avail = ioread8(idmem + 0x15); - if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) { - dev_warn(&dev->dev, "invalid id size"); - dev->id_avail = 0x0c; - } - } else if ((ioread8(idmem + 0) == 'I') && - (ioread8(idmem + 1) == 'V') && - (ioread8(idmem + 2) == 'A') && - (ioread8(idmem + 3) == 'T') && - (ioread8(idmem + 4) == ' ') && - (ioread8(idmem + 5) == '4')) { - dev->id_format = IPACK_ID_VERSION_2; - dev->id_avail = ioread16be(idmem + 0x16); - if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) { - dev_warn(&dev->dev, "invalid id size"); - dev->id_avail = 0x1a; - } - } else { - dev->id_format = IPACK_ID_VERSION_INVALID; - dev->id_avail = 0; - } - - if (!dev->id_avail) { - ret = -ENODEV; - goto out; - } - - /* Obtain the amount of memory required to store a copy of the complete - * ID ROM contents */ - dev->id = kmalloc(dev->id_avail, GFP_KERNEL); - if (!dev->id) { - dev_err(&dev->dev, "dev->id alloc failed.\n"); - ret = -ENOMEM; - goto out; - } - for (i = 0; i < dev->id_avail; i++) { - if (dev->id_format == IPACK_ID_VERSION_1) - dev->id[i] = ioread8(idmem + (i << 1) + 1); - else - dev->id[i] = ioread8(idmem + i); - } - - /* now we can finally work with the copy */ - switch (dev->id_format) { - case IPACK_ID_VERSION_1: - ipack_parse_id1(dev); - break; - case IPACK_ID_VERSION_2: - ipack_parse_id2(dev); - break; - } - -out: - iounmap(idmem); - - return ret; -} - -int ipack_device_register(struct ipack_device *dev) -{ - int ret; - - dev->dev.bus = &ipack_bus_type; - dev->dev.release = ipack_device_release; - dev->dev.parent = dev->bus->parent; - dev_set_name(&dev->dev, - "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); - - if (dev->bus->ops->set_clockrate(dev, 8)) - dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n"); - if (dev->bus->ops->reset_timeout(dev)) - dev_warn(&dev->dev, "failed to reset potential timeout."); - - ret = ipack_device_read_id(dev); - if (ret < 0) { - dev_err(&dev->dev, "error reading device id section.\n"); - return ret; - } - - /* if the device supports 32 MHz operation, use it. */ - if (dev->speed_32mhz) { - ret = dev->bus->ops->set_clockrate(dev, 32); - if (ret < 0) - dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n"); - } - - ret = device_register(&dev->dev); - if (ret < 0) - kfree(dev->id); - - return ret; -} -EXPORT_SYMBOL_GPL(ipack_device_register); - -void ipack_device_unregister(struct ipack_device *dev) -{ - device_unregister(&dev->dev); -} -EXPORT_SYMBOL_GPL(ipack_device_unregister); - -static int __init ipack_init(void) -{ - ida_init(&ipack_ida); - return bus_register(&ipack_bus_type); -} - -static void __exit ipack_exit(void) -{ - bus_unregister(&ipack_bus_type); - ida_destroy(&ipack_ida); -} - -module_init(ipack_init); -module_exit(ipack_exit); - -MODULE_AUTHOR("Samuel Iglesias Gonsalvez "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Industry-pack bus core"); diff --git a/drivers/staging/ipack/ipack.h b/drivers/staging/ipack/ipack.h deleted file mode 100644 index 6760bfaf0ac4..000000000000 --- a/drivers/staging/ipack/ipack.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Industry-pack bus. - * - * Copyright (C) 2011-2012 CERN (www.cern.ch) - * Author: Samuel Iglesias Gonsalvez - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2 of the License. - */ - -#include -#include -#include - -#include "ipack_ids.h" - -#define IPACK_IDPROM_OFFSET_I 0x01 -#define IPACK_IDPROM_OFFSET_P 0x03 -#define IPACK_IDPROM_OFFSET_A 0x05 -#define IPACK_IDPROM_OFFSET_C 0x07 -#define IPACK_IDPROM_OFFSET_MANUFACTURER_ID 0x09 -#define IPACK_IDPROM_OFFSET_MODEL 0x0B -#define IPACK_IDPROM_OFFSET_REVISION 0x0D -#define IPACK_IDPROM_OFFSET_RESERVED 0x0F -#define IPACK_IDPROM_OFFSET_DRIVER_ID_L 0x11 -#define IPACK_IDPROM_OFFSET_DRIVER_ID_H 0x13 -#define IPACK_IDPROM_OFFSET_NUM_BYTES 0x15 -#define IPACK_IDPROM_OFFSET_CRC 0x17 - -struct ipack_bus_ops; -struct ipack_driver; - -enum ipack_space { - IPACK_IO_SPACE = 0, - IPACK_ID_SPACE, - IPACK_INT_SPACE, - IPACK_MEM8_SPACE, - IPACK_MEM16_SPACE, - /* Dummy for counting the number of entries. Must remain the last - * entry */ - IPACK_SPACE_COUNT, -}; - -/** - */ -struct ipack_region { - phys_addr_t start; - size_t size; -}; - -/** - * struct ipack_device - * - * @slot: Slot where the device is plugged in the carrier board - * @bus: ipack_bus_device where the device is plugged to. - * @id_space: Virtual address to ID space. - * @io_space: Virtual address to IO space. - * @mem_space: Virtual address to MEM space. - * @dev: device in kernel representation. - * - * Warning: Direct access to mapped memory is possible but the endianness - * is not the same with PCI carrier or VME carrier. The endianness is managed - * by the carrier board throught bus->ops. - */ -struct ipack_device { - unsigned int slot; - struct ipack_bus_device *bus; - struct device dev; - void (*release) (struct ipack_device *dev); - struct ipack_region region[IPACK_SPACE_COUNT]; - u8 *id; - size_t id_avail; - u32 id_vendor; - u32 id_device; - u8 id_format; - unsigned int id_crc_correct:1; - unsigned int speed_8mhz:1; - unsigned int speed_32mhz:1; -}; - -/** - * struct ipack_driver_ops -- Callbacks to IPack device driver - * - * @probe: Probe function - * @remove: Prepare imminent removal of the device. Services provided by the - * device should be revoked. - */ - -struct ipack_driver_ops { - int (*probe) (struct ipack_device *dev); - void (*remove) (struct ipack_device *dev); -}; - -/** - * struct ipack_driver -- Specific data to each ipack device driver - * - * @driver: Device driver kernel representation - * @ops: Callbacks provided by the IPack device driver - */ -struct ipack_driver { - struct device_driver driver; - const struct ipack_device_id *id_table; - const struct ipack_driver_ops *ops; -}; - -/** - * struct ipack_bus_ops - available operations on a bridge module - * - * @map_space: map IP address space - * @unmap_space: unmap IP address space - * @request_irq: request IRQ - * @free_irq: free IRQ - * @get_clockrate: Returns the clockrate the carrier is currently - * communicating with the device at. - * @set_clockrate: Sets the clock-rate for carrier / module communication. - * Should return -EINVAL if the requested speed is not supported. - * @get_error: Returns the error state for the slot the device is attached - * to. - * @get_timeout: Returns 1 if the communication with the device has - * previously timed out. - * @reset_timeout: Resets the state returned by get_timeout. - */ -struct ipack_bus_ops { - int (*request_irq) (struct ipack_device *dev, - irqreturn_t (*handler)(void *), void *arg); - int (*free_irq) (struct ipack_device *dev); - int (*get_clockrate) (struct ipack_device *dev); - int (*set_clockrate) (struct ipack_device *dev, int mherz); - int (*get_error) (struct ipack_device *dev); - int (*get_timeout) (struct ipack_device *dev); - int (*reset_timeout) (struct ipack_device *dev); -}; - -/** - * struct ipack_bus_device - * - * @dev: pointer to carrier device - * @slots: number of slots available - * @bus_nr: ipack bus number - * @ops: bus operations for the mezzanine drivers - */ -struct ipack_bus_device { - struct device *parent; - int slots; - int bus_nr; - const struct ipack_bus_ops *ops; -}; - -/** - * ipack_bus_register -- register a new ipack bus - * - * @parent: pointer to the parent device, if any. - * @slots: number of slots available in the bus device. - * @ops: bus operations for the mezzanine drivers. - * - * The carrier board device should call this function to register itself as - * available bus device in ipack. - */ -struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, - const struct ipack_bus_ops *ops); - -/** - * ipack_bus_unregister -- unregister an ipack bus - */ -int ipack_bus_unregister(struct ipack_bus_device *bus); - -/** - * ipack_driver_register -- Register a new ipack device driver - * - * Called by a ipack driver to register itself as a driver - * that can manage ipack devices. - */ -int ipack_driver_register(struct ipack_driver *edrv, struct module *owner, - const char *name); -void ipack_driver_unregister(struct ipack_driver *edrv); - -/** - * ipack_device_register -- register an IPack device with the kernel - * @dev: the new device to register. - * - * Register a new IPack device ("module" in IndustryPack jargon). The call - * is done by the carrier driver. The carrier should populate the fields - * bus and slot as well as the region array of @dev prior to calling this - * function. The rest of the fields will be allocated and populated - * during registration. - * - * Return zero on success or error code on failure. - */ -int ipack_device_register(struct ipack_device *dev); -void ipack_device_unregister(struct ipack_device *dev); - -/** - * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table - * @_table: device table name - * - * This macro is used to create a struct ipack_device_id array (a device table) - * in a generic manner. - */ -#define DEFINE_IPACK_DEVICE_TABLE(_table) \ - const struct ipack_device_id _table[] __devinitconst - -/** - * IPACK_DEVICE - macro used to describe a specific IndustryPack device - * @_format: the format version (currently either 1 or 2, 8 bit value) - * @vend: the 8 or 24 bit IndustryPack Vendor ID - * @dev: the 8 or 16 bit IndustryPack Device ID - * - * This macro is used to create a struct ipack_device_id that matches a specific - * device. - */ -#define IPACK_DEVICE(_format, vend, dev) \ - .format = (_format), \ - .vendor = (vend), \ - .device = (dev) diff --git a/drivers/staging/ipack/ipack_ids.h b/drivers/staging/ipack/ipack_ids.h deleted file mode 100644 index 8153fee3f2f7..000000000000 --- a/drivers/staging/ipack/ipack_ids.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * IndustryPack Fromat, Vendor and Device IDs. - */ - -/* ID section format versions */ -#define IPACK_ID_VERSION_INVALID 0x00 -#define IPACK_ID_VERSION_1 0x01 -#define IPACK_ID_VERSION_2 0x02 - -/* Vendors and devices. Sort key: vendor first, device next. */ -#define IPACK1_VENDOR_ID_RESERVED1 0x00 -#define IPACK1_VENDOR_ID_RESERVED2 0xFF -#define IPACK1_VENDOR_ID_UNREGISTRED01 0x01 -#define IPACK1_VENDOR_ID_UNREGISTRED02 0x02 -#define IPACK1_VENDOR_ID_UNREGISTRED03 0x03 -#define IPACK1_VENDOR_ID_UNREGISTRED04 0x04 -#define IPACK1_VENDOR_ID_UNREGISTRED05 0x05 -#define IPACK1_VENDOR_ID_UNREGISTRED06 0x06 -#define IPACK1_VENDOR_ID_UNREGISTRED07 0x07 -#define IPACK1_VENDOR_ID_UNREGISTRED08 0x08 -#define IPACK1_VENDOR_ID_UNREGISTRED09 0x09 -#define IPACK1_VENDOR_ID_UNREGISTRED10 0x0A -#define IPACK1_VENDOR_ID_UNREGISTRED11 0x0B -#define IPACK1_VENDOR_ID_UNREGISTRED12 0x0C -#define IPACK1_VENDOR_ID_UNREGISTRED13 0x0D -#define IPACK1_VENDOR_ID_UNREGISTRED14 0x0E -#define IPACK1_VENDOR_ID_UNREGISTRED15 0x0F - -#define IPACK1_VENDOR_ID_SBS 0xF0 -#define IPACK1_DEVICE_ID_SBS_OCTAL_232 0x22 -#define IPACK1_DEVICE_ID_SBS_OCTAL_422 0x2A -#define IPACK1_DEVICE_ID_SBS_OCTAL_485 0x48 -- cgit