// SPDX-License-Identifier: GPL-2.0 /* * AX99100 PCIe to Multi I/O Controller - SPI driver * * drivers/spi/ax99100-pci-spi.c * * (C) Copyright 2019 Hilscher Gesellschaft fuer Systemautomation mbH * http://www.hilscher.com * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define DRIVER_DESC "AX99100 PCIe to Multi I/O Controller - SPI driver" #define DRIVER_NAME "ax99100-pci-spi" #define HILSCHER_PCI_VENDOR_ID 0x15cf #define HILSCHER_PCI_DEVICE_ID 0x0090 #define HILSCHER_PCI_SUB_VENDOR_ID 0x15cf #define HILSCHER_PCI_SUB_DEVICE_ID_GPIO 0x1002 #define HILSCHER_PCI_SUB_DEVICE_ID_SPI 0x6001 #include #include #include #include #include #include #include /* -------------------------------------------------------------------------- */ /* ------ Global chip settings ---------------------------------------------- */ /* -------------------------------------------------------------------------- */ #define MAX_SPEED_HZ (DIV_ROUND_UP(125000000, 3)) /* ~41,67MHz (max. Chip frequency = 42MHz) */ #define MIN_SPEED_HZ (DIV_ROUND_UP(125000000, 255)) /* ~490,197kHz */ /* NOTE: Currently only the internal CS decoder is supported! */ #define NUM_CHIPSELECT (3) #define DMA_MIN_SIZE (8) #define DMA_MAX_SIZE (64 * 1024) /* -------------------------------------------------------------------------- */ /* ------ Regdefs ----------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #define regdef(mask, shift, name) \ static inline uint8_t g##name(uint8_t val)\ { \ return (val >> shift) & mask; \ } \ static inline uint8_t s##name(uint8_t val) \ { \ return (val & mask) << shift; \ } /* ------ BAR0 Register (I/O mapped) ---------------------------------------- */ #pragma pack(1) struct regdef_spi { uint8_t cm; uint8_t css; uint8_t res0[2]; uint8_t br; uint8_t ds; uint8_t dt; uint8_t sdaof; uint8_t stof[8]; uint8_t sdfl0; uint8_t sdfl1; uint8_t ssol; uint8_t sdc; uint8_t mis; }; #pragma pack() regdef(0x1, 0, CM_SSP) regdef(0x1, 1, CM_CPHA) regdef(0x1, 2, CM_CPOL) regdef(0x1, 3, CM_LSB) regdef(0x1, 4, CM_SPIMEN) regdef(0x1, 5, CM_ASS) regdef(0x1, 6, CM_SWE) regdef(0x1, 7, CM_SSOE) regdef(0x3, 0, CSS_SPICKS) regdef(0x1, 2, CSS_DIVEN) regdef(0xff, 0, BR_DIVIDER) regdef(0x7, 0, SSOL_SS) regdef(0x1, 3, SSOL_EDE) regdef(0x7, 4, SSOL_STOL) regdef(0x1, 0, SDC_ETDMA) regdef(0x1, 1, SDC_ERDMA) regdef(0x1, 2, SDC_OPCFE) regdef(0x1, 3, SDC_DMA_GO) regdef(0x1, 4, SDC_FBT) regdef(0x1, 5, SDC_CSS) regdef(0x1, 6, SDC_STCIE) regdef(0x1, 7, SDC_STERRIE) regdef(0x1, 0, MIS_STC) regdef(0x1, 1, MIS_STERR) /* ------ BAR1 Register (MEM mapped) ---------------------------------------- */ #pragma pack(1) struct regdef_dma { uint32_t sar0; uint32_t sar1; uint32_t lr; uint32_t star; uint32_t stpr; uint32_t sr; uint32_t bntxr; /* TBNTS / RBNRR*/ }; #pragma pack() #pragma pack(1) struct regdef_common { uint32_t swrst; }; #pragma pack() /* -------------------------------------------------------------------------- */ /* ------ Global variables -------------------------------------------------- */ /* -------------------------------------------------------------------------- */ struct priv_data { struct pci_dev *pci; struct spi_master *spi_master; struct spi_device *spi[NUM_CHIPSELECT]; struct { struct regdef_spi __iomem *spi; struct regdef_dma __iomem *txdma; struct regdef_dma __iomem *rxdma; struct regdef_common __iomem *common; } reg; /* Used for OP-Code-Field transfer mode */ const uint8_t *tx_buf; uint8_t *rx_buf; uint32_t tx_len; uint32_t rx_len; /* Used for DMA transfer mode */ struct sg_table tx_sgt; struct sg_table rx_sgt; struct { uint32_t use_opcf : 1; /* OP-Code field usage */ uint32_t use_dma : 1; /* DMA usage */ } flags; }; /* -------------------------------------------------------------------------- */ /* ------ Macros ------------------------------------------------------------ */ /* -------------------------------------------------------------------------- */ #define iomod8(cmask, smask, addr) \ do { \ uint8_t val = ioread8(addr); \ val &= ~(cmask); \ val |= (smask); \ iowrite8(val, addr); \ } while (0) #define iomod32(cmask, smask, addr) \ do { \ uint32_t val = ioread32(addr); \ val &= ~(cmask); \ val |= (smask); \ iowrite32(val, addr); \ } while (0) /* -------------------------------------------------------------------------- */ /* ------ Help functions ---------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /** * ax99100_pci_spi_set_frequency: Set requested transfer frequency. * * @spi: Pointer to the device structure which provides information about the slave device. * @freq: Requested SPI frequency which should be configured. */ static void ax99100_pci_spi_set_frequency(struct spi_device *spi, uint32_t freq) { struct priv_data *pd = spi_master_get_devdata(spi->master); if (freq > spi->max_speed_hz) dev_warn(&spi->dev, "The requested frequency (%dkHz) exceeds the defined max. frequency (%dkHz) of the SPI device.", freq/1000, spi->max_speed_hz/1000); iomod8(sCSS_DIVEN(1), 0, &pd->reg.spi->css); iomod8(sBR_DIVIDER(-1), sBR_DIVIDER(DIV_ROUND_UP(125000000, freq)), &pd->reg.spi->br); iomod8(0, sCSS_DIVEN(1), &pd->reg.spi->css); } /** * ax99100_pci_spi_set_mode: Set requested SPI transfer mode. * * @spi: Pointer to the device structure which provides information about the slave device. * @mode: Requested SPI mode which should be configured. */ static void ax99100_pci_spi_set_mode(struct spi_device *spi, uint32_t mode) { struct priv_data *pd = spi_master_get_devdata(spi->master); iomod8(sCM_CPHA(-1) | sCM_CPOL(-1), sCM_CPHA(!!(mode & SPI_CPHA)) | sCM_CPOL(!!(mode & SPI_CPOL)), &pd->reg.spi->cm); } /** * ax99100_pci_spi_enable_irq: Enable IRQs of SPI controller. * * @sm: Pointer to the spi_master structure which provides information about the controller. */ static void ax99100_pci_spi_enable_irq(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); iomod8(0, sSDC_STCIE(1) | sSDC_STERRIE(1), &pd->reg.spi->sdc); } /** * ax99100_pci_spi_disable_irq: Disable IRQs of SPI controller. * * @sm: Pointer to the spi_master structure which provides information about the controller. */ static void ax99100_pci_spi_disable_irq(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); iomod8(sSDC_STCIE(1) | sSDC_STERRIE(1), 0, &pd->reg.spi->sdc); } /* -------------------------------------------------------------------------- */ /* ------ DMA functions ----------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /** * ax99100_pci_spi_do_dma_transfer: Processing the SPI DMA transfer. * * @sm: Pointer to the spi_master structure which provides information about the controller. * * Return: Always 0 */ static inline void ax99100_pci_spi_do_dma_transfer(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); struct scatterlist *rx_sgl, *tx_sgl; if (!pd->rx_sgt.nents && !pd->tx_sgt.nents) { pd->flags.use_dma = 0; /* Disable IRQs */ ax99100_pci_spi_disable_irq(sm); /* Wake up the framework */ spi_finalize_current_transfer(sm); return; } pd->flags.use_dma = 1; if (pd->rx_sgt.nents--) { rx_sgl = pd->rx_sgt.sgl++; iowrite32(lower_32_bits(sg_dma_address(rx_sgl)), &pd->reg.rxdma->sar0); /* Set DMA lower 32-bit addr */ iowrite32(upper_32_bits(sg_dma_address(rx_sgl)), &pd->reg.rxdma->sar1); /* Set DMA upper 32-bit addr */ iowrite32(sg_dma_len(rx_sgl), &pd->reg.rxdma->lr); /* Set DMA transfer length */ iowrite32(0x1, &pd->reg.rxdma->star); /* Enable RX DMA*/ iomod8(0, sSDC_ERDMA(1), &pd->reg.spi->sdc); } if (pd->tx_sgt.nents--) { tx_sgl = pd->tx_sgt.sgl++; iowrite32(lower_32_bits(sg_dma_address(tx_sgl)), &pd->reg.txdma->sar0); /* Set DMA lower 32-bit addr */ iowrite32(upper_32_bits(sg_dma_address(tx_sgl)), &pd->reg.txdma->sar1); /* Set DMA upper 32-bit addr */ iowrite32(sg_dma_len(tx_sgl), &pd->reg.txdma->lr); /* Set DMA transfer length */ iowrite32(0x1, &pd->reg.txdma->star); /* Enable TX DMA*/ iomod8(0, sSDC_ETDMA(1), &pd->reg.spi->sdc); } ax99100_pci_spi_enable_irq(sm); /* Start DMA processing */ iomod8(0, sSDC_DMA_GO(1), &pd->reg.spi->sdc); } /* -------------------------------------------------------------------------- */ /* ------ OPCF functions ---------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /** * ax99100_pci_spi_read_fifo: Read as many bytes as possible from STOF registers. * * @sm: Pointer to the spi_master structure which provides information about the controller. */ static inline void ax99100_pci_spi_read_fifo(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); uint8_t nbytes = min_t(uint32_t, pd->rx_len, 8); uint8_t byte, i = 0; if (!nbytes) return; /* Read STOF data */ while (nbytes-- && pd->rx_len--) { byte = ioread8(&pd->reg.spi->stof[i++]); if (pd->rx_buf) *pd->rx_buf++ = byte; } } /** * ax99100_pci_spi_write_fifo: Write as many bytes as possible to STOF registers. * * @sm: Pointer to the spi_master structure which provides information about the controller. */ static inline void ax99100_pci_spi_write_fifo(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); uint8_t nbytes = min_t(uint32_t, pd->tx_len, 8); uint8_t byte, i = 0; if (!nbytes) { pd->flags.use_opcf = 0; /* Disable IRQs */ ax99100_pci_spi_disable_irq(sm); /* Wake up the framework */ spi_finalize_current_transfer(sm); return; } pd->flags.use_opcf = 1; /* Set STOF length */ iomod8(sSSOL_STOL(-1), sSSOL_STOL(nbytes - 1), &pd->reg.spi->ssol); /* Write STOF data */ while (nbytes-- && pd->tx_len--) { byte = pd->tx_buf ? *pd->tx_buf++ : 0; iowrite8(byte, &pd->reg.spi->stof[i++]); } ax99100_pci_spi_enable_irq(sm); /* Start OPCF processing */ iomod8(0, sSDC_OPCFE(1) | sSDC_DMA_GO(1), &pd->reg.spi->sdc); } /* -------------------------------------------------------------------------- */ /* ------ API functions of SPI master --------------------------------------- */ /* -------------------------------------------------------------------------- */ /** * ax99100_pci_spi_can_dma: Checks for a possible dma transfer. * * @sm: Pointer to the spi_master structure which provides information about the controller. * @spi: Pointer to the device structure which provides information about the slave device. * @transfer: Pointer to the spi_transfer structure which provide information about next transfer parameters * * Return: true, if a dma transfer is possible; otherwise false */ static bool ax99100_pci_spi_can_dma(struct spi_master *sm, struct spi_device *spi, struct spi_transfer *transfer) { if (transfer->len < DMA_MIN_SIZE) return false; return true; } /** * ax99100_pci_spi_set_cs: Select or deselect the chip select line. * * @spi: Pointer to the spi_device structure * @deselect: Select(0) or deselect (1) the chip select line */ static void ax99100_pci_spi_set_cs(struct spi_device *spi, bool deselect) { struct priv_data *pd = spi_master_get_devdata(spi->master); /* NOTE: * To prevent toggling of SCLK and MOSI/MISO, * we do the SPI mode selection in front of processing the chip select. */ ax99100_pci_spi_set_mode(spi, spi->mode); if (gSSOL_EDE(ioread8(&pd->reg.spi->ssol))) iomod8(sSSOL_SS(-1), sSSOL_SS((deselect) ? (-1) : (spi->chip_select)), &pd->reg.spi->ssol); else iomod8(sSSOL_SS(-1), sSSOL_SS((deselect) ? (-1) : ~(1 << spi->chip_select)), &pd->reg.spi->ssol); } /** * ax99100_pci_spi_transfer_one: Initiates the SPI transfer. * * @sm: Pointer to the spi_master structure which provides information about the controller. * @spi: Pointer to the spi_device structure * @transfer: Pointer to the spi_transfer structure which provide information about next transfer parameters * * Return: Always 1 to signal that the transfer is still in progress */ static int ax99100_pci_spi_transfer_one(struct spi_master *sm, struct spi_device *spi, struct spi_transfer *transfer) { struct priv_data *pd = spi_master_get_devdata(sm); ax99100_pci_spi_set_frequency(spi, transfer->speed_hz); /* MSB or LSB first? */ iomod8(sCM_LSB(-1), sCM_LSB((spi->mode & SPI_LSB_FIRST) ? 1 : 0), &pd->reg.spi->cm); if ((transfer->rx_sg.nents > 0) || (transfer->tx_sg.nents > 0)) { /* As we modify the sg_table structures while processing, * we creates private copies of these! */ pd->rx_sgt = transfer->rx_sg; pd->tx_sgt = transfer->tx_sg; /* Start DMA transfer */ ax99100_pci_spi_do_dma_transfer(sm); } else { /* As we modify the buffer pointers and length fields while processing, * we creates private copies of these! */ pd->rx_buf = transfer->rx_buf; pd->tx_buf = transfer->tx_buf; pd->rx_len = transfer->len; pd->tx_len = transfer->len; /* Start OPCF transfer */ ax99100_pci_spi_write_fifo(sm); } return 1; } /* -------------------------------------------------------------------------- */ /* ------ IRQ functions ----------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /** * ax99100_pci_spi_isr: Interrupt subroutine of SPI controller. * * @irq: IRQ number * @dev_id: Pointer to the spi_master structure which provides information about the controller. * * Return: IRQ_HANDLED on success; error value otherwise */ static irqreturn_t ax99100_pci_spi_isr(int irq, void *dev_id) { struct spi_master *sm = dev_id; struct priv_data *pd = spi_master_get_devdata(sm); uint8_t status; status = ioread8(&pd->reg.spi->mis); if (status & sMIS_STC(1)) { if (pd->flags.use_dma) ax99100_pci_spi_do_dma_transfer(sm); if (pd->flags.use_opcf) { /* Read as many bytes as possible from OPCF */ ax99100_pci_spi_read_fifo(sm); /* Write as many bytes as possible to OPCF */ ax99100_pci_spi_write_fifo(sm); } } if (status & sMIS_STERR(1)) { dev_err(&sm->cur_msg->spi->dev, "SPI transceiver error interrupt occurred\n"); /* TODO: Implementation of error handling! */ } iowrite8(status, &pd->reg.spi->mis); return IRQ_HANDLED; } /* -------------------------------------------------------------------------- */ /* ------ Driver / Module functions ----------------------------------------- */ /* -------------------------------------------------------------------------- */ struct spi_board_info spi_slave_devices[NUM_CHIPSELECT] = {0}; #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) module_param_named(mode0, spi_slave_devices[0].mode, uint, 0644); module_param_named(mode1, spi_slave_devices[1].mode, uint, 0644); module_param_named(mode2, spi_slave_devices[2].mode, uint, 0644); #else module_param_named(mode0, spi_slave_devices[0].mode, ushort, 0644); module_param_named(mode1, spi_slave_devices[1].mode, ushort, 0644); module_param_named(mode2, spi_slave_devices[2].mode, ushort, 0644); #endif module_param_string(modalias0, spi_slave_devices[0].modalias, sizeof(spi_slave_devices[0].modalias), 0644); module_param_named(max_speed_hz0, spi_slave_devices[0].max_speed_hz, uint, 0644); module_param_string(modalias1, spi_slave_devices[1].modalias, sizeof(spi_slave_devices[0].modalias), 0644); module_param_named(max_speed_hz1, spi_slave_devices[1].max_speed_hz, uint, 0644); module_param_string(modalias2, spi_slave_devices[2].modalias, sizeof(spi_slave_devices[0].modalias), 0644); module_param_named(max_speed_hz2, spi_slave_devices[2].max_speed_hz, uint, 0644); /** * ax99100_pci_spi_new_devices: Add optional SPI slave devices defined by module parameters. */ static void ax99100_pci_spi_new_devices(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); int i; for (i = 0; i < NUM_CHIPSELECT; i++) { if (!strlen(spi_slave_devices[i].modalias)) continue; spi_slave_devices[i].chip_select = i; pd->spi[i] = spi_new_device(sm, &spi_slave_devices[i]); if (!pd->spi[i]) dev_info(&pd->pci->dev, "Add SPI slave (cs=%i, modalias=%s, mode=%d, max_speed_hz=%d) failed!\n", i, spi_slave_devices[i].modalias, spi_slave_devices[i].mode, spi_slave_devices[i].max_speed_hz); else dev_info(&pd->pci->dev, "SPI slave (cs=%i, modalias=%s, mode=%d, max_speed_hz=%d) successfully added.\n", i, spi_slave_devices[i].modalias, spi_slave_devices[i].mode, spi_slave_devices[i].max_speed_hz); } } /** * ax99100_pci_spi_unregister_devices: Unregister optional SPI slave devices defined by module parameters. */ static void ax99100_pci_spi_unregister_devices(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); int i; for (i = 0; i < NUM_CHIPSELECT; i++) { if (pd->spi[i]) { spi_unregister_device(pd->spi[i]); pd->spi[i] = NULL; } } } /** * ax99100_pci_spi_chip_init: Initialize the SPI controller chip. * * @sm: Pointer to the spi_master structure which provides information about the controller. */ static void ax99100_pci_spi_chip_init(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); /* Reset chip function */ iowrite32(0x1, &pd->reg.common->swrst); mdelay(10); /* Use internal 125MHz PLL clock as clock source */ iomod8(sCSS_SPICKS(-1), sCSS_SPICKS(0), &pd->reg.spi->css); /* Enable SPI controller and slave select pins */ iowrite8(sCM_SPIMEN(1) | sCM_SSOE(1), &pd->reg.spi->cm); } /** * ax99100_pci_spi_chip_dinit: Deinitialize the SPI controller chip. * * @sm: Pointer to the spi_master structure which provides information about the controller. */ static void ax99100_pci_spi_chip_deinit(struct spi_master *sm) { struct priv_data *pd = spi_master_get_devdata(sm); /* Reset chip function */ iowrite32(0x1, &pd->reg.common->swrst); mdelay(10); } /** * ax99100_pci_spi_probe: * * @pci: Pointer to the pci device of spi master. * @pci_id: * * Return: 0 on success; error value otherwise */ static int ax99100_pci_spi_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct device *dev = &pci->dev; struct priv_data *pd; struct spi_master *sm; int err; sm = spi_alloc_master(dev, sizeof(*pd)); if (!sm) return -ENOMEM; pd = spi_master_get_devdata(sm); pd->spi_master = sm; pci_set_drvdata(pci, pd); pd->pci = pci; /* Enable PCI device, request regions and enable bus-master for DMA transfers */ err = pci_enable_device(pci); if (err) { dev_err(dev, "Enable PCI device failed!\n"); goto err0; } err = pci_request_regions(pci, dev_name(dev)); if (err) { dev_info(dev, "Request PCI regions failed!\n"); goto err1; } err = dma_set_mask(&pci->dev, DMA_BIT_MASK(64)); if (err) { dev_info(dev, "Set DMA mask failed!\n"); goto err2; } pci_set_master(pci); /* Map required IO regions */ pd->reg.spi = pci_iomap_range(pci, 0, 0x0, sizeof(*pd->reg.spi)); if (!pd->reg.spi) { dev_err(dev, "Map PCI IO bar0 failed (spi)!\n"); err = -ENODEV; goto err5; } /* Map required memory regions */ pd->reg.txdma = pci_iomap_range(pci, 1, 0x80, sizeof(*pd->reg.txdma)); if (!pd->reg.txdma) { dev_err(dev, "Map PCI memory bar1 failed (txdma)!\n"); err = -ENODEV; goto err2; } pd->reg.rxdma = pci_iomap_range(pci, 1, 0x100, sizeof(*pd->reg.rxdma)); if (!pd->reg.rxdma) { dev_err(dev, "Map PCI memory bar1 failed (rxdma)!\n"); err = -ENODEV; goto err3; } pd->reg.common = pci_iomap_range(pci, 1, 0x238, sizeof(*pd->reg.common)); if (!pd->reg.common) { dev_err(dev, "Map PCI memory bar1 failed (common)!\n"); err = -ENODEV; goto err4; } /* Install IRQ handler */ err = devm_request_irq(dev, pci->irq, ax99100_pci_spi_isr, IRQF_SHARED, KBUILD_MODNAME, sm); if (err) { dev_err(dev, "Register IRQ%d failed!\n", pci->irq); goto err6; } /* Configure the SPI master structure */ sm->dev.of_node = dev->of_node; sm->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; sm->bits_per_word_mask = SPI_BPW_MASK(8); sm->max_speed_hz = MAX_SPEED_HZ; sm->min_speed_hz = MIN_SPEED_HZ; sm->num_chipselect = NUM_CHIPSELECT; sm->set_cs = ax99100_pci_spi_set_cs; sm->transfer_one = ax99100_pci_spi_transfer_one; sm->can_dma = ax99100_pci_spi_can_dma; sm->max_dma_len = DMA_MAX_SIZE; ax99100_pci_spi_chip_init(sm); err = devm_spi_register_master(dev, sm); if (err) { dev_err(dev, "Register SPI master failed!\n"); goto err7; } dev_info(dev, "%s successfully initialized!\n", dev_name(&sm->dev)); /* Add optional SPI devices defined by module parameters */ ax99100_pci_spi_new_devices(sm); return 0; err7: ax99100_pci_spi_chip_deinit(sm); err6: pci_iounmap(pci, pd->reg.spi); err5: pci_iounmap(pci, pd->reg.common); err4: pci_iounmap(pci, pd->reg.rxdma); err3: pci_iounmap(pci, pd->reg.txdma); err2: pci_clear_master(pci); pci_release_regions(pci); err1: pci_disable_device(pci); err0: spi_master_put(sm); return err; } /** * ax99100_pci_spi_remove: * * @pci: */ static void ax99100_pci_spi_remove(struct pci_dev *pci) { struct priv_data *pd = pci_get_drvdata(pci); struct spi_master *sm = pd->spi_master; ax99100_pci_spi_unregister_devices(sm); ax99100_pci_spi_chip_deinit(sm); pci_iounmap(pci, pd->reg.spi); pci_iounmap(pci, pd->reg.common); pci_iounmap(pci, pd->reg.rxdma); pci_iounmap(pci, pd->reg.txdma); pci_clear_master(pci); pci_release_regions(pci); pci_disable_device(pci); dev_info(&pci->dev, "%s successfully removed!\n", dev_name(&sm->dev)); } /* ------------------------------------------------------------------------- */ static const struct pci_device_id ax99100_pci_spi_devices[] = { { PCI_DEVICE_SUB(HILSCHER_PCI_VENDOR_ID, HILSCHER_PCI_DEVICE_ID, HILSCHER_PCI_SUB_VENDOR_ID, HILSCHER_PCI_SUB_DEVICE_ID_SPI)}, { }, }; MODULE_DEVICE_TABLE(pci, ax99100_pci_spi_devices); /* ------------------------------------------------------------------------- */ static struct pci_driver ax99100_pci_spi_driver = { .name = DRIVER_NAME, .id_table = ax99100_pci_spi_devices, .probe = ax99100_pci_spi_probe, .remove = ax99100_pci_spi_remove, }; static int __init ax99100_pci_spi_init(void) { pr_info("%s: %s\n", DRIVER_NAME, DRIVER_DESC); return pci_register_driver(&ax99100_pci_spi_driver); } module_init(ax99100_pci_spi_init); static void __exit ax99100_pci_spi_exit(void) { pci_unregister_driver(&ax99100_pci_spi_driver); } module_exit(ax99100_pci_spi_exit); MODULE_AUTHOR("Hilscher Gesellschaft fuer Systemautomation mbH"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL v2");