162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ddbridge.c: Digital Devices PCIe bridge driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010-2017 Digital Devices GmbH 662306a36Sopenharmony_ci * Ralph Metzler <rjkm@metzlerbros.de> 762306a36Sopenharmony_ci * Marcus Metzler <mocm@metzlerbros.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/poll.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/pci_ids.h> 2162306a36Sopenharmony_ci#include <linux/timer.h> 2262306a36Sopenharmony_ci#include <linux/i2c.h> 2362306a36Sopenharmony_ci#include <linux/swab.h> 2462306a36Sopenharmony_ci#include <linux/vmalloc.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "ddbridge.h" 2762306a36Sopenharmony_ci#include "ddbridge-i2c.h" 2862306a36Sopenharmony_ci#include "ddbridge-regs.h" 2962306a36Sopenharmony_ci#include "ddbridge-hw.h" 3062306a36Sopenharmony_ci#include "ddbridge-io.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/****************************************************************************/ 3362306a36Sopenharmony_ci/* module parameters */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 3662306a36Sopenharmony_ci#ifdef CONFIG_DVB_DDBRIDGE_MSIENABLE 3762306a36Sopenharmony_cistatic int msi = 1; 3862306a36Sopenharmony_ci#else 3962306a36Sopenharmony_cistatic int msi; 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_cimodule_param(msi, int, 0444); 4262306a36Sopenharmony_ci#ifdef CONFIG_DVB_DDBRIDGE_MSIENABLE 4362306a36Sopenharmony_ciMODULE_PARM_DESC(msi, "Control MSI interrupts: 0-disable, 1-enable (default)"); 4462306a36Sopenharmony_ci#else 4562306a36Sopenharmony_ciMODULE_PARM_DESC(msi, "Control MSI interrupts: 0-disable (default), 1-enable"); 4662306a36Sopenharmony_ci#endif 4762306a36Sopenharmony_ci#endif 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/****************************************************************************/ 5062306a36Sopenharmony_ci/****************************************************************************/ 5162306a36Sopenharmony_ci/****************************************************************************/ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void ddb_irq_disable(struct ddb *dev) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci ddbwritel(dev, 0, INTERRUPT_ENABLE); 5662306a36Sopenharmony_ci ddbwritel(dev, 0, MSI1_ENABLE); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void ddb_msi_exit(struct ddb *dev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 6262306a36Sopenharmony_ci if (dev->msi) 6362306a36Sopenharmony_ci pci_free_irq_vectors(dev->pdev); 6462306a36Sopenharmony_ci#endif 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void ddb_irq_exit(struct ddb *dev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci ddb_irq_disable(dev); 7062306a36Sopenharmony_ci if (dev->msi == 2) 7162306a36Sopenharmony_ci free_irq(pci_irq_vector(dev->pdev, 1), dev); 7262306a36Sopenharmony_ci free_irq(pci_irq_vector(dev->pdev, 0), dev); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void ddb_remove(struct pci_dev *pdev) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct ddb *dev = (struct ddb *)pci_get_drvdata(pdev); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ddb_device_destroy(dev); 8062306a36Sopenharmony_ci ddb_ports_detach(dev); 8162306a36Sopenharmony_ci ddb_i2c_release(dev); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ddb_irq_exit(dev); 8462306a36Sopenharmony_ci ddb_msi_exit(dev); 8562306a36Sopenharmony_ci ddb_ports_release(dev); 8662306a36Sopenharmony_ci ddb_buffers_free(dev); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ddb_unmap(dev); 8962306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 9062306a36Sopenharmony_ci pci_disable_device(pdev); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 9462306a36Sopenharmony_cistatic void ddb_irq_msi(struct ddb *dev, int nr) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int stat; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (msi && pci_msi_enabled()) { 9962306a36Sopenharmony_ci stat = pci_alloc_irq_vectors(dev->pdev, 1, nr, 10062306a36Sopenharmony_ci PCI_IRQ_MSI | PCI_IRQ_MSIX); 10162306a36Sopenharmony_ci if (stat >= 1) { 10262306a36Sopenharmony_ci dev->msi = stat; 10362306a36Sopenharmony_ci dev_info(dev->dev, "using %d MSI interrupt(s)\n", 10462306a36Sopenharmony_ci dev->msi); 10562306a36Sopenharmony_ci } else { 10662306a36Sopenharmony_ci dev_info(dev->dev, "MSI not available.\n"); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci#endif 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int ddb_irq_init(struct ddb *dev) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int stat; 11562306a36Sopenharmony_ci int irq_flag = IRQF_SHARED; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, INTERRUPT_ENABLE); 11862306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI1_ENABLE); 11962306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI2_ENABLE); 12062306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI3_ENABLE); 12162306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI4_ENABLE); 12262306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI5_ENABLE); 12362306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI6_ENABLE); 12462306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI7_ENABLE); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 12762306a36Sopenharmony_ci ddb_irq_msi(dev, 2); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (dev->msi) 13062306a36Sopenharmony_ci irq_flag = 0; 13162306a36Sopenharmony_ci if (dev->msi == 2) { 13262306a36Sopenharmony_ci stat = request_irq(pci_irq_vector(dev->pdev, 0), 13362306a36Sopenharmony_ci ddb_irq_handler0, irq_flag, "ddbridge", 13462306a36Sopenharmony_ci (void *)dev); 13562306a36Sopenharmony_ci if (stat < 0) 13662306a36Sopenharmony_ci return stat; 13762306a36Sopenharmony_ci stat = request_irq(pci_irq_vector(dev->pdev, 1), 13862306a36Sopenharmony_ci ddb_irq_handler1, irq_flag, "ddbridge", 13962306a36Sopenharmony_ci (void *)dev); 14062306a36Sopenharmony_ci if (stat < 0) { 14162306a36Sopenharmony_ci free_irq(pci_irq_vector(dev->pdev, 0), dev); 14262306a36Sopenharmony_ci return stat; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } else 14562306a36Sopenharmony_ci#endif 14662306a36Sopenharmony_ci { 14762306a36Sopenharmony_ci stat = request_irq(pci_irq_vector(dev->pdev, 0), 14862306a36Sopenharmony_ci ddb_irq_handler, irq_flag, "ddbridge", 14962306a36Sopenharmony_ci (void *)dev); 15062306a36Sopenharmony_ci if (stat < 0) 15162306a36Sopenharmony_ci return stat; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci if (dev->msi == 2) { 15462306a36Sopenharmony_ci ddbwritel(dev, 0x0fffff00, INTERRUPT_ENABLE); 15562306a36Sopenharmony_ci ddbwritel(dev, 0x0000000f, MSI1_ENABLE); 15662306a36Sopenharmony_ci } else { 15762306a36Sopenharmony_ci ddbwritel(dev, 0x0fffff0f, INTERRUPT_ENABLE); 15862306a36Sopenharmony_ci ddbwritel(dev, 0x00000000, MSI1_ENABLE); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci return stat; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int ddb_probe(struct pci_dev *pdev, 16462306a36Sopenharmony_ci const struct pci_device_id *id) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct ddb *dev; 16762306a36Sopenharmony_ci int stat = 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (pci_enable_device(pdev) < 0) 17062306a36Sopenharmony_ci return -ENODEV; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci pci_set_master(pdev); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) 17562306a36Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) 17662306a36Sopenharmony_ci return -ENODEV; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci dev = vzalloc(sizeof(*dev)); 17962306a36Sopenharmony_ci if (!dev) 18062306a36Sopenharmony_ci return -ENOMEM; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mutex_init(&dev->mutex); 18362306a36Sopenharmony_ci dev->has_dma = 1; 18462306a36Sopenharmony_ci dev->pdev = pdev; 18562306a36Sopenharmony_ci dev->dev = &pdev->dev; 18662306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci dev->link[0].ids.vendor = id->vendor; 18962306a36Sopenharmony_ci dev->link[0].ids.device = id->device; 19062306a36Sopenharmony_ci dev->link[0].ids.subvendor = id->subvendor; 19162306a36Sopenharmony_ci dev->link[0].ids.subdevice = pdev->subsystem_device; 19262306a36Sopenharmony_ci dev->link[0].ids.devid = (id->device << 16) | id->vendor; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci dev->link[0].dev = dev; 19562306a36Sopenharmony_ci dev->link[0].info = get_ddb_info(id->vendor, id->device, 19662306a36Sopenharmony_ci id->subvendor, pdev->subsystem_device); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci dev_info(&pdev->dev, "detected %s\n", dev->link[0].info->name); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dev->regs_len = pci_resource_len(dev->pdev, 0); 20162306a36Sopenharmony_ci dev->regs = ioremap(pci_resource_start(dev->pdev, 0), 20262306a36Sopenharmony_ci pci_resource_len(dev->pdev, 0)); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!dev->regs) { 20562306a36Sopenharmony_ci dev_err(&pdev->dev, "not enough memory for register map\n"); 20662306a36Sopenharmony_ci stat = -ENOMEM; 20762306a36Sopenharmony_ci goto fail; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci if (ddbreadl(dev, 0) == 0xffffffff) { 21062306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot read registers\n"); 21162306a36Sopenharmony_ci stat = -ENODEV; 21262306a36Sopenharmony_ci goto fail; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci dev->link[0].ids.hwid = ddbreadl(dev, 0); 21662306a36Sopenharmony_ci dev->link[0].ids.regmapid = ddbreadl(dev, 4); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci dev_info(&pdev->dev, "HW %08x REGMAP %08x\n", 21962306a36Sopenharmony_ci dev->link[0].ids.hwid, dev->link[0].ids.regmapid); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ddbwritel(dev, 0, DMA_BASE_READ); 22262306a36Sopenharmony_ci ddbwritel(dev, 0, DMA_BASE_WRITE); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci stat = ddb_irq_init(dev); 22562306a36Sopenharmony_ci if (stat < 0) 22662306a36Sopenharmony_ci goto fail0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (ddb_init(dev) == 0) 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ddb_irq_exit(dev); 23262306a36Sopenharmony_cifail0: 23362306a36Sopenharmony_ci dev_err(&pdev->dev, "fail0\n"); 23462306a36Sopenharmony_ci ddb_msi_exit(dev); 23562306a36Sopenharmony_cifail: 23662306a36Sopenharmony_ci dev_err(&pdev->dev, "fail\n"); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ddb_unmap(dev); 23962306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 24062306a36Sopenharmony_ci pci_disable_device(pdev); 24162306a36Sopenharmony_ci return stat; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/****************************************************************************/ 24562306a36Sopenharmony_ci/****************************************************************************/ 24662306a36Sopenharmony_ci/****************************************************************************/ 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci#define DDB_DEVICE_ANY(_device) \ 24962306a36Sopenharmony_ci { PCI_DEVICE_SUB(DDVID, _device, DDVID, PCI_ANY_ID) } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic const struct pci_device_id ddb_id_table[] = { 25262306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0002), 25362306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0003), 25462306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0005), 25562306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0006), 25662306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0007), 25762306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0008), 25862306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0009), 25962306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0011), 26062306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0012), 26162306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0013), 26262306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0201), 26362306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0203), 26462306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0210), 26562306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0220), 26662306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0320), 26762306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0321), 26862306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0322), 26962306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0323), 27062306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0328), 27162306a36Sopenharmony_ci DDB_DEVICE_ANY(0x0329), 27262306a36Sopenharmony_ci {0} 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ddb_id_table); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct pci_driver ddb_pci_driver = { 27862306a36Sopenharmony_ci .name = "ddbridge", 27962306a36Sopenharmony_ci .id_table = ddb_id_table, 28062306a36Sopenharmony_ci .probe = ddb_probe, 28162306a36Sopenharmony_ci .remove = ddb_remove, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic __init int module_init_ddbridge(void) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int stat; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci pr_info("Digital Devices PCIE bridge driver " 28962306a36Sopenharmony_ci DDBRIDGE_VERSION 29062306a36Sopenharmony_ci ", Copyright (C) 2010-17 Digital Devices GmbH\n"); 29162306a36Sopenharmony_ci stat = ddb_init_ddbridge(); 29262306a36Sopenharmony_ci if (stat < 0) 29362306a36Sopenharmony_ci return stat; 29462306a36Sopenharmony_ci stat = pci_register_driver(&ddb_pci_driver); 29562306a36Sopenharmony_ci if (stat < 0) 29662306a36Sopenharmony_ci ddb_exit_ddbridge(0, stat); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return stat; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic __exit void module_exit_ddbridge(void) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci pci_unregister_driver(&ddb_pci_driver); 30462306a36Sopenharmony_ci ddb_exit_ddbridge(0, 0); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cimodule_init(module_init_ddbridge); 30862306a36Sopenharmony_cimodule_exit(module_exit_ddbridge); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ciMODULE_DESCRIPTION("Digital Devices PCIe Bridge"); 31162306a36Sopenharmony_ciMODULE_AUTHOR("Ralph and Marcus Metzler, Metzler Brothers Systementwicklung GbR"); 31262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 31362306a36Sopenharmony_ciMODULE_VERSION(DDBRIDGE_VERSION); 314