+/* esdpci266.c - support for ESD PCI/PMC 266 cards
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3 17 Jun 2004
+ */
+
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
+#include "../include/main.h"
+#include "../include/sja1000p.h"
+
+#ifdef CAN_ENABLE_PCI_SUPPORT
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
+#define ioread32 can_readl
+#define iowrite32 can_writel
+#define ioread8 can_readb
+#define iowrite8 can_writeb
+#define wmb()
+#define rmb()
+#endif
+
+#define PLX_9056_VENDOR_ID 0x10B5
+#define PLX_9056_DEVICE_ID 0x9056
+#define ESDPCI266_PCI_VENDOR_ID 0x12FE
+#define ESDPCI266_PCI_PRODUCT_ID 0x000E
+
+/* PCI to local bus bridge PLX9050 */
+#define ESDPCI266_BYTES_PER_CIRCUIT 0x100
+
+void esdpci266_enable_irq(struct candevice_t *candev)
+{
+ uint32_t intcsr;
+ can_ioptr_t addr = candev->aux_base_addr + 0x68;
+ intcsr = ioread32(addr);
+ intcsr |= 0x900;
+ iowrite32(intcsr, addr);
+}
+
+void esdpci266_disable_irq(struct candevice_t *candev)
+{
+ uint32_t intcsr;
+ can_ioptr_t addr = candev->aux_base_addr + 0x68;
+ intcsr = ioread32(addr);
+ intcsr &= ~0x900;
+ iowrite32(intcsr, addr);
+}
+
+int esdpci266_request_io(struct candevice_t *candev)
+{
+ unsigned long addr, size;
+ can_ioptr_t remap_addr;
+ struct pci_dev *dev = candev->sysdevptr.pcidev;
+
+ /* request memory region for SJA chips window */
+ if (pci_request_region(dev, 2, "ESD PCI/PMC 266 - MEM") != 0) {
+ printk("lincan: request of memory range failed\n");
+ return -ENODEV;
+ }
+
+ /* determine address and size of memory window #2 */
+ addr = pci_resource_start(dev, 2);
+ size = pci_resource_len(dev, 2);
+
+ /* remap to kernel address space */
+ remap_addr = ioremap(addr, size);
+
+ if (!remap_addr) {
+ printk("lincan: Unable to access I/O memory at: 0x%lx\n", addr);
+ pci_release_region(dev, 2);
+ return -ENODEV;
+ }
+
+ /* remap base device address */
+ can_base_addr_fixup(candev, remap_addr);
+
+ /* request memory region for PLX registers */
+ if (pci_request_region(dev, 0, "ESD PCI/PMC 266 - PLX9056") != 0) {
+ printk("lincan: request of memory range failed\n");
+ iounmap(candev->dev_base_addr);
+ pci_release_region(dev, 2);
+ return -ENODEV;
+ }
+
+ /* determine address and size of memory window #2 */
+ addr = pci_resource_start(dev, 0);
+ size = pci_resource_len(dev, 0);
+
+ /* remap to kernel address space */
+ remap_addr = ioremap(addr, size);
+
+ if (!remap_addr) {
+ printk("lincan: Unable to access I/O memory at: 0x%lx\n", addr);
+ pci_release_region(dev, 0);
+ iounmap(candev->dev_base_addr);
+ pci_release_region(dev, 2);
+ return -ENODEV;
+ }
+
+ /* save address of PLX regs */
+ candev->aux_base_addr = remap_addr;
+
+ /* enable irqs */
+ esdpci266_enable_irq(candev);
+
+ return 0;
+}
+
+int esdpci266_release_io(struct candevice_t *candev)
+{
+ esdpci266_disable_irq(candev);
+ iounmap(candev->aux_base_addr);
+ pci_release_region(candev->sysdevptr.pcidev, 0);
+ iounmap(candev->dev_base_addr);
+ pci_release_region(candev->sysdevptr.pcidev, 2);
+ return 0;
+}
+
+void esdpci266_write_register(unsigned data, can_ioptr_t address)
+{
+ iowrite8(data, address);
+ wmb();
+}
+
+unsigned esdpci266_read_register(can_ioptr_t address)
+{
+ return ioread8(address);
+}
+
+int esdpci266_reset(struct candevice_t *candev)
+{
+ int i = 0, chip_nr;
+ struct canchip_t *chip;
+ unsigned cdr;
+
+ printk("lincan: resetting ESD PCI/PMC 266 hardware ...\n");
+
+ for (chip_nr = 0; chip_nr < candev->nr_all_chips; chip_nr++) {
+ if (!candev->chip[chip_nr])
+ continue;
+
+ printk("lincan: resetting SJA1000 chip nr. %d\n", chip_nr);
+
+ chip = candev->chip[chip_nr];
+
+ esdpci266_write_register(sjaMOD_RM,
+ chip->chip_base_addr + SJAMOD);
+ udelay(1000);
+
+ cdr = esdpci266_read_register(chip->chip_base_addr + SJACDR);
+ esdpci266_write_register(cdr | sjaCDR_PELICAN,
+ chip->chip_base_addr + SJACDR);
+ esdpci266_write_register(0, chip->chip_base_addr + SJAIER);
+
+ i = 20;
+ esdpci266_write_register(0, chip->chip_base_addr + SJAMOD);
+ while (esdpci266_read_register(chip->chip_base_addr + SJAMOD) &
+ sjaMOD_RM) {
+ if (!i--)
+ return -ENODEV;
+ udelay(1000);
+ esdpci266_write_register(0,
+ chip->chip_base_addr + SJAMOD);
+ }
+
+ cdr = esdpci266_read_register(chip->chip_base_addr + SJACDR);
+ esdpci266_write_register(cdr | sjaCDR_PELICAN,
+ chip->chip_base_addr + SJACDR);
+ esdpci266_write_register(0, chip->chip_base_addr + SJAIER);
+ esdpci266_read_register(chip->chip_base_addr + SJAIR);
+ }
+
+ return 0;
+}
+
+int esdpci266_init_hw_data(struct candevice_t *candev)
+{
+ struct pci_dev *pcidev = NULL;
+
+ printk("lincan: search for ESD PCI/PMC 266 board ...\n");
+
+ do {
+ pcidev =
+ pci_find_device(PLX_9056_VENDOR_ID, PLX_9056_DEVICE_ID,
+ pcidev);
+ if (pcidev == NULL)
+ return -ENODEV;
+ if (pcidev->subsystem_vendor != ESDPCI266_PCI_VENDOR_ID
+ || pcidev->subsystem_device != ESDPCI266_PCI_PRODUCT_ID) {
+ printk
+ ("PLX9056 found, subvendor/subdevice mismatch (%04d:%04d)\n",
+ pcidev->subsystem_vendor,
+ pcidev->subsystem_device);
+ continue;
+ }
+ } while (can_check_dev_taken(pcidev));
+
+ if (pci_enable_device(pcidev)) {
+ printk("lincan: pci_enable_device() failed\n");
+ return -EIO;
+ }
+
+ candev->sysdevptr.pcidev = pcidev;
+
+ candev->io_addr = pci_resource_start(pcidev, 2);
+ candev->dev_base_addr = 0;
+ candev->res_addr = 0;
+ candev->nr_82527_chips = 0;
+ candev->nr_sja1000_chips = 2;
+ candev->nr_all_chips = 2;
+
+ printk("lincan: ESD PCI/PMC 266 board found (%lx)\n", candev->io_addr);
+
+ return 0;
+}
+
+int esdpci266_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+ if (candev->sysdevptr.pcidev == NULL)
+ return -ENODEV;
+
+ candev->chip[chipnr]->chip_irq = candev->sysdevptr.pcidev->irq;
+ sja1000p_fill_chipspecops(candev->chip[chipnr]);
+ candev->chip[chipnr]->chip_base_addr =
+ candev->dev_base_addr + chipnr * ESDPCI266_BYTES_PER_CIRCUIT;
+ candev->chip[chipnr]->clock = 16000000;
+ candev->chip[chipnr]->int_clk_reg = 0;
+ candev->chip[chipnr]->int_bus_reg = 0;
+ candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
+ candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
+ candev->chip[chipnr]->flags |= CHIP_IRQ_PCI;
+ return 0;
+}
+
+int esdpci266_init_obj_data(struct canchip_t *chip, int objnr)
+{
+ chip->msgobj[objnr]->obj_base_addr = chip->chip_base_addr;
+ return 0;
+}
+
+int esdpci266_program_irq(struct candevice_t *candev)
+{
+ return 0;
+}
+
+int esdpci266_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = esdpci266_request_io;
+ hwspecops->release_io = esdpci266_release_io;
+ hwspecops->reset = esdpci266_reset;
+ hwspecops->init_hw_data = esdpci266_init_hw_data;
+ hwspecops->init_chip_data = esdpci266_init_chip_data;
+ hwspecops->init_obj_data = esdpci266_init_obj_data;
+ hwspecops->write_register = esdpci266_write_register;
+ hwspecops->read_register = esdpci266_read_register;
+ hwspecops->program_irq = esdpci266_program_irq;
+ return 0;
+}
+
+#endif /* CAN_ENABLE_PCI_SUPPORT */