diff options
Diffstat (limited to 'drivers/staging/kpc2000')
23 files changed, 2389 insertions, 2795 deletions
diff --git a/drivers/staging/kpc2000/Kconfig b/drivers/staging/kpc2000/Kconfig index 3bb2efd511c4..897965359fcb 100644 --- a/drivers/staging/kpc2000/Kconfig +++ b/drivers/staging/kpc2000/Kconfig @@ -23,7 +23,7 @@ config KPC2000_CORE If unsure, say N. config KPC2000_SPI - tristate "Kaktronics KPC SPI device" + tristate "Daktronics KPC SPI device" depends on KPC2000 && SPI help Say Y here if you wish to support the Daktronics KPC PCI @@ -35,7 +35,7 @@ config KPC2000_SPI If unsure, say N. config KPC2000_I2C - tristate "Kaktronics KPC I2C device" + tristate "Daktronics KPC I2C device" depends on KPC2000 && I2C help Say Y here if you wish to support the Daktronics KPC PCI diff --git a/drivers/staging/kpc2000/Makefile b/drivers/staging/kpc2000/Makefile index 1e48e9df1329..d15ed49807d5 100644 --- a/drivers/staging/kpc2000/Makefile +++ b/drivers/staging/kpc2000/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_KPC2000) += kpc2000/ -obj-$(CONFIG_KPC2000_I2C) += kpc_i2c/ -obj-$(CONFIG_KPC2000_SPI) += kpc_spi/ +obj-$(CONFIG_KPC2000_I2C) += kpc2000_i2c.o +obj-$(CONFIG_KPC2000_SPI) += kpc2000_spi.o obj-$(CONFIG_KPC2000_DMA) += kpc_dma/ diff --git a/drivers/staging/kpc2000/TODO b/drivers/staging/kpc2000/TODO index 8c7af29fefae..9b5ab37fb3a0 100644 --- a/drivers/staging/kpc2000/TODO +++ b/drivers/staging/kpc2000/TODO @@ -1,8 +1,2 @@ - the kpc_spi driver doesn't seem to let multiple transactions (to different instances of the core) happen in parallel... - The kpc_i2c driver is a hot mess, it should probably be cleaned up a ton. It functions against current hardware though. -- pcard->card_num in kp2000_pcie_probe() is a global variable and needs atomic / locking / something better. -- probe_core_uio() probably needs error handling -- the loop in kp2000_probe_cores() that uses probe_core_uio() also probably needs error handling -- would be nice if the AIO fileops in kpc_dma could be made to work - - probably want to add a CONFIG_ option to control compilation of the AIO functions -- if the AIO fileops in kpc_dma start working, next would be making iov_count > 1 work too diff --git a/drivers/staging/kpc2000/kpc2000/Makefile b/drivers/staging/kpc2000/kpc2000/Makefile index 28ab1e185f9f..c274ad083db6 100644 --- a/drivers/staging/kpc2000/kpc2000/Makefile +++ b/drivers/staging/kpc2000/kpc2000/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-m := kpc2000.o -kpc2000-objs += kp2000_module.o core.o cell_probe.o fileops.o +kpc2000-objs += core.o cell_probe.o diff --git a/drivers/staging/kpc2000/kpc2000/cell_probe.c b/drivers/staging/kpc2000/kpc2000/cell_probe.c index e0dba91e7fa8..c124a836db27 100644 --- a/drivers/staging/kpc2000/kpc2000/cell_probe.c +++ b/drivers/staging/kpc2000/kpc2000/cell_probe.c @@ -4,7 +4,6 @@ #include <linux/types.h> #include <linux/export.h> #include <linux/slab.h> -#include <asm/io.h> #include <linux/io.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/mfd/core.h> @@ -25,7 +24,7 @@ * D C2S DMA Present * DDD C2S DMA Channel Number [up to 8 channels] * II IRQ Count [0 to 3 IRQs per core] - 1111111000 + * 1111111000 * IIIIIII IRQ Base Number [up to 128 IRQs per card] * ___ Spare * @@ -40,166 +39,192 @@ #define KP_CORE_ID_SPI 5 struct core_table_entry { - u16 type; - u32 offset; - u32 length; - bool s2c_dma_present; - u8 s2c_dma_channel_num; - bool c2s_dma_present; - u8 c2s_dma_channel_num; - u8 irq_count; - u8 irq_base_num; + u16 type; + u32 offset; + u32 length; + bool s2c_dma_present; + u8 s2c_dma_channel_num; + bool c2s_dma_present; + u8 c2s_dma_channel_num; + u8 irq_count; + u8 irq_base_num; }; static void parse_core_table_entry_v0(struct core_table_entry *cte, const u64 read_val) { - cte->type = ((read_val & 0xFFF0000000000000) >> 52); - cte->offset = ((read_val & 0x00000000FFFF0000) >> 16) * 4096; - cte->length = ((read_val & 0x0000FFFF00000000) >> 32) * 8; - cte->s2c_dma_present = ((read_val & 0x0008000000000000) >> 51); - cte->s2c_dma_channel_num = ((read_val & 0x0007000000000000) >> 48); - cte->c2s_dma_present = ((read_val & 0x0000000000008000) >> 15); - cte->c2s_dma_channel_num = ((read_val & 0x0000000000007000) >> 12); - cte->irq_count = ((read_val & 0x0000000000000C00) >> 10); - cte->irq_base_num = ((read_val & 0x00000000000003F8) >> 3); + cte->type = ((read_val & 0xFFF0000000000000) >> 52); + cte->offset = ((read_val & 0x00000000FFFF0000) >> 16) * 4096; + cte->length = ((read_val & 0x0000FFFF00000000) >> 32) * 8; + cte->s2c_dma_present = ((read_val & 0x0008000000000000) >> 51); + cte->s2c_dma_channel_num = ((read_val & 0x0007000000000000) >> 48); + cte->c2s_dma_present = ((read_val & 0x0000000000008000) >> 15); + cte->c2s_dma_channel_num = ((read_val & 0x0000000000007000) >> 12); + cte->irq_count = ((read_val & 0x0000000000000C00) >> 10); + cte->irq_base_num = ((read_val & 0x00000000000003F8) >> 3); } static void dbg_cte(struct kp2000_device *pcard, struct core_table_entry *cte) { - dev_dbg(&pcard->pdev->dev, "CTE: type:%3d offset:%3d (%3d) length:%3d (%3d) s2c:%d c2s:%d irq_count:%d base_irq:%d\n", - cte->type, - cte->offset, - cte->offset / 4096, - cte->length, - cte->length / 8, - (cte->s2c_dma_present ? cte->s2c_dma_channel_num : -1), - (cte->c2s_dma_present ? cte->c2s_dma_channel_num : -1), - cte->irq_count, - cte->irq_base_num - ); + dev_dbg(&pcard->pdev->dev, "CTE: type:%3d offset:%3d (%3d) length:%3d (%3d) s2c:%d c2s:%d irq_count:%d base_irq:%d\n", + cte->type, + cte->offset, + cte->offset / 4096, + cte->length, + cte->length / 8, + (cte->s2c_dma_present ? cte->s2c_dma_channel_num : -1), + (cte->c2s_dma_present ? cte->c2s_dma_channel_num : -1), + cte->irq_count, + cte->irq_base_num + ); } static void parse_core_table_entry(struct core_table_entry *cte, const u64 read_val, const u8 entry_rev) { switch (entry_rev) { - case 0: parse_core_table_entry_v0(cte, read_val); break; - default: cte->type = 0; break; + case 0: + parse_core_table_entry_v0(cte, read_val); + break; + default: + cte->type = 0; + break; } } - -int probe_core_basic(unsigned int core_num, struct kp2000_device *pcard, char *name, const struct core_table_entry cte) +static int probe_core_basic(unsigned int core_num, struct kp2000_device *pcard, + char *name, const struct core_table_entry cte) { - struct mfd_cell cell = {0}; - struct resource resources[2]; - - struct kpc_core_device_platdata core_pdata = { - .card_id = pcard->card_id, - .build_version = pcard->build_version, - .hardware_revision = pcard->hardware_revision, - .ssid = pcard->ssid, - .ddna = pcard->ddna, - }; - - dev_dbg(&pcard->pdev->dev, "Found Basic core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8); - - - cell.platform_data = &core_pdata; - cell.pdata_size = sizeof(struct kpc_core_device_platdata); - cell.name = name; - cell.id = core_num; - cell.num_resources = 2; - - memset(&resources, 0, sizeof(resources)); - - resources[0].start = cte.offset; - resources[0].end = cte.offset + (cte.length - 1); - resources[0].flags = IORESOURCE_MEM; - - resources[1].start = pcard->pdev->irq; - resources[1].end = pcard->pdev->irq; - resources[1].flags = IORESOURCE_IRQ; - - cell.resources = resources; - - return mfd_add_devices( - PCARD_TO_DEV(pcard), // parent - pcard->card_num * 100, // id - &cell, // struct mfd_cell * - 1, // ndevs - &pcard->regs_base_resource, - 0, // irq_base - NULL // struct irq_domain * - ); + struct mfd_cell cell = { .id = core_num, .name = name }; + struct resource resources[2]; + + struct kpc_core_device_platdata core_pdata = { + .card_id = pcard->card_id, + .build_version = pcard->build_version, + .hardware_revision = pcard->hardware_revision, + .ssid = pcard->ssid, + .ddna = pcard->ddna, + }; + + dev_dbg(&pcard->pdev->dev, "Found Basic core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8); + + cell.platform_data = &core_pdata; + cell.pdata_size = sizeof(struct kpc_core_device_platdata); + cell.num_resources = 2; + + memset(&resources, 0, sizeof(resources)); + + resources[0].start = cte.offset; + resources[0].end = cte.offset + (cte.length - 1); + resources[0].flags = IORESOURCE_MEM; + + resources[1].start = pcard->pdev->irq; + resources[1].end = pcard->pdev->irq; + resources[1].flags = IORESOURCE_IRQ; + + cell.resources = resources; + + return mfd_add_devices(PCARD_TO_DEV(pcard), // parent + pcard->card_num * 100, // id + &cell, // struct mfd_cell * + 1, // ndevs + &pcard->regs_base_resource, + 0, // irq_base + NULL); // struct irq_domain * } - struct kpc_uio_device { - struct list_head list; - struct kp2000_device *pcard; - struct device *dev; - struct uio_info uioinfo; - struct core_table_entry cte; - u16 core_num; + struct list_head list; + struct kp2000_device *pcard; + struct device *dev; + struct uio_info uioinfo; + struct core_table_entry cte; + u16 core_num; }; -static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t offset_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", kudev->cte.offset); +} +static DEVICE_ATTR_RO(offset); + +static ssize_t size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", kudev->cte.length); +} +static DEVICE_ATTR_RO(size); + +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", kudev->cte.type); +} +static DEVICE_ATTR_RO(type); + +static ssize_t s2c_dma_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + if (!kudev->cte.s2c_dma_present) + return sprintf(buf, "%s", "not present\n"); + + return sprintf(buf, "%u\n", kudev->cte.s2c_dma_channel_num); +} +static DEVICE_ATTR_RO(s2c_dma); + +static ssize_t c2s_dma_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + if (!kudev->cte.c2s_dma_present) + return sprintf(buf, "%s", "not present\n"); + + return sprintf(buf, "%u\n", kudev->cte.c2s_dma_channel_num); +} +static DEVICE_ATTR_RO(c2s_dma); + +static ssize_t irq_count_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct kpc_uio_device *kudev = dev_get_drvdata(dev); - - #define ATTR_NAME_CMP(v) (strcmp(v, attr->attr.name) == 0) - if ATTR_NAME_CMP("offset"){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.offset); - } else if ATTR_NAME_CMP("size"){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.length); - } else if ATTR_NAME_CMP("type"){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.type); - } - else if ATTR_NAME_CMP("s2c_dma"){ - if (kudev->cte.s2c_dma_present){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.s2c_dma_channel_num); - } else { - return scnprintf(buf, PAGE_SIZE, "not present\n"); - } - } else if ATTR_NAME_CMP("c2s_dma"){ - if (kudev->cte.c2s_dma_present){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.c2s_dma_channel_num); - } else { - return scnprintf(buf, PAGE_SIZE, "not present\n"); - } - } - else if ATTR_NAME_CMP("irq_count"){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.irq_count); - } else if ATTR_NAME_CMP("irq_base_num"){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.irq_base_num); - } else if ATTR_NAME_CMP("core_num"){ - return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->core_num); - } else { - return 0; - } - #undef ATTR_NAME_CMP + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", kudev->cte.irq_count); +} +static DEVICE_ATTR_RO(irq_count); + +static ssize_t irq_base_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", kudev->cte.irq_base_num); } +static DEVICE_ATTR_RO(irq_base_num); + +static ssize_t core_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kpc_uio_device *kudev = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", kudev->core_num); +} +static DEVICE_ATTR_RO(core_num); -DEVICE_ATTR(offset, 0444, show_attr, NULL); -DEVICE_ATTR(size, 0444, show_attr, NULL); -DEVICE_ATTR(type, 0444, show_attr, NULL); -DEVICE_ATTR(s2c_dma_ch, 0444, show_attr, NULL); -DEVICE_ATTR(c2s_dma_ch, 0444, show_attr, NULL); -DEVICE_ATTR(s2c_dma, 0444, show_attr, NULL); -DEVICE_ATTR(c2s_dma, 0444, show_attr, NULL); -DEVICE_ATTR(irq_count, 0444, show_attr, NULL); -DEVICE_ATTR(irq_base_num, 0444, show_attr, NULL); -DEVICE_ATTR(core_num, 0444, show_attr, NULL); -struct attribute * kpc_uio_class_attrs[] = { +struct attribute *kpc_uio_class_attrs[] = { &dev_attr_offset.attr, &dev_attr_size.attr, &dev_attr_type.attr, - &dev_attr_s2c_dma_ch.attr, - &dev_attr_c2s_dma_ch.attr, &dev_attr_s2c_dma.attr, &dev_attr_c2s_dma.attr, &dev_attr_irq_count.attr, @@ -208,264 +233,277 @@ struct attribute * kpc_uio_class_attrs[] = { NULL, }; - static int kp2000_check_uio_irq(struct kp2000_device *pcard, u32 irq_num) { - u64 interrupt_active = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); - u64 interrupt_mask_inv = ~readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); - u64 irq_check_mask = (1 << irq_num); - if (interrupt_active & irq_check_mask){ // if it's active (interrupt pending) - if (interrupt_mask_inv & irq_check_mask){ // and if it's not masked off - return 1; - } - } - return 0; + u64 interrupt_active = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); + u64 interrupt_mask_inv = ~readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); + u64 irq_check_mask = BIT_ULL(irq_num); + + if (interrupt_active & irq_check_mask) { // if it's active (interrupt pending) + if (interrupt_mask_inv & irq_check_mask) { // and if it's not masked off + return 1; + } + } + return 0; } static irqreturn_t kuio_handler(int irq, struct uio_info *uioinfo) { - struct kpc_uio_device *kudev = uioinfo->priv; - if (irq != kudev->pcard->pdev->irq) - return IRQ_NONE; - - if (kp2000_check_uio_irq(kudev->pcard, kudev->cte.irq_base_num)){ - writeq((1 << kudev->cte.irq_base_num), kudev->pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); // Clear the active flag - return IRQ_HANDLED; - } - return IRQ_NONE; + struct kpc_uio_device *kudev = uioinfo->priv; + + if (irq != kudev->pcard->pdev->irq) + return IRQ_NONE; + + if (kp2000_check_uio_irq(kudev->pcard, kudev->cte.irq_base_num)) { + /* Clear the active flag */ + writeq(BIT_ULL(kudev->cte.irq_base_num), + kudev->pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); + return IRQ_HANDLED; + } + return IRQ_NONE; } static int kuio_irqcontrol(struct uio_info *uioinfo, s32 irq_on) { - struct kpc_uio_device *kudev = uioinfo->priv; - struct kp2000_device *pcard = kudev->pcard; - u64 mask; - - lock_card(pcard); - mask = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); - if (irq_on){ - mask &= ~(1 << (kudev->cte.irq_base_num)); - } else { - mask |= (1 << (kudev->cte.irq_base_num)); - } - writeq(mask, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); - unlock_card(pcard); - - return 0; + struct kpc_uio_device *kudev = uioinfo->priv; + struct kp2000_device *pcard = kudev->pcard; + u64 mask; + + mutex_lock(&pcard->sem); + mask = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); + if (irq_on) + mask &= ~(BIT_ULL(kudev->cte.irq_base_num)); + else + mask |= BIT_ULL(kudev->cte.irq_base_num); + writeq(mask, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); + mutex_unlock(&pcard->sem); + + return 0; } -int probe_core_uio(unsigned int core_num, struct kp2000_device *pcard, char *name, const struct core_table_entry cte) +static int probe_core_uio(unsigned int core_num, struct kp2000_device *pcard, + char *name, const struct core_table_entry cte) { - struct kpc_uio_device *kudev; - int rv; - - dev_dbg(&pcard->pdev->dev, "Found UIO core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8); - - kudev = kzalloc(sizeof(struct kpc_uio_device), GFP_KERNEL); - if (!kudev){ - dev_err(&pcard->pdev->dev, "probe_core_uio: failed to kzalloc kpc_uio_device\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&kudev->list); - kudev->pcard = pcard; - kudev->cte = cte; - kudev->core_num = core_num; - - kudev->uioinfo.priv = kudev; - kudev->uioinfo.name = name; - kudev->uioinfo.version = "0.0"; - if (cte.irq_count > 0){ - kudev->uioinfo.irq_flags = IRQF_SHARED; - kudev->uioinfo.irq = pcard->pdev->irq; - kudev->uioinfo.handler = kuio_handler; - kudev->uioinfo.irqcontrol = kuio_irqcontrol; - } else { - kudev->uioinfo.irq = 0; - } - - kudev->uioinfo.mem[0].name = "uiomap"; - kudev->uioinfo.mem[0].addr = pci_resource_start(pcard->pdev, REG_BAR) + cte.offset; - kudev->uioinfo.mem[0].size = (cte.length + PAGE_SIZE-1) & ~(PAGE_SIZE-1); // Round up to nearest PAGE_SIZE boundary - kudev->uioinfo.mem[0].memtype = UIO_MEM_PHYS; - - kudev->dev = device_create(kpc_uio_class, &pcard->pdev->dev, MKDEV(0,0), kudev, "%s.%d.%d.%d", kudev->uioinfo.name, pcard->card_num, cte.type, kudev->core_num); - if (IS_ERR(kudev->dev)) { - dev_err(&pcard->pdev->dev, "probe_core_uio device_create failed!\n"); - return -ENODEV; - } - dev_set_drvdata(kudev->dev, kudev); - - rv = uio_register_device(kudev->dev, &kudev->uioinfo); - if (rv){ - dev_err(&pcard->pdev->dev, "probe_core_uio failed uio_register_device: %d\n", rv); - return rv; - } - - list_add_tail(&kudev->list, &pcard->uio_devices_list); - - return 0; -} + struct kpc_uio_device *kudev; + int rv; + + dev_dbg(&pcard->pdev->dev, "Found UIO core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8); + + kudev = kzalloc(sizeof(*kudev), GFP_KERNEL); + if (!kudev) + return -ENOMEM; + + INIT_LIST_HEAD(&kudev->list); + kudev->pcard = pcard; + kudev->cte = cte; + kudev->core_num = core_num; + + kudev->uioinfo.priv = kudev; + kudev->uioinfo.name = name; + kudev->uioinfo.version = "0.0"; + if (cte.irq_count > 0) { + kudev->uioinfo.irq_flags = IRQF_SHARED; + kudev->uioinfo.irq = pcard->pdev->irq; + kudev->uioinfo.handler = kuio_handler; + kudev->uioinfo.irqcontrol = kuio_irqcontrol; + } else { + kudev->uioinfo.irq = 0; + } + + kudev->uioinfo.mem[0].name = "uiomap"; + kudev->uioinfo.mem[0].addr = pci_resource_start(pcard->pdev, REG_BAR) + cte.offset; + kudev->uioinfo.mem[0].size = (cte.length + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); // Round up to nearest PAGE_SIZE boundary + kudev->uioinfo.mem[0].memtype = UIO_MEM_PHYS; + + kudev->dev = device_create(kpc_uio_class, &pcard->pdev->dev, MKDEV(0, 0), kudev, "%s.%d.%d.%d", kudev->uioinfo.name, pcard->card_num, cte.type, kudev->core_num); + if (IS_ERR(kudev->dev)) { + dev_err(&pcard->pdev->dev, "%s: device_create failed!\n", + __func__); + kfree(kudev); + return -ENODEV; + } + dev_set_drvdata(kudev->dev, kudev); + + rv = uio_register_device(kudev->dev, &kudev->uioinfo); + if (rv) { + dev_err(&pcard->pdev->dev, "%s: failed uio_register_device: %d\n", + __func__, rv); + put_device(kudev->dev); + kfree(kudev); + return rv; + } + list_add_tail(&kudev->list, &pcard->uio_devices_list); + + return 0; +} static int create_dma_engine_core(struct kp2000_device *pcard, size_t engine_regs_offset, int engine_num, int irq_num) { - struct mfd_cell cell = {0}; - struct resource resources[2]; - - dev_dbg(&pcard->pdev->dev, "create_dma_core(pcard = [%p], engine_regs_offset = %zx, engine_num = %d)\n", pcard, engine_regs_offset, engine_num); - - cell.platform_data = NULL; - cell.pdata_size = 0; - cell.id = engine_num; - cell.name = KP_DRIVER_NAME_DMA_CONTROLLER; - cell.num_resources = 2; - - memset(&resources, 0, sizeof(resources)); - - resources[0].start = engine_regs_offset; - resources[0].end = engine_regs_offset + (KPC_DMA_ENGINE_SIZE - 1); - resources[0].flags = IORESOURCE_MEM; - - resources[1].start = irq_num; - resources[1].end = irq_num; - resources[1].flags = IORESOURCE_IRQ; - - cell.resources = resources; - - return mfd_add_devices( - PCARD_TO_DEV(pcard), // parent - pcard->card_num * 100, // id - &cell, // struct mfd_cell * - 1, // ndevs - &pcard->dma_base_resource, - 0, // irq_base - NULL // struct irq_domain * - ); + struct mfd_cell cell = { .id = engine_num }; + struct resource resources[2]; + + cell.platform_data = NULL; + cell.pdata_size = 0; + cell.name = KP_DRIVER_NAME_DMA_CONTROLLER; + cell.num_resources = 2; + + memset(&resources, 0, sizeof(resources)); + + resources[0].start = engine_regs_offset; + resources[0].end = engine_regs_offset + (KPC_DMA_ENGINE_SIZE - 1); + resources[0].flags = IORESOURCE_MEM; + + resources[1].start = irq_num; + resources[1].end = irq_num; + resources[1].flags = IORESOURCE_IRQ; + + cell.resources = resources; + + return mfd_add_devices(PCARD_TO_DEV(pcard), // parent + pcard->card_num * 100, // id + &cell, // struct mfd_cell * + 1, // ndevs + &pcard->dma_base_resource, + 0, // irq_base + NULL); // struct irq_domain * } static int kp2000_setup_dma_controller(struct kp2000_device *pcard) { - int err; - unsigned int i; - u64 capabilities_reg; - - // S2C Engines - for (i = 0 ; i < 32 ; i++){ - capabilities_reg = readq( pcard->dma_bar_base + KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i) ); - if (capabilities_reg & ENGINE_CAP_PRESENT_MASK){ - err = create_dma_engine_core(pcard, (KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), i, pcard->pdev->irq); - if (err) goto err_out; - } - } - // C2S Engines - for (i = 0 ; i < 32 ; i++){ - capabilities_reg = readq( pcard->dma_bar_base + KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i) ); - if (capabilities_reg & ENGINE_CAP_PRESENT_MASK){ - err = create_dma_engine_core(pcard, (KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), 32+i, pcard->pdev->irq); - if (err) goto err_out; - } - } - - return 0; - + int err; + unsigned int i; + u64 capabilities_reg; + + // S2C Engines + for (i = 0 ; i < 32 ; i++) { + capabilities_reg = readq(pcard->dma_bar_base + KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)); + if (capabilities_reg & ENGINE_CAP_PRESENT_MASK) { + err = create_dma_engine_core(pcard, (KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), i, pcard->pdev->irq); + if (err) + goto err_out; + } + } + // C2S Engines + for (i = 0 ; i < 32 ; i++) { + capabilities_reg = readq(pcard->dma_bar_base + KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)); + if (capabilities_reg & ENGINE_CAP_PRESENT_MASK) { + err = create_dma_engine_core(pcard, (KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), 32 + i, pcard->pdev->irq); + if (err) + goto err_out; + } + } + + return 0; + err_out: - dev_err(&pcard->pdev->dev, "kp2000_setup_dma_controller: failed to add a DMA Engine: %d\n", err); - return err; + dev_err(&pcard->pdev->dev, "%s: failed to add a DMA Engine: %d\n", + __func__, err); + return err; } int kp2000_probe_cores(struct kp2000_device *pcard) { - int err = 0; - int i; - int current_type_id; - u64 read_val; - unsigned int highest_core_id = 0; - struct core_table_entry cte; - - dev_dbg(&pcard->pdev->dev, "kp2000_probe_cores(pcard = %p / %d)\n", pcard, pcard->card_num); - - err = kp2000_setup_dma_controller(pcard); - if (err) return err; - - INIT_LIST_HEAD(&pcard->uio_devices_list); - - // First, iterate the core table looking for the highest CORE_ID - for (i = 0 ; i < pcard->core_table_length ; i++){ - read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8)); - parse_core_table_entry(&cte, read_val, pcard->core_table_rev); - dbg_cte(pcard, &cte); - if (cte.type > highest_core_id){ - highest_core_id = cte.type; - } - if (cte.type == KP_CORE_ID_INVALID){ - dev_info(&pcard->pdev->dev, "Found Invalid core: %016llx\n", read_val); - } - } - // Then, iterate over the possible core types. - for (current_type_id = 1 ; current_type_id <= highest_core_id ; current_type_id++){ - unsigned int core_num = 0; - // Foreach core type, iterate the whole table and instantiate subdevices for each core. - // Yes, this is O(n*m) but the actual runtime is small enough that it's an acceptable tradeoff. - for (i = 0 ; i < pcard->core_table_length ; i++){ - read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8)); - parse_core_table_entry(&cte, read_val, pcard->core_table_rev); - - if (cte.type == current_type_id){ - switch (cte.type){ - case KP_CORE_ID_I2C: - err = probe_core_basic(core_num, pcard, KP_DRIVER_NAME_I2C, cte); - break; - - case KP_CORE_ID_SPI: - err = probe_core_basic(core_num, pcard, KP_DRIVER_NAME_SPI, cte); - break; - - default: - err = probe_core_uio(core_num, pcard, "kpc_uio", cte); - break; - } - if (err){ - dev_err(&pcard->pdev->dev, "kp2000_probe_cores: failed to add core %d: %d\n", i, err); - return err; - } - core_num++; - } - } - } - - // Finally, instantiate a UIO device for the core_table. - cte.type = 0; // CORE_ID_BOARD_INFO - cte.offset = 0; // board info is always at the beginning - cte.length = 512*8; - cte.s2c_dma_present = false; - cte.s2c_dma_channel_num = 0; - cte.c2s_dma_present = false; - cte.c2s_dma_channel_num = 0; - cte.irq_count = 0; - cte.irq_base_num = 0; - err = probe_core_uio(0, pcard, "kpc_uio", cte); - if (err){ - dev_err(&pcard->pdev->dev, "kp2000_probe_cores: failed to add board_info core: %d\n", err); - return err; - } - - return 0; + int err = 0; + int i; + int current_type_id; + u64 read_val; + unsigned int highest_core_id = 0; + struct core_table_entry cte; + + err = kp2000_setup_dma_controller(pcard); + if (err) + return err; + + INIT_LIST_HEAD(&pcard->uio_devices_list); + + // First, iterate the core table looking for the highest CORE_ID + for (i = 0 ; i < pcard->core_table_length ; i++) { + read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8)); + parse_core_table_entry(&cte, read_val, pcard->core_table_rev); + dbg_cte(pcard, &cte); + if (cte.type > highest_core_id) + highest_core_id = cte.type; + if (cte.type == KP_CORE_ID_INVALID) + dev_info(&pcard->pdev->dev, "Found Invalid core: %016llx\n", read_val); + } + // Then, iterate over the possible core types. + for (current_type_id = 1 ; current_type_id <= highest_core_id ; current_type_id++) { + unsigned int core_num = 0; + // Foreach core type, iterate the whole table and instantiate subdevices for each core. + // Yes, this is O(n*m) but the actual runtime is small enough that it's an acceptable tradeoff. + for (i = 0 ; i < pcard->core_table_length ; i++) { + read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8)); + parse_core_table_entry(&cte, read_val, pcard->core_table_rev); + + if (cte.type != current_type_id) + continue; + + switch (cte.type) { + case KP_CORE_ID_I2C: + err = probe_core_basic(core_num, pcard, + KP_DRIVER_NAME_I2C, cte); + break; + + case KP_CORE_ID_SPI: + err = probe_core_basic(core_num, pcard, + KP_DRIVER_NAME_SPI, cte); + break; + + default: + err = probe_core_uio(core_num, pcard, "kpc_uio", cte); + break; + } + if (err) { + dev_err(&pcard->pdev->dev, + "%s: failed to add core %d: %d\n", + __func__, i, err); + goto error; + } + core_num++; + } + } + + // Finally, instantiate a UIO device for the core_table. + cte.type = 0; // CORE_ID_BOARD_INFO + cte.offset = 0; // board info is always at the beginning + cte.length = 512 * 8; + cte.s2c_dma_present = false; + cte.s2c_dma_channel_num = 0; + cte.c2s_dma_present = false; + cte.c2s_dma_channel_num = 0; + cte.irq_count = 0; + cte.irq_base_num = 0; + err = probe_core_uio(0, pcard, "kpc_uio", cte); + if (err) { + dev_err(&pcard->pdev->dev, "%s: failed to add board_info core: %d\n", + __func__, err); + goto error; + } + + return 0; + +error: + kp2000_remove_cores(pcard); + mfd_remove_devices(PCARD_TO_DEV(pcard)); + return err; } void kp2000_remove_cores(struct kp2000_device *pcard) { - struct list_head *ptr; - struct list_head *next; - list_for_each_safe(ptr, next, &pcard->uio_devices_list){ - struct kpc_uio_device *kudev = list_entry(ptr, struct kpc_uio_device, list); - uio_unregister_device(&kudev->uioinfo); - device_unregister(kudev->dev); - list_del(&kudev->list); - kfree(kudev); - } + struct list_head *ptr; + struct list_head *next; + + list_for_each_safe(ptr, next, &pcard->uio_devices_list) { + struct kpc_uio_device *kudev = list_entry(ptr, struct kpc_uio_device, list); + + uio_unregister_device(&kudev->uioinfo); + device_unregister(kudev->dev); + list_del(&kudev->list); + kfree(kudev); + } } diff --git a/drivers/staging/kpc2000/kpc2000/core.c b/drivers/staging/kpc2000/kpc2000/core.c index 40390cdd3c8d..cb05cca687e1 100644 --- a/drivers/staging/kpc2000/kpc2000/core.c +++ b/drivers/staging/kpc2000/kpc2000/core.c @@ -1,11 +1,17 @@ // SPDX-License-Identifier: GPL-2.0+ +#include <linux/kernel.h> +#include <linux/idr.h> #include <linux/init.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/types.h> #include <linux/export.h> #include <linux/slab.h> -#include <asm/io.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/cdev.h> +#include <linux/rwsem.h> +#include <linux/uaccess.h> #include <linux/io.h> #include <linux/mfd/core.h> #include <linux/platform_device.h> @@ -17,421 +23,543 @@ #include <linux/sched.h> #include <linux/jiffies.h> #include "pcie.h" +#include "uapi.h" +static DEFINE_IDA(card_num_ida); /******************************************************* - * SysFS Attributes - ******************************************************/ -static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf) + * SysFS Attributes + ******************************************************/ + +static ssize_t ssid_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct pci_dev *pdev = to_pci_dev(dev); - struct kp2000_device *pcard; - - if (!pdev) return -ENXIO; - pcard = pci_get_drvdata(pdev); - if (!pcard) return -ENXIO; - - if (strcmp("ssid", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%016llx\n", pcard->ssid); } else - if (strcmp("ddna", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%016llx\n", pcard->ddna); } else - if (strcmp("card_id", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->card_id); } else - if (strcmp("hw_rev", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->hardware_revision); } else - if (strcmp("build", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_version); } else - if (strcmp("build_date", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_datestamp); } else - if (strcmp("build_time", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_timestamp); } else - { return -ENXIO; } + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%016llx\n", pcard->ssid); } +static DEVICE_ATTR_RO(ssid); -static ssize_t show_cpld_config_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t ddna_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct pci_dev *pdev = to_pci_dev(dev); - struct kp2000_device *pcard; - u64 val; + struct kp2000_device *pcard = dev_get_drvdata(dev); - if (!pdev) - return -ENXIO; + return sprintf(buf, "%016llx\n", pcard->ddna); +} +static DEVICE_ATTR_RO(ddna); - pcard = pci_get_drvdata(pdev); - if (!pcard) - return -ENXIO; +static ssize_t card_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->card_id); +} +static DEVICE_ATTR_RO(card_id); + +static ssize_t hw_rev_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->hardware_revision); +} +static DEVICE_ATTR_RO(hw_rev); + +static ssize_t build_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->build_version); +} +static DEVICE_ATTR_RO(build); + +static ssize_t build_date_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->build_datestamp); +} +static DEVICE_ATTR_RO(build_date); + +static ssize_t build_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->build_timestamp); +} +static DEVICE_ATTR_RO(build_time); + +static ssize_t cpld_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + u64 val; val = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG); - return scnprintf(buf, PAGE_SIZE, "%016llx\n", val); + return sprintf(buf, "%016llx\n", val); +} +static DEVICE_ATTR_RO(cpld_reg); + +static ssize_t cpld_reconfigure(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + long wr_val; + int rv; + + rv = kstrtol(buf, 0, &wr_val); + if (rv < 0) + return rv; + if (wr_val > 7) + return -EINVAL; + + wr_val = wr_val << 8; + wr_val |= 0x1; // Set the "Configure Go" bit + writeq(wr_val, pcard->sysinfo_regs_base + REG_CPLD_CONFIG); + return count; } -static ssize_t cpld_reconfigure(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static DEVICE_ATTR(cpld_reconfigure, 0220, NULL, cpld_reconfigure); + +static ssize_t irq_mask_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct pci_dev *pdev = to_pci_dev(dev); - long wr_val; - struct kp2000_device *pcard; - int rv; - - if (!pdev) return -ENXIO; - pcard = pci_get_drvdata(pdev); - if (!pcard) return -ENXIO; - - rv = kstrtol(buf, 0, &wr_val); - if (rv < 0) return rv; - if (wr_val > 7) return -EINVAL; - - wr_val = wr_val << 8; - wr_val |= 0x1; // Set the "Configure Go" bit - writeq(wr_val, pcard->sysinfo_regs_base + REG_CPLD_CONFIG); - return count; + struct kp2000_device *pcard = dev_get_drvdata(dev); + u64 val; + + val = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); + return sprintf(buf, "%016llx\n", val); } +static DEVICE_ATTR_RO(irq_mask_reg); +static ssize_t irq_active_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + u64 val; -DEVICE_ATTR(ssid, 0444, show_attr, NULL); -DEVICE_ATTR(ddna, 0444, show_attr, NULL); -DEVICE_ATTR(card_id, 0444, show_attr, NULL); -DEVICE_ATTR(hw_rev, 0444, show_attr, NULL); -DEVICE_ATTR(build, 0444, show_attr, NULL); -DEVICE_ATTR(build_date, 0444, show_attr, NULL); -DEVICE_ATTR(build_time, 0444, show_attr, NULL); -DEVICE_ATTR(cpld_reg, 0444, show_cpld_config_reg, NULL); -DEVICE_ATTR(cpld_reconfigure, 0220, NULL, cpld_reconfigure); - -static const struct attribute * kp_attr_list[] = { - &dev_attr_ssid.attr, - &dev_attr_ddna.attr, - &dev_attr_card_id.attr, - &dev_attr_hw_rev.attr, - &dev_attr_build.attr, - &dev_attr_build_date.attr, - &dev_attr_build_time.attr, - &dev_attr_cpld_reg.attr, - &dev_attr_cpld_reconfigure.attr, - NULL, -}; + val = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); + return sprintf(buf, "%016llx\n", val); +} +static DEVICE_ATTR_RO(irq_active_reg); +static ssize_t pcie_error_count_reg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + u64 val; + + val = readq(pcard->sysinfo_regs_base + REG_PCIE_ERROR_COUNT); + return sprintf(buf, "%016llx\n", val); +} +static DEVICE_ATTR_RO(pcie_error_count_reg); + +static ssize_t core_table_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->core_table_offset); +} +static DEVICE_ATTR_RO(core_table_offset); + +static ssize_t core_table_length_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kp2000_device *pcard = dev_get_drvdata(dev); + + return sprintf(buf, "%08x\n", pcard->core_table_length); +} +static DEVICE_ATTR_RO(core_table_length); + +static const struct attribute *kp_attr_list[] = { + &dev_attr_ssid.attr, + &dev_attr_ddna.attr, + &dev_attr_card_id.attr, + &dev_attr_hw_rev.attr, + &dev_attr_build.attr, + &dev_attr_build_date.attr, + &dev_attr_build_time.attr, + &dev_attr_cpld_reg.attr, + &dev_attr_cpld_reconfigure.attr, + &dev_attr_irq_mask_reg.attr, + &dev_attr_irq_active_reg.attr, + &dev_attr_pcie_error_count_reg.attr, + &dev_attr_core_table_offset.attr, + &dev_attr_core_table_length.attr, + NULL, +}; /******************************************************* - * Functions - ******************************************************/ + * Functions + ******************************************************/ static void wait_and_read_ssid(struct kp2000_device *pcard) { - u64 read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID); - unsigned long timeout; - - if (read_val & 0x8000000000000000){ - pcard->ssid = read_val; - return; - } - - timeout = jiffies + (HZ * 2); - do { - read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID); - if (read_val & 0x8000000000000000){ - pcard->ssid = read_val; - return; - } - cpu_relax(); - //schedule(); - } while (time_before(jiffies, timeout)); - - dev_notice(&pcard->pdev->dev, "SSID didn't show up!\n"); - - #if 0 - // Timed out waiting for the SSID to show up, just use the DDNA instead? - read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA); - pcard->ssid = read_val; - #else - // Timed out waiting for the SSID to show up, stick all zeros in the value - pcard->ssid = 0; - #endif + u64 read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID); + unsigned long timeout; + + if (read_val & 0x8000000000000000) { + pcard->ssid = read_val; + return; + } + + timeout = jiffies + (HZ * 2); + do { + read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID); + if (read_val & 0x8000000000000000) { + pcard->ssid = read_val; + return; + } + cpu_relax(); + //schedule(); + } while (time_before(jiffies, timeout)); + + dev_notice(&pcard->pdev->dev, "SSID didn't show up!\n"); + + // Timed out waiting for the SSID to show up, stick all zeros in the + // value + pcard->ssid = 0; } static int read_system_regs(struct kp2000_device *pcard) { - u64 read_val; - - read_val = readq(pcard->sysinfo_regs_base + REG_MAGIC_NUMBER); - if (read_val != KP2000_MAGIC_VALUE){ - dev_err(&pcard->pdev->dev, "Invalid magic! Got: 0x%016llx Want: 0x%016lx\n", read_val, KP2000_MAGIC_VALUE); - return -EILSEQ; - } - - read_val = readq(pcard->sysinfo_regs_base + REG_CARD_ID_AND_BUILD); - pcard->card_id = (read_val & 0xFFFFFFFF00000000) >> 32; - pcard->build_version = (read_val & 0x00000000FFFFFFFF) >> 0; - - read_val = readq(pcard->sysinfo_regs_base + REG_DATE_AND_TIME_STAMPS); - pcard->build_datestamp = (read_val & 0xFFFFFFFF00000000) >> 32; - pcard->build_timestamp = (read_val & 0x00000000FFFFFFFF) >> 0; - - read_val = readq(pcard->sysinfo_regs_base + REG_CORE_TABLE_OFFSET); - pcard->core_table_length = (read_val & 0xFFFFFFFF00000000) >> 32; - pcard->core_table_offset = (read_val & 0x00000000FFFFFFFF) >> 0; - - wait_and_read_ssid(pcard); - - read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_HW_ID); - pcard->core_table_rev = (read_val & 0x0000000000000F00) >> 8; - pcard->hardware_revision = (read_val & 0x000000000000001F); - - read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA); - pcard->ddna = read_val; - - dev_info(&pcard->pdev->dev, "system_regs: %08x %08x %08x %08x %02x %d %d %016llx %016llx\n", - pcard->card_id, - pcard->build_version, - pcard->build_datestamp, - pcard->build_timestamp, - pcard->hardware_revision, - pcard->core_table_rev, - pcard->core_table_length, - pcard->ssid, - pcard->ddna - ); - - if (pcard->core_table_rev > 1){ - dev_err(&pcard->pdev->dev, "core table entry revision is higher than we can deal with, cannot continue with this card!\n"); - return 1; - } - - return 0; + u64 read_val; + + read_val = readq(pcard->sysinfo_regs_base + REG_MAGIC_NUMBER); + if (read_val != KP2000_MAGIC_VALUE) { + dev_err(&pcard->pdev->dev, + "Invalid magic! Got: 0x%016llx Want: 0x%016llx\n", + read_val, KP2000_MAGIC_VALUE); + return -EILSEQ; + } + + read_val = readq(pcard->sysinfo_regs_base + REG_CARD_ID_AND_BUILD); + pcard->card_id = (read_val & 0xFFFFFFFF00000000) >> 32; + pcard->build_version = (read_val & 0x00000000FFFFFFFF) >> 0; + + read_val = readq(pcard->sysinfo_regs_base + REG_DATE_AND_TIME_STAMPS); + pcard->build_datestamp = (read_val & 0xFFFFFFFF00000000) >> 32; + pcard->build_timestamp = (read_val & 0x00000000FFFFFFFF) >> 0; + + read_val = readq(pcard->sysinfo_regs_base + REG_CORE_TABLE_OFFSET); + pcard->core_table_length = (read_val & 0xFFFFFFFF00000000) >> 32; + pcard->core_table_offset = (read_val & 0x00000000FFFFFFFF) >> 0; + + wait_and_read_ssid(pcard); + + read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_HW_ID); + pcard->core_table_rev = (read_val & 0x0000000000000F00) >> 8; + pcard->hardware_revision = (read_val & 0x000000000000001F); + + read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA); + pcard->ddna = read_val; + + dev_info(&pcard->pdev->dev, + "system_regs: %08x %08x %08x %08x %02x %d %d %016llx %016llx\n", + pcard->card_id, + pcard->build_version, + pcard->build_datestamp, + pcard->build_timestamp, + pcard->hardware_revision, + pcard->core_table_rev, + pcard->core_table_length, + pcard->ssid, + pcard->ddna); + + if (pcard->core_table_rev > 1) { + dev_err(&pcard->pdev->dev, + "core table entry revision is higher than we can deal with, cannot continue with this card!\n"); + return 1; + } + + return 0; } -irqreturn_t kp2000_irq_handler(int irq, void *dev_id) +static irqreturn_t kp2000_irq_handler(int irq, void *dev_id) { - struct kp2000_device *pcard = (struct kp2000_device*)dev_id; - SetBackEndControl(pcard->dma_common_regs, KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE | KPC_DMA_CARD_USER_INTERRUPT_ACTIVE); - return IRQ_HANDLED; + struct kp2000_device *pcard = dev_id; + + writel(KPC_DMA_CARD_IRQ_ENABLE | + KPC_DMA_CARD_USER_INTERRUPT_MODE | + KPC_DMA_CARD_USER_INTERRUPT_ACTIVE, + pcard->dma_common_regs); + return IRQ_HANDLED; } -int kp2000_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +static int kp2000_pcie_probe(struct pci_dev *pdev, + const struct pci_device_id *id) { - int err = 0; - struct kp2000_device *pcard; - static int card_count = 1; - int rv; - unsigned long reg_bar_phys_addr; - unsigned long reg_bar_phys_len; - unsigned long dma_bar_phys_addr; - unsigned long dma_bar_phys_len; - u16 regval; - - dev_dbg(&pdev->dev, "kp2000_pcie_probe(pdev = [%p], id = [%p])\n", pdev, id); - - //{ Step 1: Allocate a struct for the pcard - pcard = kzalloc(sizeof(struct kp2000_device), GFP_KERNEL); - if (NULL == pcard){ - dev_err(&pdev->dev, "probe: failed to allocate private card data\n"); - return -ENOMEM; - } - dev_dbg(&pdev->dev, "probe: allocated struct kp2000_device @ %p\n", pcard); - //} - - //{ Step 2: Initialize trivial pcard elements - pcard->card_num = card_count; - card_count++; - scnprintf(pcard->name, 16, "kpcard%d", pcard->card_num); - - mutex_init(&pcard->sem); - lock_card(pcard); - - pcard->pdev = pdev; - pci_set_drvdata(pdev, pcard); - //} - - //{ Step 3: Enable PCI device - err = pci_enable_device(pcard->pdev); - if (err){ - dev_err(&pcard->pdev->dev, "probe: failed to enable PCIE2000 PCIe device (%d)\n", err); - goto out3; - } - //} - - //{ Step 4: Setup the Register BAR - reg_bar_phys_addr = pci_resource_start(pcard->pdev, REG_BAR); - reg_bar_phys_len = pci_resource_len(pcard->pdev, REG_BAR); - - pcard->regs_bar_base = ioremap_nocache(reg_bar_phys_addr, PAGE_SIZE); - if (NULL == pcard->regs_bar_base){ - dev_err(&pcard->pdev->dev, "probe: REG_BAR could not remap memory to virtual space\n"); - err = -ENODEV; - goto out4; - } - dev_dbg(&pcard->pdev->dev, "probe: REG_BAR virt hardware address start [%p]\n", pcard->regs_bar_base); - - err = pci_request_region(pcard->pdev, REG_BAR, KP_DRIVER_NAME_KP2000); - if (err){ - iounmap(pcard->regs_bar_base); - dev_err(&pcard->pdev->dev, "probe: failed to acquire PCI region (%d)\n", err); - err = -ENODEV; - goto out4; - } - - pcard->regs_base_resource.start = reg_bar_phys_addr; - pcard->regs_base_resource.end = reg_bar_phys_addr + reg_bar_phys_len - 1; - pcard->regs_base_resource.flags = IORESOURCE_MEM; - //} - - //{ Step 5: Setup the DMA BAR - dma_bar_phys_addr = pci_resource_start(pcard->pdev, DMA_BAR); - dma_bar_phys_len = pci_resource_len(pcard->pdev, DMA_BAR); - - pcard->dma_bar_base = ioremap_nocache(dma_bar_phys_addr, dma_bar_phys_len); - if (NULL == pcard->dma_bar_base){ - dev_err(&pcard->pdev->dev, "probe: DMA_BAR could not remap memory to virtual space\n"); - err = -ENODEV; - goto out5; - } - dev_dbg(&pcard->pdev->dev, "probe: DMA_BAR virt hardware address start [%p]\n", pcard->dma_bar_base); - - pcard->dma_common_regs = pcard->dma_bar_base + KPC_DMA_COMMON_OFFSET; - - err = pci_request_region(pcard->pdev, DMA_BAR, "kp2000_pcie"); - if (err){ - iounmap(pcard->dma_bar_base); - dev_err(&pcard->pdev->dev, "probe: failed to acquire PCI region (%d)\n", err); - err = -ENODEV; - goto out5; - } - - pcard->dma_base_resource.start = dma_bar_phys_addr; - pcard->dma_base_resource.end = dma_bar_phys_addr + dma_bar_phys_len - 1; - pcard->dma_base_resource.flags = IORESOURCE_MEM; - //} - - //{ Step 6: System Regs - pcard->sysinfo_regs_base = pcard->regs_bar_base; - err = read_system_regs(pcard); - if (err) - goto out6; - - // Disable all "user" interrupts because they're not used yet. - writeq(0xFFFFFFFFFFFFFFFF, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); - //} - - //{ Step 7: Configure PCI thingies - // let the card master PCIe - pci_set_master(pcard->pdev); - // enable IO and mem if not already done - pci_read_config_word(pcard->pdev, PCI_COMMAND, ®val); - regval |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - pci_write_config_word(pcard->pdev, PCI_COMMAND, regval); - - // Clear relaxed ordering bit - pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN, 0); - - // Set Max_Payload_Size and Max_Read_Request_Size - regval = (0x0) << 5; // Max_Payload_Size = 128 B - pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, regval); - regval = (0x0) << 12; // Max_Read_Request_Size = 128 B - pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_READRQ, regval); - - // Enable error reporting for: Correctable Errors, Non-Fatal Errors, Fatal Errors, Unsupported Requests - pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, 0, PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); - - err = dma_set_mask(PCARD_TO_DEV(pcard), DMA_BIT_MASK(64)); - if (err){ - dev_err(&pcard->pdev->dev, "CANNOT use DMA mask %0llx\n", DMA_BIT_MASK(64)); - goto out7; - } - dev_dbg(&pcard->pdev->dev, "Using DMA mask %0llx\n", dma_get_mask(PCARD_TO_DEV(pcard))); - //} - - //{ Step 8: Configure IRQs - err = pci_enable_msi(pcard->pdev); - if (err < 0) - goto out8a; - - rv = request_irq(pcard->pdev->irq, kp2000_irq_handler, IRQF_SHARED, pcard->name, pcard); - if (rv){ - dev_err(&pcard->pdev->dev, "kp2000_pcie_probe: failed to request_irq: %d\n", rv); - goto out8b; - } - //} - - //{ Step 9: Setup sysfs attributes - err = sysfs_create_files(&(pdev->dev.kobj), kp_attr_list); - if (err){ - dev_err(&pdev->dev, "Failed to add sysfs files: %d\n", err); - goto out9; - } - //} - - //{ Step 10: Setup misc device - pcard->miscdev.minor = MISC_DYNAMIC_MINOR; - pcard->miscdev.fops = &kp2000_fops; - pcard->miscdev.parent = &pcard->pdev->dev; - pcard->miscdev.name = pcard->name; - - err = misc_register(&pcard->miscdev); - if (err){ - dev_err(&pcard->pdev->dev, "kp2000_pcie_probe: misc_register failed: %d\n", err); - goto out10; - } - //} - - //{ Step 11: Probe cores - err = kp2000_probe_cores(pcard); - if (err) - goto out11; - //} - - //{ Step 12: Enable IRQs in HW - SetBackEndControl(pcard->dma_common_regs, KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE); - //} - - dev_dbg(&pcard->pdev->dev, "kp2000_pcie_probe() complete!\n"); - unlock_card(pcard); - return 0; - - out11: - misc_deregister(&pcard->miscdev); - out10: - sysfs_remove_files(&(pdev->dev.kobj), kp_attr_list); - out9: - free_irq(pcard->pdev->irq, pcard); - out8b: - pci_disable_msi(pcard->pdev); - out8a: - out7: - out6: - iounmap(pcard->dma_bar_base); - pci_release_region(pdev, DMA_BAR); - pcard->dma_bar_base = NULL; - out5: - iounmap(pcard->regs_bar_base); - pci_release_region(pdev, REG_BAR); - pcard->regs_bar_base = NULL; - out4: - pci_disable_device(pcard->pdev); - out3: - unlock_card(pcard); - kfree(pcard); - return err; + int err = 0; + struct kp2000_device *pcard; + int rv; + unsigned long reg_bar_phys_addr; + unsigned long reg_bar_phys_len; + unsigned long dma_bar_phys_addr; + unsigned long dma_bar_phys_len; + u16 regval; + + pcard = kzalloc(sizeof(*pcard), GFP_KERNEL); + if (!pcard) + return -ENOMEM; + dev_dbg(&pdev->dev, "probe: allocated struct kp2000_device @ %p\n", + pcard); + + err = ida_simple_get(&card_num_ida, 1, INT_MAX, GFP_KERNEL); + if (err < 0) { + dev_err(&pdev->dev, "probe: failed to get card number (%d)\n", + err); + goto err_free_pcard; + } + pcard->card_num = err; + scnprintf(pcard->name, 16, "kpcard%u", pcard->card_num); + + mutex_init(&pcard->sem); + mutex_lock(&pcard->sem); + + pcard->pdev = pdev; + pci_set_drvdata(pdev, pcard); + + err = pci_enable_device(pcard->pdev); + if (err) { + dev_err(&pcard->pdev->dev, + "probe: failed to enable PCIE2000 PCIe device (%d)\n", + err); + goto err_remove_ida; + } + + /* Setup the Register BAR */ + reg_bar_phys_addr = pci_resource_start(pcard->pdev, REG_BAR); + reg_bar_phys_len = pci_resource_len(pcard->pdev, REG_BAR); + + pcard->regs_bar_base = ioremap_nocache(reg_bar_phys_addr, PAGE_SIZE); + if (!pcard->regs_bar_base) { + dev_err(&pcard->pdev->dev, + "probe: REG_BAR could not remap memory to virtual space\n"); + err = -ENODEV; + goto err_disable_device; + } + dev_dbg(&pcard->pdev->dev, + "probe: REG_BAR virt hardware address start [%p]\n", + pcard->regs_bar_base); + + err = pci_request_region(pcard->pdev, REG_BAR, KP_DRIVER_NAME_KP2000); + if (err) { + dev_err(&pcard->pdev->dev, + "probe: failed to acquire PCI region (%d)\n", + err); + err = -ENODEV; + goto err_unmap_regs; + } + + pcard->regs_base_resource.start = reg_bar_phys_addr; + pcard->regs_base_resource.end = reg_bar_phys_addr + + reg_bar_phys_len - 1; + pcard->regs_base_resource.flags = IORESOURCE_MEM; + + /* Setup the DMA BAR */ + dma_bar_phys_addr = pci_resource_start(pcard->pdev, DMA_BAR); + dma_bar_phys_len = pci_resource_len(pcard->pdev, DMA_BAR); + + pcard->dma_bar_base = ioremap_nocache(dma_bar_phys_addr, + dma_bar_phys_len); + if (!pcard->dma_bar_base) { + dev_err(&pcard->pdev->dev, + "probe: DMA_BAR could not remap memory to virtual space\n"); + err = -ENODEV; + goto err_release_regs; + } + dev_dbg(&pcard->pdev->dev, + "probe: DMA_BAR virt hardware address start [%p]\n", + pcard->dma_bar_base); + + pcard->dma_common_regs = pcard->dma_bar_base + KPC_DMA_COMMON_OFFSET; + + err = pci_request_region(pcard->pdev, DMA_BAR, "kp2000_pcie"); + if (err) { + dev_err(&pcard->pdev->dev, + "probe: failed to acquire PCI region (%d)\n", err); + err = -ENODEV; + goto err_unmap_dma; + } + + pcard->dma_base_resource.start = dma_bar_phys_addr; + pcard->dma_base_resource.end = dma_bar_phys_addr + + dma_bar_phys_len - 1; + pcard->dma_base_resource.flags = IORESOURCE_MEM; + + /* Read System Regs */ + pcard->sysinfo_regs_base = pcard->regs_bar_base; + err = read_system_regs(pcard); + if (err) + goto err_release_dma; + + // Disable all "user" interrupts because they're not used yet. + writeq(0xFFFFFFFFFFFFFFFF, + pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); + + // let the card master PCIe + pci_set_master(pcard->pdev); + + // enable IO and mem if not already done + pci_read_config_word(pcard->pdev, PCI_COMMAND, ®val); + regval |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(pcard->pdev, PCI_COMMAND, regval); + + // Clear relaxed ordering bit + pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_RELAX_EN, 0); + + // Set Max_Payload_Size and Max_Read_Request_Size + regval = (0x0) << 5; // Max_Payload_Size = 128 B + pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_PAYLOAD, regval); + regval = (0x0) << 12; // Max_Read_Request_Size = 128 B + pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_READRQ, regval); + + // Enable error reporting for: Correctable Errors, Non-Fatal Errors, + // Fatal Errors, Unsupported Requests + pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, 0, + PCI_EXP_DEVCTL_CERE | + PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_URRE); + + err = dma_set_mask(PCARD_TO_DEV(pcard), DMA_BIT_MASK(64)); + if (err) { + dev_err(&pcard->pdev->dev, + "CANNOT use DMA mask %0llx\n", DMA_BIT_MASK(64)); + goto err_release_dma; + } + dev_dbg(&pcard->pdev->dev, + "Using DMA mask %0llx\n", dma_get_mask(PCARD_TO_DEV(pcard))); + + err = pci_enable_msi(pcard->pdev); + if (err < 0) + goto err_release_dma; + + rv = request_irq(pcard->pdev->irq, kp2000_irq_handler, IRQF_SHARED, + pcard->name, pcard); + if (rv) { + dev_err(&pcard->pdev->dev, + "%s: failed to request_irq: %d\n", __func__, rv); + goto err_disable_msi; + } + + err = sysfs_create_files(&pdev->dev.kobj, kp_attr_list); + if (err) { + dev_err(&pdev->dev, "Failed to add sysfs files: %d\n", err); + goto err_free_irq; + } + + err = kp2000_probe_cores(pcard); + if (err) + goto err_remove_sysfs; + + /* Enable IRQs in HW */ + writel(KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE, + pcard->dma_common_regs); + + mutex_unlock(&pcard->sem); + return 0; + +err_remove_sysfs: + sysfs_remove_files(&pdev->dev.kobj, kp_attr_list); +err_free_irq: + free_irq(pcard->pdev->irq, pcard); +err_disable_msi: + pci_disable_msi(pcard->pdev); +err_release_dma: + pci_release_region(pdev, DMA_BAR); +err_unmap_dma: + iounmap(pcard->dma_bar_base); +err_release_regs: + pci_release_region(pdev, REG_BAR); +err_unmap_regs: + iounmap(pcard->regs_bar_base); +err_disable_device: + pci_disable_device(pcard->pdev); +err_remove_ida: + mutex_unlock(&pcard->sem); + ida_simple_remove(&card_num_ida, pcard->card_num); +err_free_pcard: + kfree(pcard); + return err; +} + +static void kp2000_pcie_remove(struct pci_dev *pdev) +{ + struct kp2000_device *pcard = pci_get_drvdata(pdev); + + if (!pcard) + return; + + mutex_lock(&pcard->sem); + kp2000_remove_cores(pcard); + mfd_remove_devices(PCARD_TO_DEV(pcard)); + sysfs_remove_files(&pdev->dev.kobj, kp_attr_list); + free_irq(pcard->pdev->irq, pcard); + pci_disable_msi(pcard->pdev); + if (pcard->dma_bar_base) { + iounmap(pcard->dma_bar_base); + pci_release_region(pdev, DMA_BAR); + pcard->dma_bar_base = NULL; + } + if (pcard->regs_bar_base) { + iounmap(pcard->regs_bar_base); + pci_release_region(pdev, REG_BAR); + pcard->regs_bar_base = NULL; + } + pci_disable_device(pcard->pdev); + pci_set_drvdata(pdev, NULL); + mutex_unlock(&pcard->sem); + ida_simple_remove(&card_num_ida, pcard->card_num); + kfree(pcard); } +struct class *kpc_uio_class; +ATTRIBUTE_GROUPS(kpc_uio_class); + +static const struct pci_device_id kp2000_pci_device_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS) }, + { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, kp2000_pci_device_ids); + +static struct pci_driver kp2000_driver_inst = { + .name = "kp2000_pcie", + .id_table = kp2000_pci_device_ids, + .probe = kp2000_pcie_probe, + .remove = kp2000_pcie_remove, +}; -void kp2000_pcie_remove(struct pci_dev *pdev) +static int __init kp2000_pcie_init(void) { - struct kp2000_device *pcard = pci_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "kp2000_pcie_remove(pdev=%p)\n", pdev); - - if (pcard == NULL) return; - - lock_card(pcard); - kp2000_remove_cores(pcard); - mfd_remove_devices(PCARD_TO_DEV(pcard)); - misc_deregister(&pcard->miscdev); - sysfs_remove_files(&(pdev->dev.kobj), kp_attr_list); - free_irq(pcard->pdev->irq, pcard); - pci_disable_msi(pcard->pdev); - if (pcard->dma_bar_base != NULL){ - iounmap(pcard->dma_bar_base); - pci_release_region(pdev, DMA_BAR); - pcard->dma_bar_base = NULL; - } - if (pcard->regs_bar_base != NULL){ - iounmap(pcard->regs_bar_base); - pci_release_region(pdev, REG_BAR); - pcard->regs_bar_base = NULL; - } - pci_disable_device(pcard->pdev); - pci_set_drvdata(pdev, NULL); - unlock_card(pcard); - kfree(pcard); + kpc_uio_class = class_create(THIS_MODULE, "kpc_uio"); + if (IS_ERR(kpc_uio_class)) + return PTR_ERR(kpc_uio_class); + + kpc_uio_class->dev_groups = kpc_uio_class_groups; + return pci_register_driver(&kp2000_driver_inst); } +module_init(kp2000_pcie_init); + +static void __exit kp2000_pcie_exit(void) +{ + pci_unregister_driver(&kp2000_driver_inst); + class_destroy(kpc_uio_class); + ida_destroy(&card_num_ida); +} +module_exit(kp2000_pcie_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lee.Brooke@Daktronics.com, Matt.Sickler@Daktronics.com"); +MODULE_SOFTDEP("pre: uio post: kpc_nwl_dma kpc_i2c kpc_spi"); diff --git a/drivers/staging/kpc2000/kpc2000/dma_common_defs.h b/drivers/staging/kpc2000/kpc2000/dma_common_defs.h index f35e636b1fb7..21450e3d408f 100644 --- a/drivers/staging/kpc2000/kpc2000/dma_common_defs.h +++ b/drivers/staging/kpc2000/kpc2000/dma_common_defs.h @@ -21,23 +21,4 @@ #define KPC_DMA_CARD_S2C_INTERRUPT_STATUS_MASK 0x00FF0000 #define KPC_DMA_CARD_C2S_INTERRUPT_STATUS_MASK 0xFF000000 -static inline void SetBackEndControl(void __iomem *regs, u32 value) -{ - writel(value, regs + 0); -} -static inline u32 GetBackEndStatus(void __iomem *regs) -{ - return readl(regs + 0); -} - -static inline u32 BackEndControlSetClear(void __iomem *regs, u32 set_bits, u32 clear_bits) -{ - u32 start_val = GetBackEndStatus(regs); - u32 new_val = start_val; - new_val &= ~clear_bits; - new_val |= set_bits; - SetBackEndControl(regs, new_val); - return start_val; -} - #endif /* KPC_DMA_COMMON_DEFS_H_ */ diff --git a/drivers/staging/kpc2000/kpc2000/fileops.c b/drivers/staging/kpc2000/kpc2000/fileops.c deleted file mode 100644 index b3b0b763fa1e..000000000000 --- a/drivers/staging/kpc2000/kpc2000/fileops.c +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> /* printk() */ -#include <linux/slab.h> /* kmalloc() */ -#include <linux/fs.h> /* everything... */ -#include <linux/errno.h> /* error codes */ -#include <linux/types.h> /* size_t */ -#include <linux/cdev.h> -#include <linux/uaccess.h> /* copy_*_user */ -#include <linux/rwsem.h> -#include <linux/idr.h> -#include <linux/io-64-nonatomic-lo-hi.h> -#include <linux/device.h> -#include <linux/sched.h> -#include "pcie.h" -#include "uapi.h" - -int kp2000_cdev_open(struct inode *inode, struct file *filp) -{ - struct kp2000_device *pcard = container_of(filp->private_data, struct kp2000_device, miscdev); - - dev_dbg(&pcard->pdev->dev, "kp2000_cdev_open(filp = [%p], pcard = [%p])\n", filp, pcard); - - filp->private_data = pcard; /* so other methods can access it */ - - return 0; -} - -int kp2000_cdev_close(struct inode *inode, struct file *filp) -{ - struct kp2000_device *pcard = filp->private_data; - - dev_dbg(&pcard->pdev->dev, "kp2000_cdev_close(filp = [%p], pcard = [%p])\n", filp, pcard); - return 0; -} - - -ssize_t kp2000_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) -{ - struct kp2000_device *pcard = filp->private_data; - int cnt = 0; - int ret; -#define BUFF_CNT 1024 - char buff[BUFF_CNT] = {0}; //NOTE: Increase this so it is at least as large as all the scnprintfs. And don't use unbounded strings. "%s" - //NOTE: also, this is a really shitty way to implement the read() call, but it will work for any size 'count'. - - if (WARN(NULL == buf, "kp2000_cdev_read: buf is a NULL pointer!\n")) - return -EINVAL; - - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Card ID : 0x%08x\n", pcard->card_id); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Version : 0x%08x\n", pcard->build_version); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Date : 0x%08x\n", pcard->build_datestamp); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Time : 0x%08x\n", pcard->build_timestamp); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Core Table Offset : 0x%08x\n", pcard->core_table_offset); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Core Table Length : 0x%08x\n", pcard->core_table_length); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Hardware Revision : 0x%08x\n", pcard->hardware_revision); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "SSID : 0x%016llx\n", pcard->ssid); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "DDNA : 0x%016llx\n", pcard->ddna); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "IRQ Mask : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK)); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "IRQ Active : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE)); - cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "CPLD : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG)); - - if (*f_pos >= cnt) - return 0; - - if (count > cnt) - count = cnt; - - ret = copy_to_user(buf, buff + *f_pos, count); - if (ret) - return -EFAULT; - *f_pos += count; - return count; -} - -ssize_t kp2000_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) -{ - return -EINVAL; -} - -long kp2000_cdev_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param) -{ - struct kp2000_device *pcard = filp->private_data; - - dev_dbg(&pcard->pdev->dev, "kp2000_cdev_ioctl(filp = [%p], ioctl_num = 0x%08x, ioctl_param = 0x%016lx) pcard = [%p]\n", filp, ioctl_num, ioctl_param, pcard); - - switch (ioctl_num){ - case KP2000_IOCTL_GET_CPLD_REG: return readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG); - case KP2000_IOCTL_GET_PCIE_ERROR_REG: return readq(pcard->sysinfo_regs_base + REG_PCIE_ERROR_COUNT); - - case KP2000_IOCTL_GET_EVERYTHING: { - struct kp2000_regs temp; - int ret; - - memset(&temp, 0, sizeof(temp)); - temp.card_id = pcard->card_id; - temp.build_version = pcard->build_version; - temp.build_datestamp = pcard->build_datestamp; - temp.build_timestamp = pcard->build_timestamp; - temp.hw_rev = pcard->hardware_revision; - temp.ssid = pcard->ssid; - temp.ddna = pcard->ddna; - temp.cpld_reg = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG); - - ret = copy_to_user((void*)ioctl_param, (void*)&temp, sizeof(temp)); - if (ret) - return -EFAULT; - - return sizeof(temp); - } - - default: - return -ENOTTY; - } - return -ENOTTY; -} - - -struct file_operations kp2000_fops = { - .owner = THIS_MODULE, - .open = kp2000_cdev_open, - .release = kp2000_cdev_close, - .read = kp2000_cdev_read, - //.write = kp2000_cdev_write, - //.poll = kp2000_cdev_poll, - //.fasync = kp2000_cdev_fasync, - .llseek = noop_llseek, - .unlocked_ioctl = kp2000_cdev_ioctl, -}; - diff --git a/drivers/staging/kpc2000/kpc2000/kp2000_module.c b/drivers/staging/kpc2000/kpc2000/kp2000_module.c deleted file mode 100644 index fa3bd266ba54..000000000000 --- a/drivers/staging/kpc2000/kpc2000/kp2000_module.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/types.h> -#include <linux/export.h> -#include <linux/slab.h> -#include <asm/io.h> -#include <linux/io.h> -#include <linux/mfd/core.h> -#include <linux/platform_device.h> -#include <linux/ioport.h> -#include "pcie.h" - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Lee.Brooke@Daktronics.com, Matt.Sickler@Daktronics.com"); -MODULE_SOFTDEP("pre: uio post: kpc_nwl_dma kpc_i2c kpc_spi"); - -struct class *kpc_uio_class; -ATTRIBUTE_GROUPS(kpc_uio_class); - -static const struct pci_device_id kp2000_pci_device_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS) }, - { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0) }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, kp2000_pci_device_ids); - -static struct pci_driver kp2000_driver_inst = { - .name = "kp2000_pcie", - .id_table = kp2000_pci_device_ids, - .probe = kp2000_pcie_probe, - .remove = kp2000_pcie_remove -}; - - -static int __init kp2000_pcie_init(void) -{ - kpc_uio_class = class_create(THIS_MODULE, "kpc_uio"); - if (IS_ERR(kpc_uio_class)) - return PTR_ERR(kpc_uio_class); - - kpc_uio_class->dev_groups = kpc_uio_class_groups; - return pci_register_driver(&kp2000_driver_inst); -} -module_init(kp2000_pcie_init); - -static void __exit kp2000_pcie_exit(void) -{ - pci_unregister_driver(&kp2000_driver_inst); - class_destroy(kpc_uio_class); -} -module_exit(kp2000_pcie_exit); diff --git a/drivers/staging/kpc2000/kpc2000/pcie.h b/drivers/staging/kpc2000/kpc2000/pcie.h index 893aebfd1449..cb815c30faa4 100644 --- a/drivers/staging/kpc2000/kpc2000/pcie.h +++ b/drivers/staging/kpc2000/kpc2000/pcie.h @@ -2,7 +2,6 @@ #ifndef KP2000_PCIE_H #define KP2000_PCIE_H #include <linux/types.h> -#include <linux/miscdevice.h> #include <linux/pci.h> #include "../kpc.h" #include "dma_common_defs.h" @@ -27,86 +26,66 @@ * 9 <---------------------- IRQ Active Flags ----------------------> */ -#define REG_WIDTH 8 -#define REG_MAGIC_NUMBER (0 * REG_WIDTH) -#define REG_CARD_ID_AND_BUILD (1 * REG_WIDTH) -#define REG_DATE_AND_TIME_STAMPS (2 * REG_WIDTH) -#define REG_CORE_TABLE_OFFSET (3 * REG_WIDTH) -#define REG_FPGA_SSID (4 * REG_WIDTH) -#define REG_FPGA_HW_ID (5 * REG_WIDTH) -#define REG_FPGA_DDNA (6 * REG_WIDTH) -#define REG_CPLD_CONFIG (7 * REG_WIDTH) -#define REG_INTERRUPT_MASK (8 * REG_WIDTH) -#define REG_INTERRUPT_ACTIVE (9 * REG_WIDTH) -#define REG_PCIE_ERROR_COUNT (10 * REG_WIDTH) +#define REG_WIDTH 8 +#define REG_MAGIC_NUMBER (0 * REG_WIDTH) +#define REG_CARD_ID_AND_BUILD (1 * REG_WIDTH) +#define REG_DATE_AND_TIME_STAMPS (2 * REG_WIDTH) +#define REG_CORE_TABLE_OFFSET (3 * REG_WIDTH) +#define REG_FPGA_SSID (4 * REG_WIDTH) +#define REG_FPGA_HW_ID (5 * REG_WIDTH) +#define REG_FPGA_DDNA (6 * REG_WIDTH) +#define REG_CPLD_CONFIG (7 * REG_WIDTH) +#define REG_INTERRUPT_MASK (8 * REG_WIDTH) +#define REG_INTERRUPT_ACTIVE (9 * REG_WIDTH) +#define REG_PCIE_ERROR_COUNT (10 * REG_WIDTH) -#define KP2000_MAGIC_VALUE 0x196C61482231894D +#define KP2000_MAGIC_VALUE 0x196C61482231894DULL -#define PCI_VENDOR_ID_DAKTRONICS 0x1c33 -#define PCI_DEVICE_ID_DAKTRONICS 0x6021 +#define PCI_VENDOR_ID_DAKTRONICS 0x1c33 +#define PCI_DEVICE_ID_DAKTRONICS 0x6021 -#define DMA_BAR 0 -#define REG_BAR 1 +#define DMA_BAR 0 +#define REG_BAR 1 struct kp2000_device { - struct pci_dev *pdev; - struct miscdevice miscdev; - char name[16]; - - unsigned int card_num; - struct mutex sem; - - void __iomem *sysinfo_regs_base; - void __iomem *regs_bar_base; - struct resource regs_base_resource; - void __iomem *dma_bar_base; - void __iomem *dma_common_regs; - struct resource dma_base_resource; - - // "System Registers" - u32 card_id; - u32 build_version; - u32 build_datestamp; - u32 build_timestamp; - u32 core_table_offset; - u32 core_table_length; - u8 core_table_rev; - u8 hardware_revision; - u64 ssid; - u64 ddna; - - // IRQ stuff - unsigned int irq; - - struct list_head uio_devices_list; + struct pci_dev *pdev; + char name[16]; + + unsigned int card_num; + struct mutex sem; + + void __iomem *sysinfo_regs_base; + void __iomem *regs_bar_base; + struct resource regs_base_resource; + void __iomem *dma_bar_base; + void __iomem *dma_common_regs; + struct resource dma_base_resource; + + // "System Registers" + u32 card_id; + u32 build_version; + u32 build_datestamp; + u32 build_timestamp; + u32 core_table_offset; + u32 core_table_length; + u8 core_table_rev; + u8 hardware_revision; + u64 ssid; + u64 ddna; + + // IRQ stuff + unsigned int irq; + + struct list_head uio_devices_list; }; extern struct class *kpc_uio_class; extern struct attribute *kpc_uio_class_attrs[]; -int kp2000_pcie_probe(struct pci_dev *dev, const struct pci_device_id *id); -void kp2000_pcie_remove(struct pci_dev *pdev); -int kp2000_probe_cores(struct kp2000_device *pcard); -void kp2000_remove_cores(struct kp2000_device *pcard); - -extern struct file_operations kp2000_fops; - +int kp2000_probe_cores(struct kp2000_device *pcard); +void kp2000_remove_cores(struct kp2000_device *pcard); // Define this quick little macro because the expression is used frequently -#define PCARD_TO_DEV(pcard) (&(pcard->pdev->dev)) - -static inline void -lock_card(struct kp2000_device *pcard) -{ - BUG_ON(pcard == NULL); - mutex_lock(&pcard->sem); -} -static inline void -unlock_card(struct kp2000_device *pcard) -{ - BUG_ON(pcard == NULL); - mutex_unlock(&pcard->sem); -} - +#define PCARD_TO_DEV(pcard) (&(pcard->pdev->dev)) #endif /* KP2000_PCIE_H */ diff --git a/drivers/staging/kpc2000/kpc2000/uapi.h b/drivers/staging/kpc2000/kpc2000/uapi.h index ef8008bcd33d..16f37f002dc6 100644 --- a/drivers/staging/kpc2000/kpc2000/uapi.h +++ b/drivers/staging/kpc2000/kpc2000/uapi.h @@ -5,18 +5,18 @@ #include <linux/ioctl.h> struct kp2000_regs { - __u32 card_id; - __u32 build_version; - __u32 build_datestamp; - __u32 build_timestamp; - __u32 hw_rev; - __u64 ssid; - __u64 ddna; - __u64 cpld_reg; + __u32 card_id; + __u32 build_version; + __u32 build_datestamp; + __u32 build_timestamp; + __u32 hw_rev; + __u64 ssid; + __u64 ddna; + __u64 cpld_reg; }; -#define KP2000_IOCTL_GET_CPLD_REG _IOR('k', 9, __u32) -#define KP2000_IOCTL_GET_PCIE_ERROR_REG _IOR('k', 11, __u32) -#define KP2000_IOCTL_GET_EVERYTHING _IOR('k', 8, struct kp2000_regs*) +#define KP2000_IOCTL_GET_CPLD_REG _IOR('k', 9, __u32) +#define KP2000_IOCTL_GET_PCIE_ERROR_REG _IOR('k', 11, __u32) +#define KP2000_IOCTL_GET_EVERYTHING _IOR('k', 8, struct kp2000_regs*) #endif /* KP2000_CDEV_UAPI_H_ */ diff --git a/drivers/staging/kpc2000/kpc2000_i2c.c b/drivers/staging/kpc2000/kpc2000_i2c.c new file mode 100644 index 000000000000..b108da4ac633 --- /dev/null +++ b/drivers/staging/kpc2000/kpc2000_i2c.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * KPC2000 i2c driver + * + * Adapted i2c-i801.c for use with Kadoka hardware. + * + * Copyright (C) 1998 - 2002 + * Frodo Looijaard <frodol@dds.nl>, + * Philip Edelbrock <phil@netroedge.com>, + * Mark D. Studebaker <mdsxyz123@yahoo.com> + * Copyright (C) 2007 - 2012 + * Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2010 Intel Corporation + * David Woodhouse <dwmw2@infradead.org> + * Copyright (C) 2014-2018 Daktronics + * Matt Sickler <matt.sickler@daktronics.com>, + * Jordon Hofer <jordon.hofer@daktronics.com> + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/fs.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include "kpc.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matt.Sickler@Daktronics.com"); + +struct i2c_device { + unsigned long smba; + struct i2c_adapter adapter; + unsigned int features; +}; + +/***************************** + *** Part 1 - i2c Handlers *** + *****************************/ + +#define REG_SIZE 8 + +/* I801 SMBus address offsets */ +#define SMBHSTSTS(p) ((0 * REG_SIZE) + (p)->smba) +#define SMBHSTCNT(p) ((2 * REG_SIZE) + (p)->smba) +#define SMBHSTCMD(p) ((3 * REG_SIZE) + (p)->smba) +#define SMBHSTADD(p) ((4 * REG_SIZE) + (p)->smba) +#define SMBHSTDAT0(p) ((5 * REG_SIZE) + (p)->smba) +#define SMBHSTDAT1(p) ((6 * REG_SIZE) + (p)->smba) +#define SMBBLKDAT(p) ((7 * REG_SIZE) + (p)->smba) +#define SMBPEC(p) ((8 * REG_SIZE) + (p)->smba) /* ICH3 and later */ +#define SMBAUXSTS(p) ((12 * REG_SIZE) + (p)->smba) /* ICH4 and later */ +#define SMBAUXCTL(p) ((13 * REG_SIZE) + (p)->smba) /* ICH4 and later */ + +/* PCI Address Constants */ +#define SMBBAR 4 +#define SMBHSTCFG 0x040 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Auxiliary control register bits, ICH4+ only */ +#define SMBAUXCTL_CRC 1 +#define SMBAUXCTL_E32B 2 + +/* kill bit for SMBHSTCNT */ +#define SMBHSTCNT_KILL 2 + +/* Other settings */ +#define MAX_RETRIES 400 +#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */ +#define I801_START 0x40 +#define I801_PEC_EN 0x80 /* ICH3 and later */ + +/* I801 Hosts Status register bits */ +#define SMBHSTSTS_BYTE_DONE 0x80 +#define SMBHSTSTS_INUSE_STS 0x40 +#define SMBHSTSTS_SMBALERT_STS 0x20 +#define SMBHSTSTS_FAILED 0x10 +#define SMBHSTSTS_BUS_ERR 0x08 +#define SMBHSTSTS_DEV_ERR 0x04 +#define SMBHSTSTS_INTR 0x02 +#define SMBHSTSTS_HOST_BUSY 0x01 + +#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | SMBHSTSTS_INTR) + +/* Older devices have their ID defined in <linux/pci_ids.h> */ +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 +/* Patsburg also has three 'Integrated Device Function' SMBus controllers */ +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 + +#define FEATURE_SMBUS_PEC BIT(0) +#define FEATURE_BLOCK_BUFFER BIT(1) +#define FEATURE_BLOCK_PROC BIT(2) +#define FEATURE_I2C_BLOCK_READ BIT(3) +/* Not really a feature, but it's convenient to handle it as such */ +#define FEATURE_IDF BIT(15) + +// FIXME! +#undef inb_p +#define inb_p(a) readq((void *)a) +#undef outb_p +#define outb_p(d, a) writeq(d, (void *)a) + +/* Make sure the SMBus host is ready to start transmitting. + * Return 0 if it is, -EBUSY if it is not. + */ +static int i801_check_pre(struct i2c_device *priv) +{ + int status; + + status = inb_p(SMBHSTSTS(priv)); + if (status & SMBHSTSTS_HOST_BUSY) { + dev_err(&priv->adapter.dev, "SMBus is busy, can't use it! (status=%x)\n", status); + return -EBUSY; + } + + status &= STATUS_FLAGS; + if (status) { + //dev_dbg(&priv->adapter.dev, "Clearing status flags (%02x)\n", status); + outb_p(status, SMBHSTSTS(priv)); + status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; + if (status) { + dev_err(&priv->adapter.dev, "Failed clearing status flags (%02x)\n", status); + return -EBUSY; + } + } + return 0; +} + +/* Convert the status register to an error code, and clear it. */ +static int i801_check_post(struct i2c_device *priv, int status, int timeout) +{ + int result = 0; + + /* If the SMBus is still busy, we give up */ + if (timeout) { + dev_err(&priv->adapter.dev, "Transaction timeout\n"); + /* try to stop the current command */ + dev_dbg(&priv->adapter.dev, "Terminating the current operation\n"); + outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, SMBHSTCNT(priv)); + usleep_range(1000, 2000); + outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL), SMBHSTCNT(priv)); + + /* Check if it worked */ + status = inb_p(SMBHSTSTS(priv)); + if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) + dev_err(&priv->adapter.dev, "Failed terminating the transaction\n"); + outb_p(STATUS_FLAGS, SMBHSTSTS(priv)); + return -ETIMEDOUT; + } + + if (status & SMBHSTSTS_FAILED) { + result = -EIO; + dev_err(&priv->adapter.dev, "Transaction failed\n"); + } + if (status & SMBHSTSTS_DEV_ERR) { + result = -ENXIO; + dev_dbg(&priv->adapter.dev, "No response\n"); + } + if (status & SMBHSTSTS_BUS_ERR) { + result = -EAGAIN; + dev_dbg(&priv->adapter.dev, "Lost arbitration\n"); + } + + if (result) { + /* Clear error flags */ + outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); + status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; + if (status) + dev_warn(&priv->adapter.dev, "Failed clearing status flags at end of transaction (%02x)\n", status); + } + + return result; +} + +static int i801_transaction(struct i2c_device *priv, int xact) +{ + int status; + int result; + int timeout = 0; + + result = i801_check_pre(priv); + if (result < 0) + return result; + /* the current contents of SMBHSTCNT can be overwritten, since PEC, + * INTREN, SMBSCMD are passed in xact + */ + outb_p(xact | I801_START, SMBHSTCNT(priv)); + + /* We will always wait for a fraction of a second! */ + do { + usleep_range(250, 500); + status = inb_p(SMBHSTSTS(priv)); + } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES)); + + result = i801_check_post(priv, status, timeout > MAX_RETRIES); + if (result < 0) + return result; + + outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); + return 0; +} + +/* wait for INTR bit as advised by Intel */ +static void i801_wait_hwpec(struct i2c_device *priv) +{ + int timeout = 0; + int status; + + do { + usleep_range(250, 500); + status = inb_p(SMBHSTSTS(priv)); + } while ((!(status & SMBHSTSTS_INTR)) && (timeout++ < MAX_RETRIES)); + + if (timeout > MAX_RETRIES) + dev_dbg(&priv->adapter.dev, "PEC Timeout!\n"); + + outb_p(status, SMBHSTSTS(priv)); +} + +static int i801_block_transaction_by_block(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int hwpec) +{ + int i, len; + int status; + + inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */ + + /* Use 32-byte buffer to process this transaction */ + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + outb_p(len, SMBHSTDAT0(priv)); + for (i = 0; i < len; i++) + outb_p(data->block[i + 1], SMBBLKDAT(priv)); + } + + status = i801_transaction(priv, I801_BLOCK_DATA | ENABLE_INT9 | I801_PEC_EN * hwpec); + if (status) + return status; + + if (read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0(priv)); + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + + data->block[0] = len; + for (i = 0; i < len; i++) + data->block[i + 1] = inb_p(SMBBLKDAT(priv)); + } + return 0; +} + +static int i801_block_transaction_byte_by_byte(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec) +{ + int i, len; + int smbcmd; + int status; + int result; + int timeout; + + result = i801_check_pre(priv); + if (result < 0) + return result; + + len = data->block[0]; + + if (read_write == I2C_SMBUS_WRITE) { + outb_p(len, SMBHSTDAT0(priv)); + outb_p(data->block[1], SMBBLKDAT(priv)); + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) { + if (command == I2C_SMBUS_I2C_BLOCK_DATA) + smbcmd = I801_I2C_BLOCK_LAST; + else + smbcmd = I801_BLOCK_LAST; + } else { + if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) + smbcmd = I801_I2C_BLOCK_DATA; + else + smbcmd = I801_BLOCK_DATA; + } + outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv)); + + if (i == 1) + outb_p(inb(SMBHSTCNT(priv)) | I801_START, SMBHSTCNT(priv)); + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + usleep_range(250, 500); + status = inb_p(SMBHSTSTS(priv)); + } while ((!(status & SMBHSTSTS_BYTE_DONE)) && (timeout++ < MAX_RETRIES)); + + result = i801_check_post(priv, status, timeout > MAX_RETRIES); + if (result < 0) + return result; + if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) { + len = inb_p(SMBHSTDAT0(priv)); + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { + dev_err(&priv->adapter.dev, "Illegal SMBus block read size %d\n", len); + /* Recover */ + while (inb_p(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) + outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); + outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); + return -EPROTO; + } + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT(priv)); + if (read_write == I2C_SMBUS_WRITE && i + 1 <= len) + outb_p(data->block[i + 1], SMBBLKDAT(priv)); + /* signals SMBBLKDAT ready */ + outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv)); + } + + return 0; +} + +static int i801_set_block_buffer_mode(struct i2c_device *priv) +{ + outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv)); + if ((inb_p(SMBAUXCTL(priv)) & SMBAUXCTL_E32B) == 0) + return -EIO; + return 0; +} + +/* Block transaction function */ +static int i801_block_transaction(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec) +{ + int result = 0; + //unsigned char hostc; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + //TODO: Figure out the right thing to do here... + //pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc); + //pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN); + } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) { + dev_err(&priv->adapter.dev, "I2C block read is unsupported!\n"); + return -EOPNOTSUPP; + } + } + + if (read_write == I2C_SMBUS_WRITE || command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (data->block[0] < 1) + data->block[0] = 1; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + } else { + data->block[0] = 32; /* max for SMBus block reads */ + } + + /* Experience has shown that the block buffer can only be used for + * SMBus (not I2C) block transactions, even though the datasheet + * doesn't mention this limitation. + */ + if ((priv->features & FEATURE_BLOCK_BUFFER) && command != I2C_SMBUS_I2C_BLOCK_DATA && i801_set_block_buffer_mode(priv) == 0) + result = i801_block_transaction_by_block(priv, data, read_write, hwpec); + else + result = i801_block_transaction_byte_by_byte(priv, data, read_write, command, hwpec); + if (result == 0 && hwpec) + i801_wait_hwpec(priv); + if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_WRITE) { + /* restore saved configuration register value */ + //TODO: Figure out the right thing to do here... + //pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return negative errno on error. */ +static s32 i801_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) +{ + int hwpec; + int block = 0; + int ret, xact = 0; + struct i2c_device *priv = i2c_get_adapdata(adap); + + hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA; + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(&priv->adapter.dev, " [acc] SMBUS_QUICK\n"); + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); + xact = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BYTE\n"); + + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD(priv)); + xact = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BYTE_DATA\n"); + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0(priv)); + xact = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + dev_dbg(&priv->adapter.dev, " [acc] SMBUS_WORD_DATA\n"); + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0(priv)); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BLOCK_DATA\n"); + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + block = 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + dev_dbg(&priv->adapter.dev, " [acc] SMBUS_I2C_BLOCK_DATA\n"); + /* NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading + */ + outb_p((addr & 0x7f) << 1, SMBHSTADD(priv)); + if (read_write == I2C_SMBUS_READ) { + /* NB: page 240 of ICH5 datasheet also shows + * that DATA1 is the cmd field when reading + */ + outb_p(command, SMBHSTDAT1(priv)); + } else { + outb_p(command, SMBHSTCMD(priv)); + } + block = 1; + break; + default: + dev_dbg(&priv->adapter.dev, " [acc] Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (hwpec) { /* enable/disable hardware PEC */ + dev_dbg(&priv->adapter.dev, " [acc] hwpec: yes\n"); + outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); + } else { + dev_dbg(&priv->adapter.dev, " [acc] hwpec: no\n"); + outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), SMBAUXCTL(priv)); + } + + if (block) { + //ret = 0; + dev_dbg(&priv->adapter.dev, " [acc] block: yes\n"); + ret = i801_block_transaction(priv, data, read_write, size, hwpec); + } else { + dev_dbg(&priv->adapter.dev, " [acc] block: no\n"); + ret = i801_transaction(priv, xact | ENABLE_INT9); + } + + /* Some BIOSes don't like it when PEC is enabled at reboot or resume + * time, so we forcibly disable it after every transaction. Turn off + * E32B for the same reason. + */ + if (hwpec || block) { + dev_dbg(&priv->adapter.dev, " [acc] hwpec || block\n"); + outb_p(inb_p(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + } + if (block) { + dev_dbg(&priv->adapter.dev, " [acc] block\n"); + return ret; + } + if (ret) { + dev_dbg(&priv->adapter.dev, " [acc] ret %d\n", ret); + return ret; + } + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) { + dev_dbg(&priv->adapter.dev, " [acc] I2C_SMBUS_WRITE || I801_QUICK -> ret 0\n"); + return 0; + } + + switch (xact & 0x7f) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + case I801_BYTE_DATA: + dev_dbg(&priv->adapter.dev, " [acc] I801_BYTE or I801_BYTE_DATA\n"); + data->byte = inb_p(SMBHSTDAT0(priv)); + break; + case I801_WORD_DATA: + dev_dbg(&priv->adapter.dev, " [acc] I801_WORD_DATA\n"); + data->word = inb_p(SMBHSTDAT0(priv)) + (inb_p(SMBHSTDAT1(priv)) << 8); + break; + } + return 0; +} + +static u32 i801_func(struct i2c_adapter *adapter) +{ + struct i2c_device *priv = i2c_get_adapdata(adapter); + + /* original settings + * u32 f = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + * I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + * I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | + * ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | + * ((priv->features & FEATURE_I2C_BLOCK_READ) ? + * I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0); + */ + + // http://lxr.free-electrons.com/source/include/uapi/linux/i2c.h#L85 + + u32 f = + I2C_FUNC_I2C | /* 0x00000001 (I enabled this one) */ + !I2C_FUNC_10BIT_ADDR | /* 0x00000002 */ + !I2C_FUNC_PROTOCOL_MANGLING | /* 0x00000004 */ + ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | /* 0x00000008 */ + !I2C_FUNC_SMBUS_BLOCK_PROC_CALL | /* 0x00008000 */ + I2C_FUNC_SMBUS_QUICK | /* 0x00010000 */ + !I2C_FUNC_SMBUS_READ_BYTE | /* 0x00020000 */ + !I2C_FUNC_SMBUS_WRITE_BYTE | /* 0x00040000 */ + !I2C_FUNC_SMBUS_READ_BYTE_DATA | /* 0x00080000 */ + !I2C_FUNC_SMBUS_WRITE_BYTE_DATA | /* 0x00100000 */ + !I2C_FUNC_SMBUS_READ_WORD_DATA | /* 0x00200000 */ + !I2C_FUNC_SMBUS_WRITE_WORD_DATA | /* 0x00400000 */ + !I2C_FUNC_SMBUS_PROC_CALL | /* 0x00800000 */ + !I2C_FUNC_SMBUS_READ_BLOCK_DATA | /* 0x01000000 */ + !I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | /* 0x02000000 */ + ((priv->features & FEATURE_I2C_BLOCK_READ) ? I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0) | /* 0x04000000 */ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | /* 0x08000000 */ + + I2C_FUNC_SMBUS_BYTE | /* _READ_BYTE _WRITE_BYTE */ + I2C_FUNC_SMBUS_BYTE_DATA | /* _READ_BYTE_DATA _WRITE_BYTE_DATA */ + I2C_FUNC_SMBUS_WORD_DATA | /* _READ_WORD_DATA _WRITE_WORD_DATA */ + I2C_FUNC_SMBUS_BLOCK_DATA | /* _READ_BLOCK_DATA _WRITE_BLOCK_DATA */ + !I2C_FUNC_SMBUS_I2C_BLOCK | /* _READ_I2C_BLOCK _WRITE_I2C_BLOCK */ + !I2C_FUNC_SMBUS_EMUL; /* _QUICK _BYTE _BYTE_DATA _WORD_DATA _PROC_CALL _WRITE_BLOCK_DATA _I2C_BLOCK _PEC */ + return f; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = i801_access, + .functionality = i801_func, +}; + +/******************************** + *** Part 2 - Driver Handlers *** + ********************************/ +static int pi2c_probe(struct platform_device *pldev) +{ + int err; + struct i2c_device *priv; + struct resource *res; + + priv = devm_kzalloc(&pldev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + priv->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + priv->adapter.algo = &smbus_algorithm; + + res = platform_get_resource(pldev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + priv->smba = (unsigned long)devm_ioremap_nocache(&pldev->dev, + res->start, + resource_size(res)); + if (!priv->smba) + return -ENOMEM; + + platform_set_drvdata(pldev, priv); + + priv->features |= FEATURE_IDF; + priv->features |= FEATURE_I2C_BLOCK_READ; + priv->features |= FEATURE_SMBUS_PEC; + priv->features |= FEATURE_BLOCK_BUFFER; + + //init_MUTEX(&lddata->sem); + + /* set up the sysfs linkage to our parent device */ + priv->adapter.dev.parent = &pldev->dev; + + /* Retry up to 3 times on lost arbitration */ + priv->adapter.retries = 3; + + //snprintf(priv->adapter.name, sizeof(priv->adapter.name), "Fake SMBus I801 adapter at %04lx", priv->smba); + snprintf(priv->adapter.name, sizeof(priv->adapter.name), "Fake SMBus I801 adapter"); + + err = i2c_add_adapter(&priv->adapter); + if (err) { + dev_err(&priv->adapter.dev, "Failed to add SMBus adapter\n"); + return err; + } + + return 0; +} + +static int pi2c_remove(struct platform_device *pldev) +{ + struct i2c_device *lddev; + + lddev = (struct i2c_device *)platform_get_drvdata(pldev); + + i2c_del_adapter(&lddev->adapter); + + //TODO: Figure out the right thing to do here... + //pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); + //pci_release_region(dev, SMBBAR); + //pci_set_drvdata(dev, NULL); + + //cdev_del(&lddev->cdev); + + return 0; +} + +static struct platform_driver i2c_plat_driver_i = { + .probe = pi2c_probe, + .remove = pi2c_remove, + .driver = { + .name = KP_DRIVER_NAME_I2C, + }, +}; + +module_platform_driver(i2c_plat_driver_i); diff --git a/drivers/staging/kpc2000/kpc2000_spi.c b/drivers/staging/kpc2000/kpc2000_spi.c new file mode 100644 index 000000000000..35ac1d7070b3 --- /dev/null +++ b/drivers/staging/kpc2000/kpc2000_spi.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * KP2000 SPI controller driver + * + * Copyright (C) 2014-2018 Daktronics + * Author: Matt Sickler <matt.sickler@daktronics.com> + * Very loosely based on spi-omap2-mcspi.c + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/gcd.h> +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> +#include <linux/mtd/partitions.h> + +#include "kpc.h" + +static struct mtd_partition p2kr0_spi0_parts[] = { + { .name = "SLOT_0", .size = 7798784, .offset = 0, }, + { .name = "SLOT_1", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_2", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_3", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "CS0_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, +}; + +static struct mtd_partition p2kr0_spi1_parts[] = { + { .name = "SLOT_4", .size = 7798784, .offset = 0, }, + { .name = "SLOT_5", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_6", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_7", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "CS1_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, +}; + +static struct flash_platform_data p2kr0_spi0_pdata = { + .name = "SPI0", + .nr_parts = ARRAY_SIZE(p2kr0_spi0_parts), + .parts = p2kr0_spi0_parts, +}; +static struct flash_platform_data p2kr0_spi1_pdata = { + .name = "SPI1", + .nr_parts = ARRAY_SIZE(p2kr0_spi1_parts), + .parts = p2kr0_spi1_parts, +}; + +static struct spi_board_info p2kr0_board_info[] = { + { + .modalias = "n25q256a11", + .bus_num = 1, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = &p2kr0_spi0_pdata + }, + { + .modalias = "n25q256a11", + .bus_num = 1, + .chip_select = 1, + .mode = SPI_MODE_0, + .platform_data = &p2kr0_spi1_pdata + }, +}; + +/*************** + * SPI Defines * + ***************/ +#define KP_SPI_REG_CONFIG 0x0 /* 0x00 */ +#define KP_SPI_REG_STATUS 0x1 /* 0x08 */ +#define KP_SPI_REG_FFCTRL 0x2 /* 0x10 */ +#define KP_SPI_REG_TXDATA 0x3 /* 0x18 */ +#define KP_SPI_REG_RXDATA 0x4 /* 0x20 */ + +#define KP_SPI_CLK 48000000 +#define KP_SPI_MAX_FIFODEPTH 64 +#define KP_SPI_MAX_FIFOWCNT 0xFFFF + +#define KP_SPI_REG_CONFIG_TRM_TXRX 0 +#define KP_SPI_REG_CONFIG_TRM_RX 1 +#define KP_SPI_REG_CONFIG_TRM_TX 2 + +#define KP_SPI_REG_STATUS_RXS 0x01 +#define KP_SPI_REG_STATUS_TXS 0x02 +#define KP_SPI_REG_STATUS_EOT 0x04 +#define KP_SPI_REG_STATUS_TXFFE 0x10 +#define KP_SPI_REG_STATUS_TXFFF 0x20 +#define KP_SPI_REG_STATUS_RXFFE 0x40 +#define KP_SPI_REG_STATUS_RXFFF 0x80 + +/****************** + * SPI Structures * + ******************/ +struct kp_spi { + struct spi_master *master; + u64 __iomem *base; + struct device *dev; +}; + +struct kp_spi_controller_state { + void __iomem *base; + s64 conf_cache; +}; + +union kp_spi_config { + /* use this to access individual elements */ + struct __packed spi_config_bitfield { + unsigned int pha : 1; /* spim_clk Phase */ + unsigned int pol : 1; /* spim_clk Polarity */ + unsigned int epol : 1; /* spim_csx Polarity */ + unsigned int dpe : 1; /* Transmission Enable */ + unsigned int wl : 5; /* Word Length */ + unsigned int : 3; + unsigned int trm : 2; /* TxRx Mode */ + unsigned int cs : 4; /* Chip Select */ + unsigned int wcnt : 7; /* Word Count */ + unsigned int ffen : 1; /* FIFO Enable */ + unsigned int spi_en : 1; /* SPI Enable */ + unsigned int : 5; + } bitfield; + /* use this to grab the whole register */ + u32 reg; +}; + +union kp_spi_status { + struct __packed spi_status_bitfield { + unsigned int rx : 1; /* Rx Status */ + unsigned int tx : 1; /* Tx Status */ + unsigned int eo : 1; /* End of Transfer */ + unsigned int : 1; + unsigned int txffe : 1; /* Tx FIFO Empty */ + unsigned int txfff : 1; /* Tx FIFO Full */ + unsigned int rxffe : 1; /* Rx FIFO Empty */ + unsigned int rxfff : 1; /* Rx FIFO Full */ + unsigned int : 24; + } bitfield; + u32 reg; +}; + +union kp_spi_ffctrl { + struct __packed spi_ffctrl_bitfield { + unsigned int ffstart : 1; /* FIFO Start */ + unsigned int : 31; + } bitfield; + u32 reg; +}; + +/*************** + * SPI Helpers * + ***************/ + static inline u64 +kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx) +{ + u64 __iomem *addr = cs->base; + u64 val; + + addr += idx; + if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)) + return cs->conf_cache; + + val = readq(addr); + return val; +} + + static inline void +kp_spi_write_reg(struct kp_spi_controller_state *cs, int idx, u64 val) +{ + u64 __iomem *addr = cs->base; + + addr += idx; + writeq(val, addr); + if (idx == KP_SPI_REG_CONFIG) + cs->conf_cache = val; +} + + static int +kp_spi_wait_for_reg_bit(struct kp_spi_controller_state *cs, int idx, + unsigned long bit) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(kp_spi_read_reg(cs, idx) & bit)) { + if (time_after(jiffies, timeout)) { + if (!(kp_spi_read_reg(cs, idx) & bit)) + return -ETIMEDOUT; + else + return 0; + } + cpu_relax(); + } + return 0; +} + + static unsigned +kp_spi_txrx_pio(struct spi_device *spidev, struct spi_transfer *transfer) +{ + struct kp_spi_controller_state *cs = spidev->controller_state; + unsigned int count = transfer->len; + unsigned int c = count; + + int i; + int res; + u8 *rx = transfer->rx_buf; + const u8 *tx = transfer->tx_buf; + int processed = 0; + + if (tx) { + for (i = 0 ; i < c ; i++) { + char val = *tx++; + + res = kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, + KP_SPI_REG_STATUS_TXS); + if (res < 0) + goto out; + + kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, val); + processed++; + } + } + else if (rx) { + for (i = 0 ; i < c ; i++) { + char test = 0; + + kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, 0x00); + res = kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, + KP_SPI_REG_STATUS_RXS); + if (res < 0) + goto out; + + test = kp_spi_read_reg(cs, KP_SPI_REG_RXDATA); + *rx++ = test; + processed++; + } + } + + if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, + KP_SPI_REG_STATUS_EOT) < 0) { + //TODO: Figure out how to abort transaction?? + //Ths has never happened in practice though... + } + +out: + return processed; +} + +/***************** + * SPI Functions * + *****************/ + static int +kp_spi_setup(struct spi_device *spidev) +{ + union kp_spi_config sc; + struct kp_spi *kpspi = spi_master_get_devdata(spidev->master); + struct kp_spi_controller_state *cs; + + /* setup controller state */ + cs = spidev->controller_state; + if (!cs) { + cs = kzalloc(sizeof(*cs), GFP_KERNEL); + if (!cs) + return -ENOMEM; + cs->base = kpspi->base; + cs->conf_cache = -1; + spidev->controller_state = cs; + } + + /* set config register */ + sc.bitfield.wl = spidev->bits_per_word - 1; + sc.bitfield.cs = spidev->chip_select; + sc.bitfield.spi_en = 0; + sc.bitfield.trm = 0; + sc.bitfield.ffen = 0; + kp_spi_write_reg(spidev->controller_state, KP_SPI_REG_CONFIG, sc.reg); + return 0; +} + + static int +kp_spi_transfer_one_message(struct spi_master *master, struct spi_message *m) +{ + struct kp_spi_controller_state *cs; + struct spi_device *spidev; + struct kp_spi *kpspi; + struct spi_transfer *transfer; + union kp_spi_config sc; + int status = 0; + + spidev = m->spi; + kpspi = spi_master_get_devdata(master); + m->actual_length = 0; + m->status = 0; + + cs = spidev->controller_state; + + /* reject invalid messages and transfers */ + if (list_empty(&m->transfers)) + return -EINVAL; + + /* validate input */ + list_for_each_entry(transfer, &m->transfers, transfer_list) { + const void *tx_buf = transfer->tx_buf; + void *rx_buf = transfer->rx_buf; + unsigned int len = transfer->len; + + if (transfer->speed_hz > KP_SPI_CLK || + (len && !(rx_buf || tx_buf))) { + dev_dbg(kpspi->dev, " transfer: %d Hz, %d %s%s, %d bpw\n", + transfer->speed_hz, + len, + tx_buf ? "tx" : "", + rx_buf ? "rx" : "", + transfer->bits_per_word); + dev_dbg(kpspi->dev, " transfer -EINVAL\n"); + return -EINVAL; + } + if (transfer->speed_hz && + transfer->speed_hz < (KP_SPI_CLK >> 15)) { + dev_dbg(kpspi->dev, "speed_hz %d below minimum %d Hz\n", + transfer->speed_hz, + KP_SPI_CLK >> 15); + dev_dbg(kpspi->dev, " speed_hz -EINVAL\n"); + return -EINVAL; + } + } + + /* assert chip select to start the sequence*/ + sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); + sc.bitfield.spi_en = 1; + kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); + + /* work */ + if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, + KP_SPI_REG_STATUS_EOT) < 0) { + dev_info(kpspi->dev, "EOT timed out\n"); + goto out; + } + + /* do the transfers for this message */ + list_for_each_entry(transfer, &m->transfers, transfer_list) { + if (!transfer->tx_buf && !transfer->rx_buf && + transfer->len) { + status = -EINVAL; + goto error; + } + + /* transfer */ + if (transfer->len) { + unsigned int word_len = spidev->bits_per_word; + unsigned int count; + + /* set up the transfer... */ + sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); + + /* ...direction */ + if (transfer->tx_buf) + sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_TX; + else if (transfer->rx_buf) + sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_RX; + + /* ...word length */ + if (transfer->bits_per_word) + word_len = transfer->bits_per_word; + sc.bitfield.wl = word_len - 1; + + /* ...chip select */ + sc.bitfield.cs = spidev->chip_select; + + /* ...and write the new settings */ + kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); + + /* do the transfer */ + count = kp_spi_txrx_pio(spidev, transfer); + m->actual_length += count; + + if (count != transfer->len) { + status = -EIO; + goto error; + } + } + + if (transfer->delay_usecs) + udelay(transfer->delay_usecs); + } + + /* de-assert chip select to end the sequence */ + sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); + sc.bitfield.spi_en = 0; + kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); + +out: + /* done work */ + spi_finalize_current_message(master); + return 0; + +error: + m->status = status; + return status; +} + + static void +kp_spi_cleanup(struct spi_device *spidev) +{ + struct kp_spi_controller_state *cs = spidev->controller_state; + + if (cs) + kfree(cs); +} + +/****************** + * Probe / Remove * + ******************/ + static int +kp_spi_probe(struct platform_device *pldev) +{ + struct kpc_core_device_platdata *drvdata; + struct spi_master *master; + struct kp_spi *kpspi; + struct resource *r; + int status = 0; + int i; + + drvdata = pldev->dev.platform_data; + if (!drvdata) { + dev_err(&pldev->dev, "%s: platform_data is NULL\n", __func__); + return -ENODEV; + } + + master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi)); + if (!master) { + dev_err(&pldev->dev, "%s: master allocation failed\n", + __func__); + return -ENOMEM; + } + + /* set up the spi functions */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = (unsigned int)SPI_BPW_RANGE_MASK(4, 32); + master->setup = kp_spi_setup; + master->transfer_one_message = kp_spi_transfer_one_message; + master->cleanup = kp_spi_cleanup; + + platform_set_drvdata(pldev, master); + + kpspi = spi_master_get_devdata(master); + kpspi->master = master; + kpspi->dev = &pldev->dev; + + master->num_chipselect = 4; + if (pldev->id != -1) + master->bus_num = pldev->id; + + r = platform_get_resource(pldev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pldev->dev, "%s: Unable to get platform resources\n", + __func__); + status = -ENODEV; + goto free_master; + } + + kpspi->base = devm_ioremap_nocache(&pldev->dev, r->start, + resource_size(r)); + + status = spi_register_master(master); + if (status < 0) { + dev_err(&pldev->dev, "Unable to register SPI device\n"); + goto free_master; + } + + /* register the slave boards */ +#define NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(table) \ + for (i = 0 ; i < ARRAY_SIZE(table) ; i++) { \ + spi_new_device(master, &(table[i])); \ + } + + switch ((drvdata->card_id & 0xFFFF0000) >> 16) { + case PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0: + NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(p2kr0_board_info); + break; + default: + dev_err(&pldev->dev, "Unknown hardware, cant know what partition table to use!\n"); + goto free_master; + } + + return status; + +free_master: + spi_master_put(master); + return status; +} + + static int +kp_spi_remove(struct platform_device *pldev) +{ + struct spi_master *master = platform_get_drvdata(pldev); + + spi_unregister_master(master); + return 0; +} + +static struct platform_driver kp_spi_driver = { + .driver = { + .name = KP_DRIVER_NAME_SPI, + }, + .probe = kp_spi_probe, + .remove = kp_spi_remove, +}; + +module_platform_driver(kp_spi_driver); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kp_spi"); diff --git a/drivers/staging/kpc2000/kpc_dma/dma.c b/drivers/staging/kpc2000/kpc_dma/dma.c index 6959bac11388..51a4dd534a0d 100644 --- a/drivers/staging/kpc2000/kpc_dma/dma.c +++ b/drivers/staging/kpc2000/kpc_dma/dma.c @@ -2,7 +2,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/types.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/platform_device.h> @@ -14,11 +14,11 @@ static irqreturn_t ndd_irq_handler(int irq, void *dev_id) { - struct kpc_dma_device *ldev = (struct kpc_dma_device*)dev_id; - + struct kpc_dma_device *ldev = (struct kpc_dma_device *)dev_id; + if ((GetEngineControl(ldev) & ENG_CTL_IRQ_ACTIVE) || (ldev->desc_completed->MyDMAAddr != GetEngineCompletePtr(ldev))) schedule_work(&ldev->irq_work); - + return IRQ_HANDLED; } @@ -27,217 +27,215 @@ void ndd_irq_worker(struct work_struct *ws) { struct kpc_dma_descriptor *cur; struct kpc_dma_device *eng = container_of(ws, struct kpc_dma_device, irq_work); + lock_engine(eng); - + if (GetEngineCompletePtr(eng) == 0) goto out; - + if (eng->desc_completed->MyDMAAddr == GetEngineCompletePtr(eng)) goto out; - + cur = eng->desc_completed; do { cur = cur->Next; dev_dbg(&eng->pldev->dev, "Handling completed descriptor %p (acd = %p)\n", cur, cur->acd); BUG_ON(cur == eng->desc_next); // Ordering failure. - - if (cur->DescControlFlags & DMA_DESC_CTL_SOP){ + + if (cur->DescControlFlags & DMA_DESC_CTL_SOP) { eng->accumulated_bytes = 0; eng->accumulated_flags = 0; } - + eng->accumulated_bytes += cur->DescByteCount; if (cur->DescStatusFlags & DMA_DESC_STS_ERROR) eng->accumulated_flags |= ACD_FLAG_ENG_ACCUM_ERROR; - + if (cur->DescStatusFlags & DMA_DESC_STS_SHORT) eng->accumulated_flags |= ACD_FLAG_ENG_ACCUM_SHORT; - - if (cur->DescControlFlags & DMA_DESC_CTL_EOP){ + + if (cur->DescControlFlags & DMA_DESC_CTL_EOP) { if (cur->acd) transfer_complete_cb(cur->acd, eng->accumulated_bytes, eng->accumulated_flags | ACD_FLAG_DONE); } - + eng->desc_completed = cur; } while (cur->MyDMAAddr != GetEngineCompletePtr(eng)); - + out: SetClearEngineControl(eng, ENG_CTL_IRQ_ACTIVE, 0); - + unlock_engine(eng); } - /********** DMA Engine Init/Teardown **********/ void start_dma_engine(struct kpc_dma_device *eng) { eng->desc_next = eng->desc_pool_first; eng->desc_completed = eng->desc_pool_last; - + // Setup the engine pointer registers SetEngineNextPtr(eng, eng->desc_pool_first); SetEngineSWPtr(eng, eng->desc_pool_first); ClearEngineCompletePtr(eng); - + WriteEngineControl(eng, ENG_CTL_DMA_ENABLE | ENG_CTL_IRQ_ENABLE); } int setup_dma_engine(struct kpc_dma_device *eng, u32 desc_cnt) { u32 caps; - struct kpc_dma_descriptor * cur; - struct kpc_dma_descriptor * next; + struct kpc_dma_descriptor *cur; + struct kpc_dma_descriptor *next; dma_addr_t next_handle; dma_addr_t head_handle; unsigned int i; int rv; - dev_dbg(&eng->pldev->dev, "Setting up DMA engine [%p]\n", eng); - + caps = GetEngineCapabilities(eng); - - if (WARN(!(caps & ENG_CAP_PRESENT), "setup_dma_engine() called for DMA Engine at %p which isn't present in hardware!\n", eng)) + + if (WARN(!(caps & ENG_CAP_PRESENT), "%s() called for DMA Engine at %p which isn't present in hardware!\n", __func__, eng)) return -ENXIO; - - if (caps & ENG_CAP_DIRECTION){ + + if (caps & ENG_CAP_DIRECTION) { eng->dir = DMA_FROM_DEVICE; } else { eng->dir = DMA_TO_DEVICE; } - + eng->desc_pool_cnt = desc_cnt; eng->desc_pool = dma_pool_create("KPC DMA Descriptors", &eng->pldev->dev, sizeof(struct kpc_dma_descriptor), DMA_DESC_ALIGNMENT, 4096); - + eng->desc_pool_first = dma_pool_alloc(eng->desc_pool, GFP_KERNEL | GFP_DMA, &head_handle); - if (!eng->desc_pool_first){ - dev_err(&eng->pldev->dev, "setup_dma_engine: couldn't allocate desc_pool_first!\n"); + if (!eng->desc_pool_first) { + dev_err(&eng->pldev->dev, "%s: couldn't allocate desc_pool_first!\n", __func__); dma_pool_destroy(eng->desc_pool); return -ENOMEM; } - + eng->desc_pool_first->MyDMAAddr = head_handle; clear_desc(eng->desc_pool_first); - + cur = eng->desc_pool_first; - for (i = 1 ; i < eng->desc_pool_cnt ; i++){ + for (i = 1 ; i < eng->desc_pool_cnt ; i++) { next = dma_pool_alloc(eng->desc_pool, GFP_KERNEL | GFP_DMA, &next_handle); - if (next == NULL) + if (!next) goto done_alloc; - + clear_desc(next); next->MyDMAAddr = next_handle; - + cur->DescNextDescPtr = next_handle; cur->Next = next; cur = next; } - + done_alloc: // Link the last descriptor back to the first, so it's a circular linked list cur->Next = eng->desc_pool_first; cur->DescNextDescPtr = eng->desc_pool_first->MyDMAAddr; - + eng->desc_pool_last = cur; eng->desc_completed = eng->desc_pool_last; - + // Setup work queue INIT_WORK(&eng->irq_work, ndd_irq_worker); - + // Grab IRQ line rv = request_irq(eng->irq, ndd_irq_handler, IRQF_SHARED, KP_DRIVER_NAME_DMA_CONTROLLER, eng); - if (rv){ - dev_err(&eng->pldev->dev, "setup_dma_engine: failed to request_irq: %d\n", rv); + if (rv) { + dev_err(&eng->pldev->dev, "%s: failed to request_irq: %d\n", __func__, rv); return rv; } - + // Turn on the engine! start_dma_engine(eng); unlock_engine(eng); - + return 0; } void stop_dma_engine(struct kpc_dma_device *eng) { unsigned long timeout; - dev_dbg(&eng->pldev->dev, "Destroying DMA engine [%p]\n", eng); - + // Disable the descriptor engine WriteEngineControl(eng, 0); - + // Wait for descriptor engine to finish current operaion timeout = jiffies + (HZ / 2); - while (GetEngineControl(eng) & ENG_CTL_DMA_RUNNING){ - if (time_after(jiffies, timeout)){ + while (GetEngineControl(eng) & ENG_CTL_DMA_RUNNING) { + if (time_after(jiffies, timeout)) { dev_crit(&eng->pldev->dev, "DMA_RUNNING still asserted!\n"); break; } } - + // Request a reset WriteEngineControl(eng, ENG_CTL_DMA_RESET_REQUEST); - + // Wait for reset request to be processed timeout = jiffies + (HZ / 2); - while (GetEngineControl(eng) & (ENG_CTL_DMA_RUNNING | ENG_CTL_DMA_RESET_REQUEST)){ - if (time_after(jiffies, timeout)){ + while (GetEngineControl(eng) & (ENG_CTL_DMA_RUNNING | ENG_CTL_DMA_RESET_REQUEST)) { + if (time_after(jiffies, timeout)) { dev_crit(&eng->pldev->dev, "ENG_CTL_DMA_RESET_REQUEST still asserted!\n"); break; } } - + // Request a reset WriteEngineControl(eng, ENG_CTL_DMA_RESET); - + // And wait for reset to complete timeout = jiffies + (HZ / 2); - while (GetEngineControl(eng) & ENG_CTL_DMA_RESET){ - if (time_after(jiffies, timeout)){ + while (GetEngineControl(eng) & ENG_CTL_DMA_RESET) { + if (time_after(jiffies, timeout)) { dev_crit(&eng->pldev->dev, "DMA_RESET still asserted!\n"); break; } } - + // Clear any persistent bits just to make sure there is no residue from the reset SetClearEngineControl(eng, (ENG_CTL_IRQ_ACTIVE | ENG_CTL_DESC_COMPLETE | ENG_CTL_DESC_ALIGN_ERR | ENG_CTL_DESC_FETCH_ERR | ENG_CTL_SW_ABORT_ERR | ENG_CTL_DESC_CHAIN_END | ENG_CTL_DMA_WAITING_PERSIST), 0); - + // Reset performance counters - + // Completely disable the engine WriteEngineControl(eng, 0); } void destroy_dma_engine(struct kpc_dma_device *eng) { - struct kpc_dma_descriptor * cur; + struct kpc_dma_descriptor *cur; dma_addr_t cur_handle; unsigned int i; - + stop_dma_engine(eng); - + cur = eng->desc_pool_first; cur_handle = eng->desc_pool_first->MyDMAAddr; - - for (i = 0 ; i < eng->desc_pool_cnt ; i++){ + + for (i = 0 ; i < eng->desc_pool_cnt ; i++) { struct kpc_dma_descriptor *next = cur->Next; dma_addr_t next_handle = cur->DescNextDescPtr; + dma_pool_free(eng->desc_pool, cur, cur_handle); cur_handle = next_handle; cur = next; } - + dma_pool_destroy(eng->desc_pool); - + free_irq(eng->irq, eng); } - - /********** Helper Functions **********/ int count_descriptors_available(struct kpc_dma_device *eng) { u32 count = 0; struct kpc_dma_descriptor *cur = eng->desc_next; - while (cur != eng->desc_completed){ + + while (cur != eng->desc_completed) { BUG_ON(cur == NULL); count++; cur = cur->Next; @@ -247,7 +245,7 @@ int count_descriptors_available(struct kpc_dma_device *eng) void clear_desc(struct kpc_dma_descriptor *desc) { - if (desc == NULL) + if (!desc) return; desc->DescByteCount = 0; desc->DescStatusErrorFlags = 0; diff --git a/drivers/staging/kpc2000/kpc_dma/fileops.c b/drivers/staging/kpc2000/kpc_dma/fileops.c index 616658709bd9..48ca88bc6b0b 100644 --- a/drivers/staging/kpc2000/kpc_dma/fileops.c +++ b/drivers/staging/kpc2000/kpc_dma/fileops.c @@ -9,7 +9,6 @@ #include <linux/types.h> /* size_t */ #include <linux/cdev.h> #include <linux/uaccess.h> /* copy_*_user */ -#include <linux/aio.h> /* aio stuff */ #include <linux/highmem.h> #include <linux/pagemap.h> #include "kpc_dma_driver.h" @@ -21,20 +20,19 @@ unsigned int count_pages(unsigned long iov_base, size_t iov_len) { unsigned long first = (iov_base & PAGE_MASK) >> PAGE_SHIFT; unsigned long last = ((iov_base+iov_len-1) & PAGE_MASK) >> PAGE_SHIFT; + return last - first + 1; } static inline unsigned int count_parts_for_sge(struct scatterlist *sg) { - unsigned int sg_length = sg_dma_len(sg); - sg_length += (0x80000-1); - return (sg_length / 0x80000); + return DIV_ROUND_UP(sg_dma_len(sg), 0x80000); } /********** Transfer Helpers **********/ -static -int kpc_dma_transfer(struct dev_private_data *priv, struct kiocb *kcb, unsigned long iov_base, size_t iov_len) +static int kpc_dma_transfer(struct dev_private_data *priv, + unsigned long iov_base, size_t iov_len) { unsigned int i = 0; long rv = 0; @@ -50,75 +48,72 @@ int kpc_dma_transfer(struct dev_private_data *priv, struct kiocb *kcb, unsigned u64 card_addr; u64 dma_addr; u64 user_ctl; - + BUG_ON(priv == NULL); ldev = priv->ldev; BUG_ON(ldev == NULL); - - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_transfer(priv = [%p], kcb = [%p], iov_base = [%p], iov_len = %ld) ldev = [%p]\n", priv, kcb, (void*)iov_base, iov_len, ldev); - - acd = (struct aio_cb_data *) kzalloc(sizeof(struct aio_cb_data), GFP_KERNEL); - if (!acd){ + + acd = kzalloc(sizeof(*acd), GFP_KERNEL); + if (!acd) { dev_err(&priv->ldev->pldev->dev, "Couldn't kmalloc space for for the aio data\n"); return -ENOMEM; } memset(acd, 0x66, sizeof(struct aio_cb_data)); - + acd->priv = priv; acd->ldev = priv->ldev; acd->cpl = &done; acd->flags = 0; - acd->kcb = kcb; acd->len = iov_len; acd->page_count = count_pages(iov_base, iov_len); - + // Allocate an array of page pointers acd->user_pages = kzalloc(sizeof(struct page *) * acd->page_count, GFP_KERNEL); - if (!acd->user_pages){ + if (!acd->user_pages) { dev_err(&priv->ldev->pldev->dev, "Couldn't kmalloc space for for the page pointers\n"); rv = -ENOMEM; goto err_alloc_userpages; } - + // Lock the user buffer pages in memory, and hold on to the page pointers (for the sglist) down_read(¤t->mm->mmap_sem); /* get memory map semaphore */ rv = get_user_pages(iov_base, acd->page_count, FOLL_TOUCH | FOLL_WRITE | FOLL_GET, acd->user_pages, NULL); up_read(¤t->mm->mmap_sem); /* release the semaphore */ - if (rv != acd->page_count){ + if (rv != acd->page_count) { dev_err(&priv->ldev->pldev->dev, "Couldn't get_user_pages (%ld)\n", rv); goto err_get_user_pages; } - + // Allocate and setup the sg_table (scatterlist entries) rv = sg_alloc_table_from_pages(&acd->sgt, acd->user_pages, acd->page_count, iov_base & (PAGE_SIZE-1), iov_len, GFP_KERNEL); - if (rv){ + if (rv) { dev_err(&priv->ldev->pldev->dev, "Couldn't alloc sg_table (%ld)\n", rv); goto err_alloc_sg_table; } - + // Setup the DMA mapping for all the sg entries acd->mapped_entry_count = dma_map_sg(&ldev->pldev->dev, acd->sgt.sgl, acd->sgt.nents, ldev->dir); - if (acd->mapped_entry_count <= 0){ + if (acd->mapped_entry_count <= 0) { dev_err(&priv->ldev->pldev->dev, "Couldn't dma_map_sg (%d)\n", acd->mapped_entry_count); goto err_dma_map_sg; } // Calculate how many descriptors are actually needed for this transfer. - for_each_sg(acd->sgt.sgl, sg, acd->mapped_entry_count, i){ + for_each_sg(acd->sgt.sgl, sg, acd->mapped_entry_count, i) { desc_needed += count_parts_for_sge(sg); } - + lock_engine(ldev); - + // Figoure out how many descriptors are available and return an error if there aren't enough num_descrs_avail = count_descriptors_available(ldev); dev_dbg(&priv->ldev->pldev->dev, " mapped_entry_count = %d num_descrs_needed = %d num_descrs_avail = %d\n", acd->mapped_entry_count, desc_needed, num_descrs_avail); - if (desc_needed >= ldev->desc_pool_cnt){ + if (desc_needed >= ldev->desc_pool_cnt) { dev_warn(&priv->ldev->pldev->dev, " mapped_entry_count = %d num_descrs_needed = %d num_descrs_avail = %d TOO MANY to ever complete!\n", acd->mapped_entry_count, desc_needed, num_descrs_avail); rv = -EAGAIN; goto err_descr_too_many; } - if (desc_needed > num_descrs_avail){ + if (desc_needed > num_descrs_avail) { dev_warn(&priv->ldev->pldev->dev, " mapped_entry_count = %d num_descrs_needed = %d num_descrs_avail = %d Too many to complete right now.\n", acd->mapped_entry_count, desc_needed, num_descrs_avail); rv = -EMSGSIZE; goto err_descr_too_many; @@ -127,70 +122,67 @@ int kpc_dma_transfer(struct dev_private_data *priv, struct kiocb *kcb, unsigned // Loop through all the sg table entries and fill out a descriptor for each one. desc = ldev->desc_next; card_addr = acd->priv->card_addr; - for_each_sg(acd->sgt.sgl, sg, acd->mapped_entry_count, i){ + for_each_sg(acd->sgt.sgl, sg, acd->mapped_entry_count, i) { pcnt = count_parts_for_sge(sg); - for (p = 0 ; p < pcnt ; p++){ + for (p = 0 ; p < pcnt ; p++) { // Fill out the descriptor BUG_ON(desc == NULL); clear_desc(desc); - if (p != pcnt-1){ + if (p != pcnt-1) { desc->DescByteCount = 0x80000; } else { desc->DescByteCount = sg_dma_len(sg) - (p * 0x80000); } desc->DescBufferByteCount = desc->DescByteCount; - + desc->DescControlFlags |= DMA_DESC_CTL_IRQONERR; if (i == 0 && p == 0) desc->DescControlFlags |= DMA_DESC_CTL_SOP; if (i == acd->mapped_entry_count-1 && p == pcnt-1) desc->DescControlFlags |= DMA_DESC_CTL_EOP | DMA_DESC_CTL_IRQONDONE; - + desc->DescCardAddrLS = (card_addr & 0xFFFFFFFF); desc->DescCardAddrMS = (card_addr >> 32) & 0xF; card_addr += desc->DescByteCount; - + dma_addr = sg_dma_address(sg) + (p * 0x80000); desc->DescSystemAddrLS = (dma_addr & 0x00000000FFFFFFFF) >> 0; desc->DescSystemAddrMS = (dma_addr & 0xFFFFFFFF00000000) >> 32; - + user_ctl = acd->priv->user_ctl; - if (i == acd->mapped_entry_count-1 && p == pcnt-1){ + if (i == acd->mapped_entry_count-1 && p == pcnt-1) { user_ctl = acd->priv->user_ctl_last; } desc->DescUserControlLS = (user_ctl & 0x00000000FFFFFFFF) >> 0; desc->DescUserControlMS = (user_ctl & 0xFFFFFFFF00000000) >> 32; - + if (i == acd->mapped_entry_count-1 && p == pcnt-1) desc->acd = acd; - + dev_dbg(&priv->ldev->pldev->dev, " Filled descriptor %p (acd = %p)\n", desc, desc->acd); - + ldev->desc_next = desc->Next; desc = desc->Next; } } - + // Send the filled descriptors off to the hardware to process! SetEngineSWPtr(ldev, ldev->desc_next); - + unlock_engine(ldev); - - // If this is a synchronous kiocb, we need to put the calling process to sleep until the transfer is complete - if (kcb == NULL || is_sync_kiocb(kcb)){ - rv = wait_for_completion_interruptible(&done); - // If the user aborted (rv == -ERESTARTSYS), we're no longer responsible for cleaning up the acd - if (rv == -ERESTARTSYS){ - acd->cpl = NULL; - } - if (rv == 0){ - rv = acd->len; - kfree(acd); - } - return rv; + + rv = wait_for_completion_interruptible(&done); + /* + * If the user aborted (rv == -ERESTARTSYS), we're no longer responsible + * for cleaning up the acd + */ + if (rv == -ERESTARTSYS) + acd->cpl = NULL; + if (rv == 0) { + rv = acd->len; + kfree(acd); } - - return -EIOCBQUEUED; + return rv; err_descr_too_many: unlock_engine(ldev); @@ -198,58 +190,52 @@ int kpc_dma_transfer(struct dev_private_data *priv, struct kiocb *kcb, unsigned sg_free_table(&acd->sgt); err_dma_map_sg: err_alloc_sg_table: - for (i = 0 ; i < acd->page_count ; i++){ + for (i = 0 ; i < acd->page_count ; i++) { put_page(acd->user_pages[i]); } err_get_user_pages: kfree(acd->user_pages); err_alloc_userpages: kfree(acd); - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_transfer returning with error %ld\n", rv); + dev_dbg(&priv->ldev->pldev->dev, "%s returning with error %ld\n", __func__, rv); return rv; } void transfer_complete_cb(struct aio_cb_data *acd, size_t xfr_count, u32 flags) { unsigned int i; - + BUG_ON(acd == NULL); BUG_ON(acd->user_pages == NULL); BUG_ON(acd->sgt.sgl == NULL); BUG_ON(acd->ldev == NULL); BUG_ON(acd->ldev->pldev == NULL); - - dev_dbg(&acd->ldev->pldev->dev, "transfer_complete_cb(acd = [%p])\n", acd); - - for (i = 0 ; i < acd->page_count ; i++){ - if (!PageReserved(acd->user_pages[i])){ + + for (i = 0 ; i < acd->page_count ; i++) { + if (!PageReserved(acd->user_pages[i])) { set_page_dirty(acd->user_pages[i]); } } - + dma_unmap_sg(&acd->ldev->pldev->dev, acd->sgt.sgl, acd->sgt.nents, acd->ldev->dir); - - for (i = 0 ; i < acd->page_count ; i++){ + + for (i = 0 ; i < acd->page_count ; i++) { put_page(acd->user_pages[i]); } - + sg_free_table(&acd->sgt); - + kfree(acd->user_pages); - + acd->flags = flags; - - if (acd->kcb == NULL || is_sync_kiocb(acd->kcb)){ - if (acd->cpl){ - complete(acd->cpl); - } else { - // There's no completion, so we're responsible for cleaning up the acd - kfree(acd); - } + + if (acd->cpl) { + complete(acd->cpl); } else { -#ifdef CONFIG_KPC_DMA_AIO - aio_complete(acd->kcb, acd->len, acd->flags); -#endif + /* + * There's no completion, so we're responsible for cleaning up + * the acd + */ kfree(acd); } } @@ -260,22 +246,22 @@ int kpc_dma_open(struct inode *inode, struct file *filp) { struct dev_private_data *priv; struct kpc_dma_device *ldev = kpc_dma_lookup_device(iminor(inode)); - if (ldev == NULL) + + if (!ldev) return -ENODEV; - - if (! atomic_dec_and_test(&ldev->open_count)){ + + if (!atomic_dec_and_test(&ldev->open_count)) { atomic_inc(&ldev->open_count); return -EBUSY; /* already open */ } - + priv = kzalloc(sizeof(struct dev_private_data), GFP_KERNEL); if (!priv) return -ENOMEM; - + priv->ldev = ldev; filp->private_data = priv; - - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_open(inode = [%p], filp = [%p]) priv = [%p] ldev = [%p]\n", inode, filp, priv, priv->ldev); + return 0; } @@ -285,134 +271,81 @@ int kpc_dma_close(struct inode *inode, struct file *filp) struct kpc_dma_descriptor *cur; struct dev_private_data *priv = (struct dev_private_data *)filp->private_data; struct kpc_dma_device *eng = priv->ldev; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_close(inode = [%p], filp = [%p]) priv = [%p], ldev = [%p]\n", inode, filp, priv, priv->ldev); - + lock_engine(eng); - + stop_dma_engine(eng); - + cur = eng->desc_completed->Next; - while (cur != eng->desc_next){ + while (cur != eng->desc_next) { dev_dbg(&eng->pldev->dev, "Aborting descriptor %p (acd = %p)\n", cur, cur->acd); - if (cur->DescControlFlags & DMA_DESC_CTL_EOP){ + if (cur->DescControlFlags & DMA_DESC_CTL_EOP) { if (cur->acd) transfer_complete_cb(cur->acd, 0, ACD_FLAG_ABORT); } - + clear_desc(cur); eng->desc_completed = cur; - + cur = cur->Next; } - + start_dma_engine(eng); - + unlock_engine(eng); - + atomic_inc(&priv->ldev->open_count); /* release the device */ kfree(priv); return 0; } -#ifdef CONFIG_KPC_DMA_AIO static -int kpc_dma_aio_cancel(struct kiocb *kcb) +ssize_t kpc_dma_read(struct file *filp, char __user *user_buf, size_t count, loff_t *ppos) { - struct dev_private_data *priv = (struct dev_private_data *)kcb->ki_filp->private_data; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_aio_cancel(kcb = [%p]) priv = [%p], ldev = [%p]\n", kcb, priv, priv->ldev); - return 0; -} + struct dev_private_data *priv = (struct dev_private_data *)filp->private_data; -static -ssize_t kpc_dma_aio_read(struct kiocb *kcb, const struct iovec *iov, unsigned long iov_count, loff_t pos) -{ - struct dev_private_data *priv = (struct dev_private_data *)kcb->ki_filp->private_data; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_aio_read(kcb = [%p], iov = [%p], iov_count = %ld, pos = %lld) priv = [%p], ldev = [%p]\n", kcb, iov, iov_count, pos, priv, priv->ldev); - if (priv->ldev->dir != DMA_FROM_DEVICE) return -EMEDIUMTYPE; - - if (iov_count != 1){ - dev_err(&priv->ldev->pldev->dev, "kpc_dma_aio_read() called with iov_count > 1!\n"); - return -EFAULT; - } - - if (!is_sync_kiocb(kcb)) - kiocb_set_cancel_fn(kcb, kpc_dma_aio_cancel); - return kpc_dma_transfer(priv, kcb, (unsigned long)iov->iov_base, iov->iov_len); -} -static -ssize_t kpc_dma_aio_write(struct kiocb *kcb, const struct iovec *iov, unsigned long iov_count, loff_t pos) -{ - struct dev_private_data *priv = (struct dev_private_data *)kcb->ki_filp->private_data; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_aio_write(kcb = [%p], iov = [%p], iov_count = %ld, pos = %lld) priv = [%p], ldev = [%p]\n", kcb, iov, iov_count, pos, priv, priv->ldev); - - if (priv->ldev->dir != DMA_TO_DEVICE) - return -EMEDIUMTYPE; - - if (iov_count != 1){ - dev_err(&priv->ldev->pldev->dev, "kpc_dma_aio_write() called with iov_count > 1!\n"); - return -EFAULT; - } - - if (!is_sync_kiocb(kcb)) - kiocb_set_cancel_fn(kcb, kpc_dma_aio_cancel); - return kpc_dma_transfer(priv, kcb, (unsigned long)iov->iov_base, iov->iov_len); -} -#endif - -static -ssize_t kpc_dma_read( struct file *filp, char __user *user_buf, size_t count, loff_t *ppos) -{ - struct dev_private_data *priv = (struct dev_private_data *)filp->private_data; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_read(filp = [%p], user_buf = [%p], count = %zu, ppos = [%p]) priv = [%p], ldev = [%p]\n", filp, user_buf, count, ppos, priv, priv->ldev); - - if (priv->ldev->dir != DMA_FROM_DEVICE) - return -EMEDIUMTYPE; - - return kpc_dma_transfer(priv, (struct kiocb *)NULL, (unsigned long)user_buf, count); + return kpc_dma_transfer(priv, (unsigned long)user_buf, count); } static ssize_t kpc_dma_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *ppos) { struct dev_private_data *priv = (struct dev_private_data *)filp->private_data; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_write(filp = [%p], user_buf = [%p], count = %zu, ppos = [%p]) priv = [%p], ldev = [%p]\n", filp, user_buf, count, ppos, priv, priv->ldev); - + if (priv->ldev->dir != DMA_TO_DEVICE) return -EMEDIUMTYPE; - - return kpc_dma_transfer(priv, (struct kiocb *)NULL, (unsigned long)user_buf, count); + + return kpc_dma_transfer(priv, (unsigned long)user_buf, count); } static long kpc_dma_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param) { struct dev_private_data *priv = (struct dev_private_data *)filp->private_data; - dev_dbg(&priv->ldev->pldev->dev, "kpc_dma_ioctl(filp = [%p], ioctl_num = 0x%x, ioctl_param = 0x%lx) priv = [%p], ldev = [%p]\n", filp, ioctl_num, ioctl_param, priv, priv->ldev); - - switch (ioctl_num){ - case KND_IOCTL_SET_CARD_ADDR: priv->card_addr = ioctl_param; return priv->card_addr; - case KND_IOCTL_SET_USER_CTL: priv->user_ctl = ioctl_param; return priv->user_ctl; - case KND_IOCTL_SET_USER_CTL_LAST: priv->user_ctl_last = ioctl_param; return priv->user_ctl_last; - case KND_IOCTL_GET_USER_STS: return priv->user_sts; + + switch (ioctl_num) { + case KND_IOCTL_SET_CARD_ADDR: + priv->card_addr = ioctl_param; return priv->card_addr; + case KND_IOCTL_SET_USER_CTL: + priv->user_ctl = ioctl_param; return priv->user_ctl; + case KND_IOCTL_SET_USER_CTL_LAST: + priv->user_ctl_last = ioctl_param; return priv->user_ctl_last; + case KND_IOCTL_GET_USER_STS: + return priv->user_sts; } - + return -ENOTTY; } - -struct file_operations kpc_dma_fops = { +const struct file_operations kpc_dma_fops = { .owner = THIS_MODULE, .open = kpc_dma_open, .release = kpc_dma_close, .read = kpc_dma_read, .write = kpc_dma_write, -#ifdef CONFIG_KPC_DMA_AIO - .aio_read = kpc_dma_aio_read, - .aio_write = kpc_dma_aio_write, -#endif .unlocked_ioctl = kpc_dma_ioctl, }; diff --git a/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.c b/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.c index aeae58d9bc18..a05ae6d40db9 100644 --- a/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.c +++ b/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.c @@ -14,16 +14,16 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Matt.Sickler@daktronics.com"); #define KPC_DMA_CHAR_MAJOR UNNAMED_MAJOR -#define KPC_DMA_NUM_MINORS 1 << MINORBITS +#define KPC_DMA_NUM_MINORS BIT(MINORBITS) static DEFINE_MUTEX(kpc_dma_mtx); static int assigned_major_num; static LIST_HEAD(kpc_dma_list); - /********** kpc_dma_list list management **********/ -struct kpc_dma_device * kpc_dma_lookup_device(int minor) +struct kpc_dma_device *kpc_dma_lookup_device(int minor) { struct kpc_dma_device *c; + mutex_lock(&kpc_dma_mtx); list_for_each_entry(c, &kpc_dma_list, list) { if (c->pldev->id == minor) { @@ -31,19 +31,19 @@ struct kpc_dma_device * kpc_dma_lookup_device(int minor) } } c = NULL; // not-found case - out: +out: mutex_unlock(&kpc_dma_mtx); return c; } -void kpc_dma_add_device(struct kpc_dma_device * ldev) +static void kpc_dma_add_device(struct kpc_dma_device *ldev) { mutex_lock(&kpc_dma_mtx); list_add(&ldev->list, &kpc_dma_list); mutex_unlock(&kpc_dma_mtx); } -void kpc_dma_del_device(struct kpc_dma_device * ldev) +static void kpc_dma_del_device(struct kpc_dma_device *ldev) { mutex_lock(&kpc_dma_mtx); list_del(&ldev->list); @@ -55,11 +55,14 @@ static ssize_t show_engine_regs(struct device *dev, struct device_attribute *at { struct kpc_dma_device *ldev; struct platform_device *pldev = to_platform_device(dev); - if (!pldev) return 0; + + if (!pldev) + return 0; ldev = platform_get_drvdata(pldev); - if (!ldev) return 0; - - return scnprintf(buf, PAGE_SIZE, + if (!ldev) + return 0; + + return scnprintf(buf, PAGE_SIZE, "EngineControlStatus = 0x%08x\n" "RegNextDescPtr = 0x%08x\n" "RegSWDescPtr = 0x%08x\n" @@ -78,15 +81,14 @@ static ssize_t show_engine_regs(struct device *dev, struct device_attribute *at ldev->desc_completed ); } -DEVICE_ATTR(engine_regs, 0444, show_engine_regs, NULL); +static DEVICE_ATTR(engine_regs, 0444, show_engine_regs, NULL); -static const struct attribute * ndd_attr_list[] = { +static const struct attribute *ndd_attr_list[] = { &dev_attr_engine_regs.attr, NULL, }; -struct class *kpc_dma_class; - +static struct class *kpc_dma_class; /********** Platform Driver Functions **********/ static @@ -95,73 +97,72 @@ int kpc_dma_probe(struct platform_device *pldev) struct resource *r = NULL; int rv = 0; dev_t dev; - + struct kpc_dma_device *ldev = kzalloc(sizeof(struct kpc_dma_device), GFP_KERNEL); - if (!ldev){ - dev_err(&pldev->dev, "kpc_dma_probe: unable to kzalloc space for kpc_dma_device\n"); + + if (!ldev) { + dev_err(&pldev->dev, "%s: unable to kzalloc space for kpc_dma_device\n", __func__); rv = -ENOMEM; goto err_rv; } - - dev_dbg(&pldev->dev, "kpc_dma_probe(pldev = [%p]) ldev = [%p]\n", pldev, ldev); - + INIT_LIST_HEAD(&ldev->list); - + ldev->pldev = pldev; platform_set_drvdata(pldev, ldev); atomic_set(&ldev->open_count, 1); - + mutex_init(&ldev->sem); lock_engine(ldev); - + // Get Engine regs resource r = platform_get_resource(pldev, IORESOURCE_MEM, 0); - if (!r){ - dev_err(&ldev->pldev->dev, "kpc_dma_probe: didn't get the engine regs resource!\n"); + if (!r) { + dev_err(&ldev->pldev->dev, "%s: didn't get the engine regs resource!\n", __func__); rv = -ENXIO; goto err_kfree; } ldev->eng_regs = ioremap_nocache(r->start, resource_size(r)); - if (!ldev->eng_regs){ - dev_err(&ldev->pldev->dev, "kpc_dma_probe: failed to ioremap engine regs!\n"); + if (!ldev->eng_regs) { + dev_err(&ldev->pldev->dev, "%s: failed to ioremap engine regs!\n", __func__); rv = -ENXIO; goto err_kfree; } - + r = platform_get_resource(pldev, IORESOURCE_IRQ, 0); - if (!r){ - dev_err(&ldev->pldev->dev, "kpc_dma_probe: didn't get the IRQ resource!\n"); + if (!r) { + dev_err(&ldev->pldev->dev, "%s: didn't get the IRQ resource!\n", __func__); rv = -ENXIO; goto err_kfree; } ldev->irq = r->start; - + // Setup miscdev struct dev = MKDEV(assigned_major_num, pldev->id); ldev->kpc_dma_dev = device_create(kpc_dma_class, &pldev->dev, dev, ldev, "kpc_dma%d", pldev->id); - if (IS_ERR(ldev->kpc_dma_dev)){ - dev_err(&ldev->pldev->dev, "kpc_dma_probe: device_create failed: %d\n", rv); + if (IS_ERR(ldev->kpc_dma_dev)) { + dev_err(&ldev->pldev->dev, "%s: device_create failed: %d\n", __func__, rv); goto err_kfree; } - + // Setup the DMA engine rv = setup_dma_engine(ldev, 30); - if (rv){ - dev_err(&ldev->pldev->dev, "kpc_dma_probe: failed to setup_dma_engine: %d\n", rv); + if (rv) { + dev_err(&ldev->pldev->dev, "%s: failed to setup_dma_engine: %d\n", __func__, rv); goto err_misc_dereg; } - + // Setup the sysfs files rv = sysfs_create_files(&(ldev->pldev->dev.kobj), ndd_attr_list); - if (rv){ - dev_err(&ldev->pldev->dev, "kpc_dma_probe: Failed to add sysfs files: %d\n", rv); + if (rv) { + dev_err(&ldev->pldev->dev, "%s: Failed to add sysfs files: %d\n", __func__, rv); goto err_destroy_eng; } - + kpc_dma_add_device(ldev); - + return 0; - + err_destroy_eng: destroy_dma_engine(ldev); err_misc_dereg: @@ -176,70 +177,67 @@ static int kpc_dma_remove(struct platform_device *pldev) { struct kpc_dma_device *ldev = platform_get_drvdata(pldev); + if (!ldev) return -ENXIO; - - dev_dbg(&ldev->pldev->dev, "kpc_dma_remove(pldev = [%p]) ldev = [%p]\n", pldev, ldev); - + lock_engine(ldev); sysfs_remove_files(&(ldev->pldev->dev.kobj), ndd_attr_list); destroy_dma_engine(ldev); kpc_dma_del_device(ldev); device_destroy(kpc_dma_class, MKDEV(assigned_major_num, ldev->pldev->id)); kfree(ldev); - + return 0; } - /********** Driver Functions **********/ -struct platform_driver kpc_dma_plat_driver_i = { +static struct platform_driver kpc_dma_plat_driver_i = { .probe = kpc_dma_probe, .remove = kpc_dma_remove, .driver = { .name = KP_DRIVER_NAME_DMA_CONTROLLER, - .owner = THIS_MODULE, }, }; static -int __init kpc_dma_driver_init(void) +int __init kpc_dma_driver_init(void) { int err; - + err = __register_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma", &kpc_dma_fops); - if (err < 0){ + if (err < 0) { pr_err("Can't allocate a major number (%d) for kpc_dma (err = %d)\n", KPC_DMA_CHAR_MAJOR, err); goto fail_chrdev_register; } assigned_major_num = err; - + kpc_dma_class = class_create(THIS_MODULE, "kpc_dma"); err = PTR_ERR(kpc_dma_class); - if (IS_ERR(kpc_dma_class)){ + if (IS_ERR(kpc_dma_class)) { pr_err("Can't create class kpc_dma (err = %d)\n", err); goto fail_class_create; } - + err = platform_driver_register(&kpc_dma_plat_driver_i); - if (err){ + if (err) { pr_err("Can't register platform driver for kpc_dma (err = %d)\n", err); goto fail_platdriver_register; } - + return err; - - fail_platdriver_register: + +fail_platdriver_register: class_destroy(kpc_dma_class); - fail_class_create: +fail_class_create: __unregister_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma"); - fail_chrdev_register: +fail_chrdev_register: return err; } module_init(kpc_dma_driver_init); static -void __exit kpc_dma_driver_exit(void) +void __exit kpc_dma_driver_exit(void) { platform_driver_unregister(&kpc_dma_plat_driver_i); class_destroy(kpc_dma_class); diff --git a/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.h b/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.h index ef913b7496e6..4c8cc866b826 100644 --- a/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.h +++ b/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.h @@ -14,11 +14,9 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/workqueue.h> -#include <linux/aio.h> #include <linux/bitops.h> #include "../kpc.h" - struct kp2000_device; struct kpc_dma_device { struct list_head list; @@ -27,23 +25,23 @@ struct kpc_dma_device { struct device *kpc_dma_dev; struct kobject kobj; char name[16]; - + int dir; // DMA_FROM_DEVICE || DMA_TO_DEVICE struct mutex sem; unsigned int irq; struct work_struct irq_work; - + atomic_t open_count; - + size_t accumulated_bytes; u32 accumulated_flags; - + // Descriptor "Pool" housekeeping u32 desc_pool_cnt; struct dma_pool *desc_pool; struct kpc_dma_descriptor *desc_pool_first; struct kpc_dma_descriptor *desc_pool_last; - + struct kpc_dma_descriptor *desc_next; struct kpc_dma_descriptor *desc_completed; }; @@ -56,9 +54,9 @@ struct dev_private_data { u64 user_sts; }; -struct kpc_dma_device * kpc_dma_lookup_device(int minor); +struct kpc_dma_device *kpc_dma_lookup_device(int minor); -extern struct file_operations kpc_dma_fops; +extern const struct file_operations kpc_dma_fops; #define ENG_CAP_PRESENT 0x00000001 #define ENG_CAP_DIRECTION 0x00000002 @@ -88,9 +86,8 @@ struct aio_cb_data { struct kpc_dma_device *ldev; struct completion *cpl; unsigned char flags; - struct kiocb *kcb; size_t len; - + unsigned int page_count; struct page **user_pages; struct sg_table sgt; @@ -119,10 +116,10 @@ struct kpc_dma_descriptor { volatile u32 DescSystemAddrLS; volatile u32 DescSystemAddrMS; volatile u32 DescNextDescPtr; - + dma_addr_t MyDMAAddr; struct kpc_dma_descriptor *Next; - + struct aio_cb_data *acd; } __attribute__((packed)); // DescControlFlags: @@ -157,35 +154,41 @@ void WriteEngineControl(struct kpc_dma_device *eng, u32 value) { writel(value, eng->eng_regs + 1); } + static inline u32 GetEngineControl(struct kpc_dma_device *eng) { return readl(eng->eng_regs + 1); } + static inline void SetClearEngineControl(struct kpc_dma_device *eng, u32 set_bits, u32 clear_bits) { u32 val = GetEngineControl(eng); + val |= set_bits; val &= ~clear_bits; WriteEngineControl(eng, val); } static inline -void SetEngineNextPtr(struct kpc_dma_device *eng, struct kpc_dma_descriptor * desc) +void SetEngineNextPtr(struct kpc_dma_device *eng, struct kpc_dma_descriptor *desc) { writel(desc->MyDMAAddr, eng->eng_regs + 2); } + static inline -void SetEngineSWPtr(struct kpc_dma_device *eng, struct kpc_dma_descriptor * desc) +void SetEngineSWPtr(struct kpc_dma_device *eng, struct kpc_dma_descriptor *desc) { writel(desc->MyDMAAddr, eng->eng_regs + 3); } + static inline void ClearEngineCompletePtr(struct kpc_dma_device *eng) { writel(0, eng->eng_regs + 4); } + static inline u32 GetEngineCompletePtr(struct kpc_dma_device *eng) { @@ -206,7 +209,6 @@ void unlock_engine(struct kpc_dma_device *eng) mutex_unlock(&eng->sem); } - /// Shared Functions void start_dma_engine(struct kpc_dma_device *eng); int setup_dma_engine(struct kpc_dma_device *eng, u32 desc_cnt); diff --git a/drivers/staging/kpc2000/kpc_i2c/Makefile b/drivers/staging/kpc2000/kpc_i2c/Makefile deleted file mode 100644 index 73ec07ac7d39..000000000000 --- a/drivers/staging/kpc2000/kpc_i2c/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-m := kpc2000_i2c.o -kpc2000_i2c-objs := i2c_driver.o fileops.o diff --git a/drivers/staging/kpc2000/kpc_i2c/fileops.c b/drivers/staging/kpc2000/kpc_i2c/fileops.c deleted file mode 100644 index e749c0994491..000000000000 --- a/drivers/staging/kpc2000/kpc_i2c/fileops.c +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -#if 0 -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> /* printk() */ -#include <linux/slab.h> /* kmalloc() */ -#include <linux/fs.h> /* everything... */ -#include <linux/errno.h> /* error codes */ -#include <linux/types.h> /* size_t */ -#include <linux/cdev.h> -#include <asm/uaccess.h> /* copy_*_user */ - -#include "i2c_driver.h" - -int i2c_cdev_open(struct inode *inode, struct file *filp) -{ - struct i2c_device *lddev; - - if(NULL == inode) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_open: inode is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_open: inode is a NULL pointer\n"); - return -EINVAL; - } - if(NULL == filp) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_open: filp is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_open: filp is a NULL pointer\n"); - return -EINVAL; - } - - lddev = container_of(inode->i_cdev, struct i2c_device, cdev); - //printk(KERN_DEBUG "<pl_i2c> i2c_cdev_open(filp = [%p], lddev = [%p])\n", filp, lddev); - DBG_PRINT(KERN_DEBUG, "i2c_cdev_open(filp = [%p], lddev = [%p])\n", filp, lddev); - - filp->private_data = lddev; /* so other methods can access it */ - - return 0; /* success */ -} - -int i2c_cdev_close(struct inode *inode, struct file *filp) -{ - struct i2c_device *lddev; - - if(NULL == inode) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_close: inode is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_close: inode is a NULL pointer\n"); - return -EINVAL; - } - if(NULL == filp) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_close: filp is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_close: filp is a NULL pointer\n"); - return -EINVAL; - } - - lddev = filp->private_data; - //printk(KERN_DEBUG "<pl_i2c> i2c_cdev_close(filp = [%p], lddev = [%p])\n", filp, lddev); - DBG_PRINT(KERN_DEBUG, "i2c_cdev_close(filp = [%p], lddev = [%p])\n", filp, lddev); - - return 0; -} - -ssize_t i2c_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) -{ - size_t copy; - ssize_t ret = 0; - int err = 0; - u64 read_val; - char tmp_buf[48] = { 0 }; - struct i2c_device *lddev = filp->private_data; - - if(NULL == filp) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_read: filp is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_read: filp is a NULL pointer\n"); - return -EINVAL; - } - if(NULL == buf) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_read: buf is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_read: buf is a NULL pointer\n"); - return -EINVAL; - } - if(NULL == f_pos) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_read: f_pos is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_read: f_pos is a NULL pointer\n"); - return -EINVAL; - } - - if(count < sizeof(tmp_buf)) { - //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: buffer is too small (count = %d, should be at least %d bytes)\n", (int)count, (int)sizeof(tmp_buf)); - DBG_PRINT(KERN_INFO, "i2c_cdev_read: buffer is too small (count = %d, should be at least %d bytes)\n", (int)count, (int)sizeof(tmp_buf)); - return -EINVAL; - } - if(((*f_pos * 8) + lddev->pldev->resource[0].start) > lddev->pldev->resource[0].end) { - //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: bad read addr %016llx\n", (*f_pos * 8) + lddev->pldev->resource[0].start); - DBG_PRINT(KERN_INFO, "i2c_cdev_read: bad read addr %016llx\n", (*f_pos * 8) + lddev->pldev->resource[0].start); - //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: addr end %016llx\n", lddev->pldev->resource[0].end); - DBG_PRINT(KERN_INFO, "i2c_cdev_read: addr end %016llx\n", lddev->pldev->resource[0].end); - //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: EOF reached\n"); - DBG_PRINT(KERN_INFO, "i2c_cdev_read: EOF reached\n"); - return 0; - } - - down_read(&lddev->rw_sem); - - read_val = *(lddev->regs + *f_pos); - copy = clamp_t(size_t, count, 1, sizeof(tmp_buf)); - copy = scnprintf(tmp_buf, copy, "reg: 0x%x val: 0x%llx\n", (unsigned int)*f_pos, read_val); - err = copy_to_user(buf, tmp_buf, copy); - if(err) { - //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: could not copy to user (err = %d)\n", err); - DBG_PRINT(KERN_INFO, "i2c_cdev_read: could not copy to user (err = %d)\n", err); - return -EINVAL; - } - - ret = (ssize_t)copy; - (*f_pos)++; - - up_read(&lddev->rw_sem); - - return ret; -} - -ssize_t i2c_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) -{ - u8 reg; - u8 val; - char tmp[8] = { 0 }; - struct i2c_device *lddev = filp->private_data; - - if(NULL == filp) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_write: filp is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_write: filp is a NULL pointer\n"); - return -EINVAL; - } - if(NULL == buf) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_write: buf is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_write: buf is a NULL pointer\n"); - return -EINVAL; - } - if(NULL == f_pos) { - //printk(KERN_WARNING "<pl_i2c> i2c_cdev_write: f_pos is a NULL pointer\n"); - DBG_PRINT(KERN_WARNING, "i2c_cdev_write: f_pos is a NULL pointer\n"); - return -EINVAL; - } - - //printk(KERN_DEBUG "<pl_i2c> i2c_cdev_write(filp = [%p], lddev = [%p])\n", filp, lddev); - DBG_PRINT(KERN_DEBUG, "i2c_cdev_write(filp = [%p], lddev = [%p])\n", filp, lddev); - - down_write(&lddev->rw_sem); - - if(count >= 2) { - if(copy_from_user(tmp, buf, 2)) { - return -EFAULT; - } - - reg = tmp[0] - '0'; - val = tmp[1] - '0'; - - //printk(KERN_DEBUG " reg = %d val = %d\n", reg, val); - DBG_PRINT(KERN_DEBUG, " reg = %d val = %d\n", reg, val); - - if(reg >= 0 && reg < 16) { - //printk(KERN_DEBUG " Writing 0x%x to %p\n", val, lddev->regs + reg); - DBG_PRINT(KERN_DEBUG, " Writing 0x%x to %p\n", val, lddev->regs + reg); - *(lddev->regs + reg) = val; - } - } - - (*f_pos)++; - - up_write(&lddev->rw_sem); - - return count; -} - -struct file_operations i2c_fops = { - .owner = THIS_MODULE, - .open = i2c_cdev_open, - .release = i2c_cdev_close, - .read = i2c_cdev_read, - .write = i2c_cdev_write, -}; -#endif diff --git a/drivers/staging/kpc2000/kpc_i2c/i2c_driver.c b/drivers/staging/kpc2000/kpc_i2c/i2c_driver.c deleted file mode 100644 index 0fb068b2408d..000000000000 --- a/drivers/staging/kpc2000/kpc_i2c/i2c_driver.c +++ /dev/null @@ -1,699 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* Copyright (c) 2014-2018 Daktronics, - Matt Sickler <matt.sickler@daktronics.com>, - Jordon Hofer <jordon.hofer@daktronics.com> - Adapted i2c-i801.c for use with Kadoka hardware. - Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker - <mdsxyz123@yahoo.com> - Copyright (C) 2007 - 2012 Jean Delvare <khali@linux-fr.org> - Copyright (C) 2010 Intel Corporation, - David Woodhouse <dwmw2@infradead.org> -*/ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/types.h> -#include <asm/io.h> -#include <linux/io-64-nonatomic-lo-hi.h> -#include <linux/export.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/fs.h> -#include <linux/rwsem.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include "../kpc.h" - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matt.Sickler@Daktronics.com"); -MODULE_SOFTDEP("pre: i2c-dev"); - -struct i2c_device { - unsigned long smba; - struct i2c_adapter adapter; - struct platform_device *pldev; - struct rw_semaphore rw_sem; - unsigned int features; -}; - -/***************************** - *** Part 1 - i2c Handlers *** - *****************************/ - -#define REG_SIZE 8 - -/* I801 SMBus address offsets */ -#define SMBHSTSTS(p) ((0 * REG_SIZE) + (p)->smba) -#define SMBHSTCNT(p) ((2 * REG_SIZE) + (p)->smba) -#define SMBHSTCMD(p) ((3 * REG_SIZE) + (p)->smba) -#define SMBHSTADD(p) ((4 * REG_SIZE) + (p)->smba) -#define SMBHSTDAT0(p) ((5 * REG_SIZE) + (p)->smba) -#define SMBHSTDAT1(p) ((6 * REG_SIZE) + (p)->smba) -#define SMBBLKDAT(p) ((7 * REG_SIZE) + (p)->smba) -#define SMBPEC(p) ((8 * REG_SIZE) + (p)->smba) /* ICH3 and later */ -#define SMBAUXSTS(p) ((12 * REG_SIZE) + (p)->smba) /* ICH4 and later */ -#define SMBAUXCTL(p) ((13 * REG_SIZE) + (p)->smba) /* ICH4 and later */ - -/* PCI Address Constants */ -#define SMBBAR 4 -#define SMBHSTCFG 0x040 - -/* Host configuration bits for SMBHSTCFG */ -#define SMBHSTCFG_HST_EN 1 -#define SMBHSTCFG_SMB_SMI_EN 2 -#define SMBHSTCFG_I2C_EN 4 - -/* Auxiliary control register bits, ICH4+ only */ -#define SMBAUXCTL_CRC 1 -#define SMBAUXCTL_E32B 2 - -/* kill bit for SMBHSTCNT */ -#define SMBHSTCNT_KILL 2 - -/* Other settings */ -#define MAX_RETRIES 400 -#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ - -/* I801 command constants */ -#define I801_QUICK 0x00 -#define I801_BYTE 0x04 -#define I801_BYTE_DATA 0x08 -#define I801_WORD_DATA 0x0C -#define I801_PROC_CALL 0x10 /* unimplemented */ -#define I801_BLOCK_DATA 0x14 -#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ -#define I801_BLOCK_LAST 0x34 -#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */ -#define I801_START 0x40 -#define I801_PEC_EN 0x80 /* ICH3 and later */ - -/* I801 Hosts Status register bits */ -#define SMBHSTSTS_BYTE_DONE 0x80 -#define SMBHSTSTS_INUSE_STS 0x40 -#define SMBHSTSTS_SMBALERT_STS 0x20 -#define SMBHSTSTS_FAILED 0x10 -#define SMBHSTSTS_BUS_ERR 0x08 -#define SMBHSTSTS_DEV_ERR 0x04 -#define SMBHSTSTS_INTR 0x02 -#define SMBHSTSTS_HOST_BUSY 0x01 - -#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | SMBHSTSTS_INTR) - -/* Older devices have their ID defined in <linux/pci_ids.h> */ -#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 -#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 -/* Patsburg also has three 'Integrated Device Function' SMBus controllers */ -#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70 -#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 -#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 -#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 -#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 -#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 - - -#define FEATURE_SMBUS_PEC (1 << 0) -#define FEATURE_BLOCK_BUFFER (1 << 1) -#define FEATURE_BLOCK_PROC (1 << 2) -#define FEATURE_I2C_BLOCK_READ (1 << 3) -/* Not really a feature, but it's convenient to handle it as such */ -#define FEATURE_IDF (1 << 15) - -static unsigned int disable_features; -module_param(disable_features, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(disable_features, "Disable selected driver features"); - -// FIXME! -#undef inb_p -#define inb_p(a) readq((void*)a) -#undef outb_p -#define outb_p(d,a) writeq(d,(void*)a) - -/* Make sure the SMBus host is ready to start transmitting. - Return 0 if it is, -EBUSY if it is not. */ -static int i801_check_pre(struct i2c_device *priv) -{ - int status; - - dev_dbg(&priv->adapter.dev, "i801_check_pre\n"); - - status = inb_p(SMBHSTSTS(priv)); - if (status & SMBHSTSTS_HOST_BUSY) { - dev_err(&priv->adapter.dev, "SMBus is busy, can't use it! (status=%x)\n", status); - return -EBUSY; - } - - status &= STATUS_FLAGS; - if (status) { - //dev_dbg(&priv->adapter.dev, "Clearing status flags (%02x)\n", status); - outb_p(status, SMBHSTSTS(priv)); - status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; - if (status) { - dev_err(&priv->adapter.dev, "Failed clearing status flags (%02x)\n", status); - return -EBUSY; - } - } - return 0; -} - -/* Convert the status register to an error code, and clear it. */ -static int i801_check_post(struct i2c_device *priv, int status, int timeout) -{ - int result = 0; - - dev_dbg(&priv->adapter.dev, "i801_check_post\n"); - - /* If the SMBus is still busy, we give up */ - if (timeout) { - dev_err(&priv->adapter.dev, "Transaction timeout\n"); - /* try to stop the current command */ - dev_dbg(&priv->adapter.dev, "Terminating the current operation\n"); - outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, SMBHSTCNT(priv)); - usleep_range(1000, 2000); - outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL), SMBHSTCNT(priv)); - - /* Check if it worked */ - status = inb_p(SMBHSTSTS(priv)); - if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) { - dev_err(&priv->adapter.dev, "Failed terminating the transaction\n"); - } - outb_p(STATUS_FLAGS, SMBHSTSTS(priv)); - return -ETIMEDOUT; - } - - if (status & SMBHSTSTS_FAILED) { - result = -EIO; - dev_err(&priv->adapter.dev, "Transaction failed\n"); - } - if (status & SMBHSTSTS_DEV_ERR) { - result = -ENXIO; - dev_dbg(&priv->adapter.dev, "No response\n"); - } - if (status & SMBHSTSTS_BUS_ERR) { - result = -EAGAIN; - dev_dbg(&priv->adapter.dev, "Lost arbitration\n"); - } - - if (result) { - /* Clear error flags */ - outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); - status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; - if (status) { - dev_warn(&priv->adapter.dev, "Failed clearing status flags at end of transaction (%02x)\n", status); - } - } - - return result; -} - -static int i801_transaction(struct i2c_device *priv, int xact) -{ - int status; - int result; - int timeout = 0; - - dev_dbg(&priv->adapter.dev, "i801_transaction\n"); - - result = i801_check_pre(priv); - if (result < 0) { - return result; - } - /* the current contents of SMBHSTCNT can be overwritten, since PEC, - * INTREN, SMBSCMD are passed in xact */ - outb_p(xact | I801_START, SMBHSTCNT(priv)); - - /* We will always wait for a fraction of a second! */ - do { - usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); - } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES)); - - result = i801_check_post(priv, status, timeout > MAX_RETRIES); - if (result < 0) { - return result; - } - - outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); - return 0; -} - -/* wait for INTR bit as advised by Intel */ -static void i801_wait_hwpec(struct i2c_device *priv) -{ - int timeout = 0; - int status; - - dev_dbg(&priv->adapter.dev, "i801_wait_hwpec\n"); - - do { - usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); - } while ((!(status & SMBHSTSTS_INTR)) && (timeout++ < MAX_RETRIES)); - - if (timeout > MAX_RETRIES) { - dev_dbg(&priv->adapter.dev, "PEC Timeout!\n"); - } - - outb_p(status, SMBHSTSTS(priv)); -} - -static int i801_block_transaction_by_block(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int hwpec) -{ - int i, len; - int status; - - dev_dbg(&priv->adapter.dev, "i801_block_transaction_by_block\n"); - - inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */ - - /* Use 32-byte buffer to process this transaction */ - if (read_write == I2C_SMBUS_WRITE) { - len = data->block[0]; - outb_p(len, SMBHSTDAT0(priv)); - for (i = 0; i < len; i++) { - outb_p(data->block[i+1], SMBBLKDAT(priv)); - } - } - - status = i801_transaction(priv, I801_BLOCK_DATA | ENABLE_INT9 | I801_PEC_EN * hwpec); - if (status) { - return status; - } - - if (read_write == I2C_SMBUS_READ) { - len = inb_p(SMBHSTDAT0(priv)); - if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { - return -EPROTO; - } - - data->block[0] = len; - for (i = 0; i < len; i++) { - data->block[i + 1] = inb_p(SMBBLKDAT(priv)); - } - } - return 0; -} - -static int i801_block_transaction_byte_by_byte(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec) -{ - int i, len; - int smbcmd; - int status; - int result; - int timeout; - - dev_dbg(&priv->adapter.dev, "i801_block_transaction_byte_by_byte\n"); - - result = i801_check_pre(priv); - if (result < 0) { - return result; - } - - len = data->block[0]; - - if (read_write == I2C_SMBUS_WRITE) { - outb_p(len, SMBHSTDAT0(priv)); - outb_p(data->block[1], SMBBLKDAT(priv)); - } - - for (i = 1; i <= len; i++) { - if (i == len && read_write == I2C_SMBUS_READ) { - if (command == I2C_SMBUS_I2C_BLOCK_DATA) { - smbcmd = I801_I2C_BLOCK_LAST; - } else { - smbcmd = I801_BLOCK_LAST; - } - } else { - if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) { - smbcmd = I801_I2C_BLOCK_DATA; - } else { - smbcmd = I801_BLOCK_DATA; - } - } - outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv)); - - if (i == 1) { - outb_p(inb(SMBHSTCNT(priv)) | I801_START, SMBHSTCNT(priv)); - } - /* We will always wait for a fraction of a second! */ - timeout = 0; - do { - usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); - } while ((!(status & SMBHSTSTS_BYTE_DONE)) && (timeout++ < MAX_RETRIES)); - - result = i801_check_post(priv, status, timeout > MAX_RETRIES); - if (result < 0) { - return result; - } - if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) { - len = inb_p(SMBHSTDAT0(priv)); - if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { - dev_err(&priv->adapter.dev, "Illegal SMBus block read size %d\n", len); - /* Recover */ - while (inb_p(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) { - outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); - } - outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); - return -EPROTO; - } - data->block[0] = len; - } - - /* Retrieve/store value in SMBBLKDAT */ - if (read_write == I2C_SMBUS_READ) { - data->block[i] = inb_p(SMBBLKDAT(priv)); - } - if (read_write == I2C_SMBUS_WRITE && i+1 <= len) { - outb_p(data->block[i+1], SMBBLKDAT(priv)); - } - /* signals SMBBLKDAT ready */ - outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv)); - } - - return 0; -} - -static int i801_set_block_buffer_mode(struct i2c_device *priv) -{ - dev_dbg(&priv->adapter.dev, "i801_set_block_buffer_mode\n"); - - outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv)); - if ((inb_p(SMBAUXCTL(priv)) & SMBAUXCTL_E32B) == 0) { - return -EIO; - } - return 0; -} - -/* Block transaction function */ -static int i801_block_transaction(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec) -{ - int result = 0; - //unsigned char hostc; - - dev_dbg(&priv->adapter.dev, "i801_block_transaction\n"); - - if (command == I2C_SMBUS_I2C_BLOCK_DATA) { - if (read_write == I2C_SMBUS_WRITE) { - /* set I2C_EN bit in configuration register */ - //TODO: Figure out the right thing to do here... - //pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc); - //pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN); - } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) { - dev_err(&priv->adapter.dev, "I2C block read is unsupported!\n"); - return -EOPNOTSUPP; - } - } - - if (read_write == I2C_SMBUS_WRITE || command == I2C_SMBUS_I2C_BLOCK_DATA) { - if (data->block[0] < 1) { - data->block[0] = 1; - } - if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { - data->block[0] = I2C_SMBUS_BLOCK_MAX; - } - } else { - data->block[0] = 32; /* max for SMBus block reads */ - } - - /* Experience has shown that the block buffer can only be used for - SMBus (not I2C) block transactions, even though the datasheet - doesn't mention this limitation. */ - if ((priv->features & FEATURE_BLOCK_BUFFER) && command != I2C_SMBUS_I2C_BLOCK_DATA && i801_set_block_buffer_mode(priv) == 0) { - result = i801_block_transaction_by_block(priv, data, read_write, hwpec); - } else { - result = i801_block_transaction_byte_by_byte(priv, data, read_write, command, hwpec); - } - if (result == 0 && hwpec) { - i801_wait_hwpec(priv); - } - if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_WRITE) { - /* restore saved configuration register value */ - //TODO: Figure out the right thing to do here... - //pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc); - } - return result; -} - -/* Return negative errno on error. */ -static s32 i801_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) -{ - int hwpec; - int block = 0; - int ret, xact = 0; - struct i2c_device *priv = i2c_get_adapdata(adap); - - dev_dbg(&priv->adapter.dev, "i801_access (addr=%0d) flags=%x read_write=%x command=%x size=%x", - addr, flags, read_write, command, size ); - - hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA; - - switch (size) { - case I2C_SMBUS_QUICK: - dev_dbg(&priv->adapter.dev, " [acc] SMBUS_QUICK\n"); - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); - xact = I801_QUICK; - break; - case I2C_SMBUS_BYTE: - dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BYTE\n"); - - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); - if (read_write == I2C_SMBUS_WRITE) { - outb_p(command, SMBHSTCMD(priv)); - } - xact = I801_BYTE; - break; - case I2C_SMBUS_BYTE_DATA: - dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BYTE_DATA\n"); - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); - outb_p(command, SMBHSTCMD(priv)); - if (read_write == I2C_SMBUS_WRITE) { - outb_p(data->byte, SMBHSTDAT0(priv)); - } - xact = I801_BYTE_DATA; - break; - case I2C_SMBUS_WORD_DATA: - dev_dbg(&priv->adapter.dev, " [acc] SMBUS_WORD_DATA\n"); - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); - outb_p(command, SMBHSTCMD(priv)); - if (read_write == I2C_SMBUS_WRITE) { - outb_p(data->word & 0xff, SMBHSTDAT0(priv)); - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); - } - xact = I801_WORD_DATA; - break; - case I2C_SMBUS_BLOCK_DATA: - dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BLOCK_DATA\n"); - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); - outb_p(command, SMBHSTCMD(priv)); - block = 1; - break; - case I2C_SMBUS_I2C_BLOCK_DATA: - dev_dbg(&priv->adapter.dev, " [acc] SMBUS_I2C_BLOCK_DATA\n"); - /* NB: page 240 of ICH5 datasheet shows that the R/#W - * bit should be cleared here, even when reading */ - outb_p((addr & 0x7f) << 1, SMBHSTADD(priv)); - if (read_write == I2C_SMBUS_READ) { - /* NB: page 240 of ICH5 datasheet also shows - * that DATA1 is the cmd field when reading */ - outb_p(command, SMBHSTDAT1(priv)); - } else { - outb_p(command, SMBHSTCMD(priv)); - } - block = 1; - break; - default: - dev_dbg(&priv->adapter.dev, " [acc] Unsupported transaction %d\n", size); - return -EOPNOTSUPP; - } - - if (hwpec) { /* enable/disable hardware PEC */ - dev_dbg(&priv->adapter.dev, " [acc] hwpec: yes\n"); - outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); - } else { - dev_dbg(&priv->adapter.dev, " [acc] hwpec: no\n"); - outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), SMBAUXCTL(priv)); - } - - if (block) { - //ret = 0; - dev_dbg(&priv->adapter.dev, " [acc] block: yes\n"); - ret = i801_block_transaction(priv, data, read_write, size, hwpec); - } else { - dev_dbg(&priv->adapter.dev, " [acc] block: no\n"); - ret = i801_transaction(priv, xact | ENABLE_INT9); - } - - /* Some BIOSes don't like it when PEC is enabled at reboot or resume - time, so we forcibly disable it after every transaction. Turn off - E32B for the same reason. */ - if (hwpec || block) { - dev_dbg(&priv->adapter.dev, " [acc] hwpec || block\n"); - outb_p(inb_p(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); - } - if (block) { - dev_dbg(&priv->adapter.dev, " [acc] block\n"); - return ret; - } - if (ret) { - dev_dbg(&priv->adapter.dev, " [acc] ret %d\n", ret); - return ret; - } - if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) { - dev_dbg(&priv->adapter.dev, " [acc] I2C_SMBUS_WRITE || I801_QUICK -> ret 0\n"); - return 0; - } - - switch (xact & 0x7f) { - case I801_BYTE: /* Result put in SMBHSTDAT0 */ - case I801_BYTE_DATA: - dev_dbg(&priv->adapter.dev, " [acc] I801_BYTE or I801_BYTE_DATA\n"); - data->byte = inb_p(SMBHSTDAT0(priv)); - break; - case I801_WORD_DATA: - dev_dbg(&priv->adapter.dev, " [acc] I801_WORD_DATA\n"); - data->word = inb_p(SMBHSTDAT0(priv)) + (inb_p(SMBHSTDAT1(priv)) << 8); - break; - } - return 0; -} - - - -static u32 i801_func(struct i2c_adapter *adapter) -{ - struct i2c_device *priv = i2c_get_adapdata(adapter); - - /* original settings - u32 f = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | - ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | - ((priv->features & FEATURE_I2C_BLOCK_READ) ? - I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0); - */ - - // http://lxr.free-electrons.com/source/include/uapi/linux/i2c.h#L85 - - u32 f = - I2C_FUNC_I2C | /* 0x00000001 (I enabled this one) */ - !I2C_FUNC_10BIT_ADDR | /* 0x00000002 */ - !I2C_FUNC_PROTOCOL_MANGLING | /* 0x00000004 */ - ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | /* 0x00000008 */ - !I2C_FUNC_SMBUS_BLOCK_PROC_CALL | /* 0x00008000 */ - I2C_FUNC_SMBUS_QUICK | /* 0x00010000 */ - !I2C_FUNC_SMBUS_READ_BYTE | /* 0x00020000 */ - !I2C_FUNC_SMBUS_WRITE_BYTE | /* 0x00040000 */ - !I2C_FUNC_SMBUS_READ_BYTE_DATA | /* 0x00080000 */ - !I2C_FUNC_SMBUS_WRITE_BYTE_DATA | /* 0x00100000 */ - !I2C_FUNC_SMBUS_READ_WORD_DATA | /* 0x00200000 */ - !I2C_FUNC_SMBUS_WRITE_WORD_DATA | /* 0x00400000 */ - !I2C_FUNC_SMBUS_PROC_CALL | /* 0x00800000 */ - !I2C_FUNC_SMBUS_READ_BLOCK_DATA | /* 0x01000000 */ - !I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | /* 0x02000000 */ - ((priv->features & FEATURE_I2C_BLOCK_READ) ? I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0) | /* 0x04000000 */ - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | /* 0x08000000 */ - - I2C_FUNC_SMBUS_BYTE | /* _READ_BYTE _WRITE_BYTE */ - I2C_FUNC_SMBUS_BYTE_DATA | /* _READ_BYTE_DATA _WRITE_BYTE_DATA */ - I2C_FUNC_SMBUS_WORD_DATA | /* _READ_WORD_DATA _WRITE_WORD_DATA */ - I2C_FUNC_SMBUS_BLOCK_DATA | /* _READ_BLOCK_DATA _WRITE_BLOCK_DATA */ - !I2C_FUNC_SMBUS_I2C_BLOCK | /* _READ_I2C_BLOCK _WRITE_I2C_BLOCK */ - !I2C_FUNC_SMBUS_EMUL; /* _QUICK _BYTE _BYTE_DATA _WORD_DATA _PROC_CALL _WRITE_BLOCK_DATA _I2C_BLOCK _PEC */ - return f; -} - -static const struct i2c_algorithm smbus_algorithm = { - .smbus_xfer = i801_access, - .functionality = i801_func, -}; - - - -/******************************** - *** Part 2 - Driver Handlers *** - ********************************/ -int pi2c_probe(struct platform_device *pldev) -{ - int err; - struct i2c_device *priv; - struct resource *res; - - dev_dbg(&pldev->dev, "pi2c_probe(pldev = %p '%s')\n", pldev, pldev->name); - - priv = kzalloc(sizeof(struct i2c_device), GFP_KERNEL); - if (!priv) { - return -ENOMEM; - } - - i2c_set_adapdata(&priv->adapter, priv); - priv->adapter.owner = THIS_MODULE; - priv->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; - priv->adapter.algo = &smbus_algorithm; - - res = platform_get_resource(pldev, IORESOURCE_MEM, 0); - priv->smba = (unsigned long)ioremap_nocache(res->start, resource_size(res)); - - priv->pldev = pldev; - pldev->dev.platform_data = priv; - - priv->features |= FEATURE_IDF; - priv->features |= FEATURE_I2C_BLOCK_READ; - priv->features |= FEATURE_SMBUS_PEC; - priv->features |= FEATURE_BLOCK_BUFFER; - - //init_MUTEX(&lddata->sem); - init_rwsem(&priv->rw_sem); - - /* set up the sysfs linkage to our parent device */ - priv->adapter.dev.parent = &pldev->dev; - - /* Retry up to 3 times on lost arbitration */ - priv->adapter.retries = 3; - - //snprintf(priv->adapter.name, sizeof(priv->adapter.name), "Fake SMBus I801 adapter at %04lx", priv->smba); - snprintf(priv->adapter.name, sizeof(priv->adapter.name), "Fake SMBus I801 adapter"); - - err = i2c_add_adapter(&priv->adapter); - if (err) { - dev_err(&priv->adapter.dev, "Failed to add SMBus adapter\n"); - return err; - } - - return 0; -} - -int pi2c_remove(struct platform_device *pldev) -{ - struct i2c_device *lddev; - dev_dbg(&pldev->dev, "pi2c_remove(pldev = %p '%s')\n", pldev, pldev->name); - - lddev = (struct i2c_device *)pldev->dev.platform_data; - - i2c_del_adapter(&lddev->adapter); - - //TODO: Figure out the right thing to do here... - //pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); - //pci_release_region(dev, SMBBAR); - //pci_set_drvdata(dev, NULL); - - //cdev_del(&lddev->cdev); - if(lddev != 0) { - kfree(lddev); - pldev->dev.platform_data = 0; - } - - return 0; -} - -struct platform_driver i2c_plat_driver_i = { - .probe = pi2c_probe, - .remove = pi2c_remove, - .driver = { - .name = KP_DRIVER_NAME_I2C, - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(i2c_plat_driver_i); diff --git a/drivers/staging/kpc2000/kpc_spi/Makefile b/drivers/staging/kpc2000/kpc_spi/Makefile deleted file mode 100644 index 3018d200484f..000000000000 --- a/drivers/staging/kpc2000/kpc_spi/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-m += kpc2000_spi.o -kpc2000_spi-objs := spi_driver.o diff --git a/drivers/staging/kpc2000/kpc_spi/spi_driver.c b/drivers/staging/kpc2000/kpc_spi/spi_driver.c deleted file mode 100644 index 86df16547a92..000000000000 --- a/drivers/staging/kpc2000/kpc_spi/spi_driver.c +++ /dev/null @@ -1,507 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * KP2000 SPI controller driver - * - * Copyright (C) 2014-2018 Daktronics - * Author: Matt Sickler <matt.sickler@daktronics.com> - * Very loosely based on spi-omap2-mcspi.c - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io-64-nonatomic-lo-hi.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/err.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/pm_runtime.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/gcd.h> -#include <linux/spi/spi.h> -#include <linux/spi/flash.h> -#include <linux/mtd/partitions.h> - -#include "../kpc.h" -#include "spi_parts.h" - - -/*************** - * SPI Defines * - ***************/ -#define KP_SPI_REG_CONFIG 0x0 /* 0x00 */ -#define KP_SPI_REG_STATUS 0x1 /* 0x08 */ -#define KP_SPI_REG_FFCTRL 0x2 /* 0x10 */ -#define KP_SPI_REG_TXDATA 0x3 /* 0x18 */ -#define KP_SPI_REG_RXDATA 0x4 /* 0x20 */ - -#define KP_SPI_CLK 48000000 -#define KP_SPI_MAX_FIFODEPTH 64 -#define KP_SPI_MAX_FIFOWCNT 0xFFFF - -#define KP_SPI_REG_CONFIG_TRM_TXRX 0 -#define KP_SPI_REG_CONFIG_TRM_RX 1 -#define KP_SPI_REG_CONFIG_TRM_TX 2 - -#define KP_SPI_REG_STATUS_RXS 0x01 -#define KP_SPI_REG_STATUS_TXS 0x02 -#define KP_SPI_REG_STATUS_EOT 0x04 -#define KP_SPI_REG_STATUS_TXFFE 0x10 -#define KP_SPI_REG_STATUS_TXFFF 0x20 -#define KP_SPI_REG_STATUS_RXFFE 0x40 -#define KP_SPI_REG_STATUS_RXFFF 0x80 - - - -/****************** - * SPI Structures * - ******************/ -struct kp_spi { - struct spi_master *master; - u64 __iomem *base; - unsigned long phys; - struct device *dev; - int fifo_depth; - unsigned int pin_dir:1; -}; - - -struct kp_spi_controller_state { - void __iomem *base; - unsigned long phys; - unsigned char chip_select; - int word_len; - s64 conf_cache; -}; - - -union kp_spi_config { - /* use this to access individual elements */ - struct __attribute__((packed)) spi_config_bitfield { - unsigned int pha : 1; /* spim_clk Phase */ - unsigned int pol : 1; /* spim_clk Polarity */ - unsigned int epol : 1; /* spim_csx Polarity */ - unsigned int dpe : 1; /* Transmission Enable */ - unsigned int wl : 5; /* Word Length */ - unsigned int : 3; - unsigned int trm : 2; /* TxRx Mode */ - unsigned int cs : 4; /* Chip Select */ - unsigned int wcnt : 7; /* Word Count */ - unsigned int ffen : 1; /* FIFO Enable */ - unsigned int spi_en : 1; /* SPI Enable */ - unsigned int : 5; - } bitfield; - /* use this to grab the whole register */ - u32 reg; -}; - - - -union kp_spi_status { - struct __attribute__((packed)) spi_status_bitfield { - unsigned int rx : 1; /* Rx Status */ - unsigned int tx : 1; /* Tx Status */ - unsigned int eo : 1; /* End of Transfer */ - unsigned int : 1; - unsigned int txffe : 1; /* Tx FIFO Empty */ - unsigned int txfff : 1; /* Tx FIFO Full */ - unsigned int rxffe : 1; /* Rx FIFO Empty */ - unsigned int rxfff : 1; /* Rx FIFO Full */ - unsigned int : 24; - } bitfield; - u32 reg; -}; - - - -union kp_spi_ffctrl { - struct __attribute__((packed)) spi_ffctrl_bitfield { - unsigned int ffstart : 1; /* FIFO Start */ - unsigned int : 31; - } bitfield; - u32 reg; -}; - - - -/*************** - * SPI Helpers * - ***************/ -static inline int -kp_spi_bytes_per_word(int word_len) -{ - if (word_len <= 8){ - return 1; - } - else if (word_len <= 16) { - return 2; - } - else { /* word_len <= 32 */ - return 4; - } -} - -static inline u64 -kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx) -{ - u64 __iomem *addr = cs->base; - u64 val; - - addr += idx; - if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)){ - return cs->conf_cache; - } - val = readq((void*)addr); - return val; -} - -static inline void -kp_spi_write_reg(struct kp_spi_controller_state *cs, int idx, u64 val) -{ - u64 __iomem *addr = cs->base; - addr += idx; - writeq(val, (void*)addr); - if (idx == KP_SPI_REG_CONFIG) - cs->conf_cache = val; -} - -static int -kp_spi_wait_for_reg_bit(struct kp_spi_controller_state *cs, int idx, unsigned long bit) -{ - unsigned long timeout; - timeout = jiffies + msecs_to_jiffies(1000); - while (!(kp_spi_read_reg(cs, idx) & bit)) { - if (time_after(jiffies, timeout)) { - if (!(kp_spi_read_reg(cs, idx) & bit)) { - return -ETIMEDOUT; - } else { - return 0; - } - } - cpu_relax(); - } - return 0; -} - -static unsigned -kp_spi_txrx_pio(struct spi_device *spidev, struct spi_transfer *transfer) -{ - struct kp_spi_controller_state *cs = spidev->controller_state; - unsigned int count = transfer->len; - unsigned int c = count; - - int i; - u8 *rx = transfer->rx_buf; - const u8 *tx = transfer->tx_buf; - int processed = 0; - - if (tx) { - for (i = 0 ; i < c ; i++) { - char val = *tx++; - - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_TXS) < 0) { - goto out; - } - - kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, val); - processed++; - } - } - else if(rx) { - for (i = 0 ; i < c ; i++) { - char test=0; - - kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, 0x00); - - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_RXS) < 0) { - goto out; - } - - test = kp_spi_read_reg(cs, KP_SPI_REG_RXDATA); - *rx++ = test; - processed++; - } - } - - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { - //TODO: Figure out how to abort transaction?? This has never happened in practice though... - } - - out: - return processed; -} - -/***************** - * SPI Functions * - *****************/ -static int -kp_spi_setup(struct spi_device *spidev) -{ - union kp_spi_config sc; - struct kp_spi *kpspi = spi_master_get_devdata(spidev->master); - struct kp_spi_controller_state *cs; - - /* setup controller state */ - cs = spidev->controller_state; - if (!cs) { - cs = kzalloc(sizeof(*cs), GFP_KERNEL); - if(!cs) { - return -ENOMEM; - } - cs->base = kpspi->base; - cs->phys = kpspi->phys; - cs->chip_select = spidev->chip_select; - cs->word_len = spidev->bits_per_word; - cs->conf_cache = -1; - spidev->controller_state = cs; - } - - /* set config register */ - sc.bitfield.wl = spidev->bits_per_word - 1; - sc.bitfield.cs = spidev->chip_select; - sc.bitfield.spi_en = 0; - sc.bitfield.trm = 0; - sc.bitfield.ffen = 0; - kp_spi_write_reg(spidev->controller_state, KP_SPI_REG_CONFIG, sc.reg); - return 0; -} - -static int -kp_spi_transfer_one_message(struct spi_master *master, struct spi_message *m) -{ - struct kp_spi_controller_state *cs; - struct spi_device *spidev; - struct kp_spi *kpspi; - struct spi_transfer *transfer; - union kp_spi_config sc; - int status = 0; - - spidev = m->spi; - kpspi = spi_master_get_devdata(master); - m->actual_length = 0; - m->status = 0; - - cs = spidev->controller_state; - - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers)) { - return -EINVAL; - } - - /* validate input */ - list_for_each_entry(transfer, &m->transfers, transfer_list) { - const void *tx_buf = transfer->tx_buf; - void *rx_buf = transfer->rx_buf; - unsigned len = transfer->len; - - if (transfer->speed_hz > KP_SPI_CLK || (len && !(rx_buf || tx_buf))) { - dev_dbg(kpspi->dev, " transfer: %d Hz, %d %s%s, %d bpw\n", - transfer->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - transfer->bits_per_word); - dev_dbg(kpspi->dev, " transfer -EINVAL\n"); - return -EINVAL; - } - if (transfer->speed_hz && (transfer->speed_hz < (KP_SPI_CLK >> 15))) { - dev_dbg(kpspi->dev, "speed_hz %d below minimum %d Hz\n", - transfer->speed_hz, - KP_SPI_CLK >> 15); - dev_dbg(kpspi->dev, " speed_hz -EINVAL\n"); - return -EINVAL; - } - } - - /* assert chip select to start the sequence*/ - sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); - sc.bitfield.spi_en = 1; - kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); - - /* work */ - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { - dev_info(kpspi->dev, "EOT timed out\n"); - goto out; - } - - /* do the transfers for this message */ - list_for_each_entry(transfer, &m->transfers, transfer_list) { - if (transfer->tx_buf == NULL && transfer->rx_buf == NULL && transfer->len) { - status = -EINVAL; - break; - } - - /* transfer */ - if (transfer->len) { - unsigned int word_len = spidev->bits_per_word; - unsigned count; - - /* set up the transfer... */ - sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); - - /* ...direction */ - if (transfer->tx_buf) { - sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_TX; - } - else if (transfer->rx_buf) { - sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_RX; - } - - /* ...word length */ - if (transfer->bits_per_word) { - word_len = transfer->bits_per_word; - } - cs->word_len = word_len; - sc.bitfield.wl = word_len-1; - - /* ...chip select */ - sc.bitfield.cs = spidev->chip_select; - - /* ...and write the new settings */ - kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); - - /* do the transfer */ - count = kp_spi_txrx_pio(spidev, transfer); - m->actual_length += count; - - if (count != transfer->len) { - status = -EIO; - break; - } - } - - if (transfer->delay_usecs) { - udelay(transfer->delay_usecs); - } - } - - /* de-assert chip select to end the sequence */ - sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); - sc.bitfield.spi_en = 0; - kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); - - out: - /* done work */ - spi_finalize_current_message(master); - return 0; -} - -static void -kp_spi_cleanup(struct spi_device *spidev) -{ - struct kp_spi_controller_state *cs = spidev->controller_state; - if (cs) { - kfree(cs); - } -} - - - -/****************** - * Probe / Remove * - ******************/ -static int -kp_spi_probe(struct platform_device *pldev) -{ - struct kpc_core_device_platdata *drvdata; - struct spi_master *master; - struct kp_spi *kpspi; - struct resource *r; - int status = 0; - int i; - - drvdata = pldev->dev.platform_data; - if (!drvdata){ - dev_err(&pldev->dev, "kp_spi_probe: platform_data is NULL!\n"); - return -ENODEV; - } - - master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi)); - if (master == NULL) { - dev_err(&pldev->dev, "kp_spi_probe: master allocation failed\n"); - return -ENOMEM; - } - - /* set up the spi functions */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - master->bits_per_word_mask = (unsigned int)SPI_BPW_RANGE_MASK(4, 32); - master->setup = kp_spi_setup; - master->transfer_one_message = kp_spi_transfer_one_message; - master->cleanup = kp_spi_cleanup; - - platform_set_drvdata(pldev, master); - - kpspi = spi_master_get_devdata(master); - kpspi->master = master; - kpspi->dev = &pldev->dev; - - master->num_chipselect = 4; - if (pldev->id != -1) { - master->bus_num = pldev->id; - } - kpspi->pin_dir = 0; - - r = platform_get_resource(pldev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pldev->dev, "kp_spi_probe: Unable to get platform resources\n"); - status = -ENODEV; - goto free_master; - } - - kpspi->phys = (unsigned long)ioremap_nocache(r->start, resource_size(r)); - kpspi->base = (u64 __iomem *)kpspi->phys; - - status = spi_register_master(master); - if (status < 0) { - dev_err(&pldev->dev, "Unable to register SPI device\n"); - goto free_master; - } - - /* register the slave boards */ - #define NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(table) \ - for (i = 0 ; i < ARRAY_SIZE(table) ; i++) { \ - spi_new_device(master, &(table[i])); \ - } - - switch ((drvdata->card_id & 0xFFFF0000) >> 16){ - case PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0: - NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(p2kr0_board_info); - break; - default: - dev_err(&pldev->dev, "Unknown hardware, cant know what partition table to use!\n"); - goto free_master; - break; - } - - return status; - - free_master: - spi_master_put(master); - return status; -} - -static int -kp_spi_remove(struct platform_device *pldev) -{ - struct spi_master * master = platform_get_drvdata(pldev); - spi_unregister_master(master); - return 0; -} - - -static struct platform_driver kp_spi_driver = { - .driver = { - .name = KP_DRIVER_NAME_SPI, - }, - .probe = kp_spi_probe, - .remove = kp_spi_remove, -}; - -module_platform_driver(kp_spi_driver); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:kp_spi"); diff --git a/drivers/staging/kpc2000/kpc_spi/spi_parts.h b/drivers/staging/kpc2000/kpc_spi/spi_parts.h deleted file mode 100644 index 33e62acc5e08..000000000000 --- a/drivers/staging/kpc2000/kpc_spi/spi_parts.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -#ifndef __KPC_SPI_SPI_PARTS_H__ -#define __KPC_SPI_SPI_PARTS_H__ - -static struct mtd_partition p2kr0_spi0_parts[] = { - { .name = "SLOT_0", .size = 7798784, .offset = 0, }, - { .name = "SLOT_1", .size = 7798784, .offset = MTDPART_OFS_NXTBLK }, - { .name = "SLOT_2", .size = 7798784, .offset = MTDPART_OFS_NXTBLK }, - { .name = "SLOT_3", .size = 7798784, .offset = MTDPART_OFS_NXTBLK }, - { .name = "CS0_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK } -}; -static struct mtd_partition p2kr0_spi1_parts[] = { - { .name = "SLOT_4", .size = 7798784, .offset = 0, }, - { .name = "SLOT_5", .size = 7798784, .offset = MTDPART_OFS_NXTBLK }, - { .name = "SLOT_6", .size = 7798784, .offset = MTDPART_OFS_NXTBLK }, - { .name = "SLOT_7", .size = 7798784, .offset = MTDPART_OFS_NXTBLK }, - { .name = "CS1_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK } -}; - -static struct flash_platform_data p2kr0_spi0_pdata = { - .name = "SPI0", - .nr_parts = ARRAY_SIZE(p2kr0_spi0_parts), - .parts = p2kr0_spi0_parts, -}; -static struct flash_platform_data p2kr0_spi1_pdata = { - .name = "SPI1", - .nr_parts = ARRAY_SIZE(p2kr0_spi1_parts), - .parts = p2kr0_spi1_parts, -}; - -static struct spi_board_info p2kr0_board_info[] = { - { - .modalias = "n25q256a11", - .bus_num = 1, - .chip_select = 0, - .mode = SPI_MODE_0, - .platform_data = &p2kr0_spi0_pdata - }, - { - .modalias = "n25q256a11", - .bus_num = 1, - .chip_select = 1, - .mode = SPI_MODE_0, - .platform_data = &p2kr0_spi1_pdata - }, -}; - -#endif |