diff options
| author | Adrian Betschart <adrian.betschart@cinemaone.ch> | 2026-09-11 17:13:14 +0200 |
|---|---|---|
| committer | Alex Deucher <alexander.deucher@amd.com> | 2026-10-01 17:18:05 -0400 |
| commit | 106b4a2a78b2a2f14885604891957b84bba502db (patch) | |
| tree | 5d40eef2428fb1a4791ab46a4b19091a5910eac2 /drivers/ipack/carriers | |
| download | linux-stable-106b4a2a78b2a2f14885604891957b84bba502db.tar.gz linux-stable-106b4a2a78b2a2f14885604891957b84bba502db.zip | |
drm/amd/display: Try RGB before YCbCr 4:4:4 in stream validationgrafted
amdgpu_dm_create_validate_stream_for_sink() walks encoding_order[] and
uses the first encoding that validates. YCbCr 4:4:4 is listed before
RGB, so an HDMI sink that advertises 4:4:4 gets YCbCr 4:4:4 whenever the
"color format" property is left at AUTO, even though RGB fits the same
link.
That contradicts the documented AUTO behaviour for HDMI in enum
drm_connector_color_format (RGB, falling back to YCbCr 4:2:0 only when
the bandwidth is not available or the mode is 4:2:0-only), which the
amdgpu implementation of the property also describes. It also leaves
the "Broadcast RGB" property without effect on such sinks, since the
quantization range it selects only applies to RGB output.
Try RGB first. The mask still holds every encoding the sink supports,
so a mode that cannot carry RGB falls back exactly as before.
For reference, v7.2 picked RGB here unless YCbCr 4:4:4 was forced
through debugfs, while earlier kernels picked YCbCr 4:4:4 for any HDMI
sink that advertised it.
Fixes: 0b0ff65d3ca1 ("drm/amd/display: Refactor stream validation")
Suggested-by: Adolfo Rodrigues <adolfotregosa@gmail.com>
Assisted-by: Claude Code:claude-fable-5-1
Signed-off-by: Adrian Betschart <adrian.betschart@cinemaone.ch>
Reviewed-by: Fangzhi Zuo <jerry.zuo@amd.com>
Tested-by: Adolfo Rodrigues <adolfotregosa@gmail.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 7de432bc753133764dc40d37f9388da56de251e7)
Diffstat (limited to 'drivers/ipack/carriers')
| -rw-r--r-- | drivers/ipack/carriers/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/ipack/carriers/Makefile | 2 | ||||
| -rw-r--r-- | drivers/ipack/carriers/tpci200.c | 648 | ||||
| -rw-r--r-- | drivers/ipack/carriers/tpci200.h | 161 |
4 files changed, 819 insertions, 0 deletions
diff --git a/drivers/ipack/carriers/Kconfig b/drivers/ipack/carriers/Kconfig new file mode 100644 index 000000000..01c2dc947 --- /dev/null +++ b/drivers/ipack/carriers/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +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 000000000..0301cae31 --- /dev/null +++ b/drivers/ipack/carriers/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +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 000000000..1cf51f763 --- /dev/null +++ b/drivers/ipack/carriers/tpci200.c @@ -0,0 +1,648 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * driver for the TEWS TPCI-200 device + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> +#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_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_disable_device(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_obj(struct slot_irq); + 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 err_disable_device; + } + + /* 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 err_ip_interface_bar; + } + + /* 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 err_io_id_int_spaces_bar; + } + + /* 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 err_mem8_space_bar; + } + + /* Map internal tpci200 driver user space */ + tpci200->info->interface_regs = + ioremap(pci_resource_start(tpci200->info->pdev, + TPCI200_IP_INTERFACE_BAR), + TPCI200_IFACE_SIZE); + if (!tpci200->info->interface_regs) { + dev_err(&tpci200->info->pdev->dev, + "(bn 0x%X, sn 0x%X) failed to map driver user space!", + tpci200->info->pdev->bus->number, + tpci200->info->pdev->devfn); + res = -ENOMEM; + goto err_mem16_space_bar; + } + + /* 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 err_interface_regs; + } + + return 0; + +err_interface_regs: + pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); +err_mem16_space_bar: + pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); +err_mem8_space_bar: + pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); +err_io_id_int_spaces_bar: + pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); +err_ip_interface_bar: + pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); +err_disable_device: + 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_objs(struct tpci200_slot, TPCI200_NB_SLOT); + 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) +{ + int ret; + enum ipack_space space; + struct ipack_device *dev = + kzalloc_obj(struct ipack_device); + 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]; + } + + ret = ipack_device_init(dev); + if (ret < 0) { + ipack_put_device(dev); + return ret; + } + + ret = ipack_device_add(dev); + if (ret < 0) + ipack_put_device(dev); + + return ret; +} + +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_obj(struct tpci200_board); + if (!tpci200) + return -ENOMEM; + + tpci200->info = kzalloc_obj(struct tpci200_infos); + if (!tpci200->info) { + ret = -ENOMEM; + goto err_tpci200; + } + + 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 err_tpci200_info; + } + tpci200->info->cfg_regs = ioremap( + 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 err_request_region; + } + + /* 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; + + /* register the device and initialize it */ + ret = tpci200_install(tpci200); + if (ret) { + dev_err(&pdev->dev, "error during tpci200 install\n"); + ret = -ENODEV; + goto err_cfg_regs; + } + + /* Register the carrier in the industry pack bus driver */ + tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, + TPCI200_NB_SLOT, + &tpci200_bus_ops, + THIS_MODULE); + if (!tpci200->info->ipack_bus) { + dev_err(&pdev->dev, + "error registering the carrier on ipack driver\n"); + ret = -EFAULT; + goto err_tpci200_install; + } + + /* 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; + +err_tpci200_install: + tpci200_uninstall(tpci200); +err_cfg_regs: + pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); +err_request_region: + pci_release_region(pdev, TPCI200_CFG_MEM_BAR); +err_tpci200_info: + kfree(tpci200->info); + pci_dev_put(pdev); +err_tpci200: + kfree(tpci200); + return ret; +} + +static void __tpci200_pci_remove(struct tpci200_board *tpci200) +{ + ipack_bus_unregister(tpci200->info->ipack_bus); + tpci200_uninstall(tpci200); + + pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); + + pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); + + pci_dev_put(tpci200->info->pdev); + + kfree(tpci200->info); + kfree(tpci200); +} + +static void tpci200_pci_remove(struct pci_dev *dev) +{ + struct tpci200_board *tpci200 = pci_get_drvdata(dev); + + __tpci200_pci_remove(tpci200); +} + +static const struct pci_device_id 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 = 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 000000000..a2bf31257 --- /dev/null +++ b/drivers/ipack/carriers/tpci200.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * driver for the carrier TEWS TPCI-200 + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> + */ + +#ifndef _TPCI200_H_ +#define _TPCI200_H_ + +#include <linux/limits.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/swab.h> +#include <linux/io.h> +#include <linux/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 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_ */ |
