diff options
author | Mark Brown <broonie@kernel.org> | 2020-12-11 20:48:32 +0300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2020-12-11 20:48:32 +0300 |
commit | 5e999f10a16b90fc1d5ded8aa365e9804e894aa9 (patch) | |
tree | 01a608e25dbfdc4f08a30e5f5c817b9146cff85d /drivers | |
parent | 291de1d102fafef0798cdad9666cd4f8da7da7cc (diff) | |
parent | 2819569147cb6e79730f2907d3ab3dfe75fe8478 (diff) | |
download | linux-5e999f10a16b90fc1d5ded8aa365e9804e894aa9.tar.xz |
Merge remote-tracking branch 'regulator/for-5.11' into regulator-next
Diffstat (limited to 'drivers')
265 files changed, 4679 insertions, 23503 deletions
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index da4b125ab4c3..0761529cac05 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -74,19 +74,6 @@ MODULE_DEVICE_TABLE(acpi, button_device_ids); /* Please keep this list sorted alphabetically by vendor and model */ static const struct dmi_system_id dmi_lid_quirks[] = { { - /* - * Acer Switch 10 SW5-012. _LID method messes with home and - * power button GPIO IRQ settings causing an interrupt storm on - * both GPIOs. This is unfixable without a DSDT override, so we - * have to disable the lid-switch functionality altogether :| - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), - }, - .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, - }, - { /* GP-electronic T701, _LID method points to a floating GPIO */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 45d4b7b69de8..24e076f44d23 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -231,7 +231,8 @@ static void hot_remove_dock_devices(struct dock_station *ds) * between them). */ list_for_each_entry_reverse(dd, &ds->dependent_devices, list) - dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); + dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, + DOCK_CALL_HANDLER); list_for_each_entry_reverse(dd, &ds->dependent_devices, list) acpi_bus_trim(dd->adev); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 756227837b3b..3a3c209ed3d3 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1564,7 +1564,7 @@ static ssize_t format1_show(struct device *dev, le16_to_cpu(nfit_dcr->dcr->code)); break; } - if (rc != ENXIO) + if (rc != -ENXIO) break; } mutex_unlock(&acpi_desc->init_mutex); diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index eb9dc14e5147..20190f66ced9 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -2100,7 +2100,7 @@ static int nv_swncq_sdbfis(struct ata_port *ap) pp->dhfis_bits &= ~done_mask; pp->dmafis_bits &= ~done_mask; pp->sdbfis_bits |= done_mask; - ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask); + ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask); if (!ap->qc_active) { DPRINTK("over\n"); diff --git a/drivers/base/core.c b/drivers/base/core.c index c852f16c111b..78114ddac755 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4264,6 +4264,7 @@ static inline bool fwnode_is_primary(struct fwnode_handle *fwnode) */ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) { + struct device *parent = dev->parent; struct fwnode_handle *fn = dev->fwnode; if (fwnode) { @@ -4278,7 +4279,8 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) } else { if (fwnode_is_primary(fn)) { dev->fwnode = fn->secondary; - fn->secondary = NULL; + if (!(parent && fn == parent->fwnode)) + fn->secondary = ERR_PTR(-ENODEV); } else { dev->fwnode = NULL; } diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 0bed21c0c81b..c4f9ccf5cc2a 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -296,7 +296,7 @@ static void nbd_size_clear(struct nbd_device *nbd) } } -static void nbd_size_update(struct nbd_device *nbd) +static void nbd_size_update(struct nbd_device *nbd, bool start) { struct nbd_config *config = nbd->config; struct block_device *bdev = bdget_disk(nbd->disk, 0); @@ -313,7 +313,8 @@ static void nbd_size_update(struct nbd_device *nbd) if (bdev) { if (bdev->bd_disk) { bd_set_nr_sectors(bdev, nr_sectors); - set_blocksize(bdev, config->blksize); + if (start) + set_blocksize(bdev, config->blksize); } else set_bit(GD_NEED_PART_SCAN, &nbd->disk->state); bdput(bdev); @@ -328,7 +329,7 @@ static void nbd_size_set(struct nbd_device *nbd, loff_t blocksize, config->blksize = blocksize; config->bytesize = blocksize * nr_blocks; if (nbd->task_recv != NULL) - nbd_size_update(nbd); + nbd_size_update(nbd, false); } static void nbd_complete_rq(struct request *req) @@ -1308,7 +1309,7 @@ static int nbd_start_device(struct nbd_device *nbd) args->index = i; queue_work(nbd->recv_workq, &args->work); } - nbd_size_update(nbd); + nbd_size_update(nbd, true); return error; } diff --git a/drivers/block/null_blk.h b/drivers/block/null_blk.h index d2e7db43a52a..cfd00ad40355 100644 --- a/drivers/block/null_blk.h +++ b/drivers/block/null_blk.h @@ -47,6 +47,8 @@ struct nullb_device { unsigned int nr_zones_closed; struct blk_zone *zones; sector_t zone_size_sects; + spinlock_t zone_dev_lock; + unsigned long *zone_locks; unsigned long size; /* device size in MB */ unsigned long completion_nsec; /* time in ns to complete a request */ diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index 7d94f2d47a6a..8775acbb4f8f 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/vmalloc.h> +#include <linux/bitmap.h> #include "null_blk.h" #define CREATE_TRACE_POINTS @@ -45,6 +46,13 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) if (!dev->zones) return -ENOMEM; + spin_lock_init(&dev->zone_dev_lock); + dev->zone_locks = bitmap_zalloc(dev->nr_zones, GFP_KERNEL); + if (!dev->zone_locks) { + kvfree(dev->zones); + return -ENOMEM; + } + if (dev->zone_nr_conv >= dev->nr_zones) { dev->zone_nr_conv = dev->nr_zones - 1; pr_info("changed the number of conventional zones to %u", @@ -123,15 +131,26 @@ int null_register_zoned_dev(struct nullb *nullb) void null_free_zoned_dev(struct nullb_device *dev) { + bitmap_free(dev->zone_locks); kvfree(dev->zones); } +static inline void null_lock_zone(struct nullb_device *dev, unsigned int zno) +{ + wait_on_bit_lock_io(dev->zone_locks, zno, TASK_UNINTERRUPTIBLE); +} + +static inline void null_unlock_zone(struct nullb_device *dev, unsigned int zno) +{ + clear_and_wake_up_bit(zno, dev->zone_locks); +} + int null_report_zones(struct gendisk *disk, sector_t sector, unsigned int nr_zones, report_zones_cb cb, void *data) { struct nullb *nullb = disk->private_data; struct nullb_device *dev = nullb->dev; - unsigned int first_zone, i; + unsigned int first_zone, i, zno; struct blk_zone zone; int error; @@ -142,15 +161,18 @@ int null_report_zones(struct gendisk *disk, sector_t sector, nr_zones = min(nr_zones, dev->nr_zones - first_zone); trace_nullb_report_zones(nullb, nr_zones); - for (i = 0; i < nr_zones; i++) { + zno = first_zone; + for (i = 0; i < nr_zones; i++, zno++) { /* * Stacked DM target drivers will remap the zone information by * modifying the zone information passed to the report callback. * So use a local copy to avoid corruption of the device zone * array. */ - memcpy(&zone, &dev->zones[first_zone + i], - sizeof(struct blk_zone)); + null_lock_zone(dev, zno); + memcpy(&zone, &dev->zones[zno], sizeof(struct blk_zone)); + null_unlock_zone(dev, zno); + error = cb(&zone, i, data); if (error) return error; @@ -159,6 +181,10 @@ int null_report_zones(struct gendisk *disk, sector_t sector, return nr_zones; } +/* + * This is called in the case of memory backing from null_process_cmd() + * with the target zone already locked. + */ size_t null_zone_valid_read_len(struct nullb *nullb, sector_t sector, unsigned int len) { @@ -295,22 +321,27 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors); + null_lock_zone(dev, zno); + spin_lock(&dev->zone_dev_lock); + switch (zone->cond) { case BLK_ZONE_COND_FULL: /* Cannot write to a full zone */ - return BLK_STS_IOERR; + ret = BLK_STS_IOERR; + goto unlock; case BLK_ZONE_COND_EMPTY: case BLK_ZONE_COND_CLOSED: ret = null_check_zone_resources(dev, zone); if (ret != BLK_STS_OK) - return ret; + goto unlock; break; case BLK_ZONE_COND_IMP_OPEN: case BLK_ZONE_COND_EXP_OPEN: break; default: /* Invalid zone condition */ - return BLK_STS_IOERR; + ret = BLK_STS_IOERR; + goto unlock; } /* @@ -326,11 +357,14 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, else cmd->rq->__sector = sector; } else if (sector != zone->wp) { - return BLK_STS_IOERR; + ret = BLK_STS_IOERR; + goto unlock; } - if (zone->wp + nr_sectors > zone->start + zone->capacity) - return BLK_STS_IOERR; + if (zone->wp + nr_sectors > zone->start + zone->capacity) { + ret = BLK_STS_IOERR; + goto unlock; + } if (zone->cond == BLK_ZONE_COND_CLOSED) { dev->nr_zones_closed--; @@ -341,9 +375,11 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, if (zone->cond != BLK_ZONE_COND_EXP_OPEN) zone->cond = BLK_ZONE_COND_IMP_OPEN; + spin_unlock(&dev->zone_dev_lock); ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors); + spin_lock(&dev->zone_dev_lock); if (ret != BLK_STS_OK) - return ret; + goto unlock; zone->wp += nr_sectors; if (zone->wp == zone->start + zone->capacity) { @@ -353,7 +389,13 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, dev->nr_zones_imp_open--; zone->cond = BLK_ZONE_COND_FULL; } - return BLK_STS_OK; + ret = BLK_STS_OK; + +unlock: + spin_unlock(&dev->zone_dev_lock); + null_unlock_zone(dev, zno); + + return ret; } static blk_status_t null_open_zone(struct nullb_device *dev, struct blk_zone *zone) @@ -464,16 +506,33 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, sector_t sector) { struct nullb_device *dev = cmd->nq->dev; - unsigned int zone_no = null_zone_no(dev, sector); - struct blk_zone *zone = &dev->zones[zone_no]; - blk_status_t ret = BLK_STS_OK; + unsigned int zone_no; + struct blk_zone *zone; + blk_status_t ret; size_t i; + if (op == REQ_OP_ZONE_RESET_ALL) { + for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) { + null_lock_zone(dev, i); + zone = &dev->zones[i]; + if (zone->cond != BLK_ZONE_COND_EMPTY) { + spin_lock(&dev->zone_dev_lock); + null_reset_zone(dev, zone); + spin_unlock(&dev->zone_dev_lock); + trace_nullb_zone_op(cmd, i, zone->cond); + } + null_unlock_zone(dev, i); + } + return BLK_STS_OK; + } + + zone_no = null_zone_no(dev, sector); + zone = &dev->zones[zone_no]; + + null_lock_zone(dev, zone_no); + spin_lock(&dev->zone_dev_lock); + switch (op) { - case REQ_OP_ZONE_RESET_ALL: - for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) - null_reset_zone(dev, &dev->zones[i]); - break; case REQ_OP_ZONE_RESET: ret = null_reset_zone(dev, zone); break; @@ -487,30 +546,46 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, ret = null_finish_zone(dev, zone); break; default: - return BLK_STS_NOTSUPP; + ret = BLK_STS_NOTSUPP; + break; } + spin_unlock(&dev->zone_dev_lock); + if (ret == BLK_STS_OK) trace_nullb_zone_op(cmd, zone_no, zone->cond); + null_unlock_zone(dev, zone_no); + return ret; } blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op, sector_t sector, sector_t nr_sectors) { + struct nullb_device *dev = cmd->nq->dev; + unsigned int zno = null_zone_no(dev, sector); + blk_status_t sts; + switch (op) { case REQ_OP_WRITE: - return null_zone_write(cmd, sector, nr_sectors, false); + sts = null_zone_write(cmd, sector, nr_sectors, false); + break; case REQ_OP_ZONE_APPEND: - return null_zone_write(cmd, sector, nr_sectors, true); + sts = null_zone_write(cmd, sector, nr_sectors, true); + break; case REQ_OP_ZONE_RESET: case REQ_OP_ZONE_RESET_ALL: case REQ_OP_ZONE_OPEN: case REQ_OP_ZONE_CLOSE: case REQ_OP_ZONE_FINISH: - return null_zone_mgmt(cmd, op, sector); + sts = null_zone_mgmt(cmd, op, sector); + break; default: - return null_process_cmd(cmd, op, sector, nr_sectors); + null_lock_zone(dev, zno); + sts = null_process_cmd(cmd, op, sector, nr_sectors); + null_unlock_zone(dev, zno); } + + return sts; } diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 8d581c7536fb..eb8ef65778c3 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -443,22 +443,27 @@ static void ace_fix_driveid(u16 *id) #define ACE_FSM_NUM_STATES 11 /* Set flag to exit FSM loop and reschedule tasklet */ -static inline void ace_fsm_yield(struct ace_device *ace) +static inline void ace_fsm_yieldpoll(struct ace_device *ace) { - dev_dbg(ace->dev, "ace_fsm_yield()\n"); tasklet_schedule(&ace->fsm_tasklet); ace->fsm_continue_flag = 0; } +static inline void ace_fsm_yield(struct ace_device *ace) +{ + dev_dbg(ace->dev, "%s()\n", __func__); + ace_fsm_yieldpoll(ace); +} + /* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */ static inline void ace_fsm_yieldirq(struct ace_device *ace) { dev_dbg(ace->dev, "ace_fsm_yieldirq()\n"); - if (!ace->irq) - /* No IRQ assigned, so need to poll */ - tasklet_schedule(&ace->fsm_tasklet); - ace->fsm_continue_flag = 0; + if (ace->irq > 0) + ace->fsm_continue_flag = 0; + else + ace_fsm_yieldpoll(ace); } static bool ace_has_next_request(struct request_queue *q) @@ -1053,12 +1058,12 @@ static int ace_setup(struct ace_device *ace) ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ); /* Now we can hook up the irq handler */ - if (ace->irq) { + if (ace->irq > 0) { rc = request_irq(ace->irq, ace_interrupt, 0, "systemace", ace); if (rc) { /* Failure - fall back to polled mode */ dev_err(ace->dev, "request_irq failed\n"); - ace->irq = 0; + ace->irq = rc; } } @@ -1110,7 +1115,7 @@ static void ace_teardown(struct ace_device *ace) tasklet_kill(&ace->fsm_tasklet); - if (ace->irq) + if (ace->irq > 0) free_irq(ace->irq, ace); iounmap(ace->baseaddr); @@ -1123,11 +1128,6 @@ static int ace_alloc(struct device *dev, int id, resource_size_t physaddr, int rc; dev_dbg(dev, "ace_alloc(%p)\n", dev); - if (!physaddr) { - rc = -ENODEV; - goto err_noreg; - } - /* Allocate and initialize the ace device structure */ ace = kzalloc(sizeof(struct ace_device), GFP_KERNEL); if (!ace) { @@ -1153,7 +1153,6 @@ err_setup: dev_set_drvdata(dev, NULL); kfree(ace); err_alloc: -err_noreg: dev_err(dev, "could not initialize device, err=%i\n", rc); return rc; } @@ -1176,10 +1175,11 @@ static void ace_free(struct device *dev) static int ace_probe(struct platform_device *dev) { - resource_size_t physaddr = 0; int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */ + resource_size_t physaddr; + struct resource *res; u32 id = dev->id; - int irq = 0; + int irq; int i; dev_dbg(&dev->dev, "ace_probe(%p)\n", dev); @@ -1190,12 +1190,15 @@ static int ace_probe(struct platform_device *dev) if (of_find_property(dev->dev.of_node, "8-bit", NULL)) bus_width = ACE_BUS_WIDTH_8; - for (i = 0; i < dev->num_resources; i++) { - if (dev->resource[i].flags & IORESOURCE_MEM) - physaddr = dev->resource[i].start; - if (dev->resource[i].flags & IORESOURCE_IRQ) - irq = dev->resource[i].start; - } + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + physaddr = res->start; + if (!physaddr) + return -ENODEV; + + irq = platform_get_irq_optional(dev, 0); /* Call the bus-independent setup code */ return ace_alloc(&dev->dev, id, physaddr, irq, bus_width); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 09346ae308eb..78cc64b42b30 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -47,7 +47,7 @@ enum { struct intel_tlv { u8 type; u8 len; - u8 val[0]; + u8 val[]; } __packed; struct intel_version_tlv { diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 2c7171e0b001..85de313ddec2 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -71,6 +71,7 @@ config CPU_FREQ_DEFAULT_GOV_USERSPACE config CPU_FREQ_DEFAULT_GOV_ONDEMAND bool "ondemand" + depends on !(X86_INTEL_PSTATE && SMP) select CPU_FREQ_GOV_ONDEMAND select CPU_FREQ_GOV_PERFORMANCE help @@ -83,6 +84,7 @@ config CPU_FREQ_DEFAULT_GOV_ONDEMAND config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE bool "conservative" + depends on !(X86_INTEL_PSTATE && SMP) select CPU_FREQ_GOV_CONSERVATIVE select CPU_FREQ_GOV_PERFORMANCE help diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f4b60663efe6..336b5e94cbc8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1908,6 +1908,18 @@ void cpufreq_resume(void) } /** + * cpufreq_driver_test_flags - Test cpufreq driver's flags against given ones. + * @flags: Flags to test against the current cpufreq driver's flags. + * + * Assumes that the driver is there, so callers must ensure that this is the + * case. + */ +bool cpufreq_driver_test_flags(u16 flags) +{ + return !!(cpufreq_driver->flags & flags); +} + +/** * cpufreq_get_current_driver - return current driver's name * * Return the name string of the currently loaded cpufreq driver @@ -2187,7 +2199,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, * exactly same freq is called again and so we can save on few function * calls. */ - if (target_freq == policy->cur) + if (target_freq == policy->cur && + !(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS)) return 0; /* Save last value to restore later on errors */ diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 776a58bab0ff..ab93bce8ae77 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -223,7 +223,6 @@ static int eps_cpu_init(struct cpufreq_policy *policy) case EPS_BRAND_C3: pr_cont("C3\n"); return -ENODEV; - break; } /* Enable Enhanced PowerSaver */ rdmsrl(MSR_IA32_MISC_ENABLE, val); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 3c1455518738..b7a9779250aa 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2568,14 +2568,12 @@ static int intel_cpufreq_update_pstate(struct cpudata *cpu, int target_pstate, int old_pstate = cpu->pstate.current_pstate; target_pstate = intel_pstate_prepare_request(cpu, target_pstate); - if (target_pstate != old_pstate) { + if (hwp_active) { + intel_cpufreq_adjust_hwp(cpu, target_pstate, fast_switch); + cpu->pstate.current_pstate = target_pstate; + } else if (target_pstate != old_pstate) { + intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, fast_switch); cpu->pstate.current_pstate = target_pstate; - if (hwp_active) - intel_cpufreq_adjust_hwp(cpu, target_pstate, - fast_switch); - else - intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, - fast_switch); } intel_cpufreq_trace(cpu, fast_switch ? INTEL_PSTATE_TRACE_FAST_SWITCH : @@ -3032,6 +3030,7 @@ static int __init intel_pstate_init(void) hwp_mode_bdw = id->driver_data; intel_pstate.attr = hwp_cpufreq_attrs; intel_cpufreq.attr = hwp_cpufreq_attrs; + intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; if (!default_driver) default_driver = &intel_pstate; diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 123fb006810d..182a4dbca095 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -593,7 +593,6 @@ static void longhaul_setup_voltagescaling(void) break; default: return; - break; } if (min_vid_speed >= highest_speed) return; diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index a13a2d1e444e..0b66df4ed513 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -240,7 +240,7 @@ unsigned int speedstep_get_frequency(enum speedstep_processor processor) return pentium3_get_frequency(processor); default: return 0; - }; + } return 0; } EXPORT_SYMBOL_GPL(speedstep_get_frequency); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 518a1437862a..90284ffda58a 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -318,24 +318,6 @@ config INTEL_IOP_ADMA help Enable support for the Intel(R) IOP Series RAID engines. -config INTEL_MIC_X100_DMA - tristate "Intel MIC X100 DMA Driver" - depends on 64BIT && X86 && INTEL_MIC_BUS - select DMA_ENGINE - help - This enables DMA support for the Intel Many Integrated Core - (MIC) family of PCIe form factor coprocessor X100 devices that - run a 64 bit Linux OS. This driver will be used by both MIC - host and card drivers. - - If you are building host kernel with a MIC device or a card - kernel for a MIC device, then say M (recommended) or Y, else - say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - config K3_DMA tristate "Hisilicon K3 DMA support" depends on ARCH_HI3xxx || ARCH_HISI || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index e60f81331d4c..948a8da05f8b 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IDXD) += idxd/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o -obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o diff --git a/drivers/dma/mic_x100_dma.c b/drivers/dma/mic_x100_dma.c deleted file mode 100644 index fea8608a7810..000000000000 --- a/drivers/dma/mic_x100_dma.c +++ /dev/null @@ -1,770 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC X100 DMA Driver. - * - * Adapted from IOAT dma driver. - */ -#include <linux/module.h> -#include <linux/io.h> -#include <linux/seq_file.h> -#include <linux/vmalloc.h> - -#include "mic_x100_dma.h" - -#define MIC_DMA_MAX_XFER_SIZE_CARD (1 * 1024 * 1024 -\ - MIC_DMA_ALIGN_BYTES) -#define MIC_DMA_MAX_XFER_SIZE_HOST (1 * 1024 * 1024 >> 1) -#define MIC_DMA_DESC_TYPE_SHIFT 60 -#define MIC_DMA_MEMCPY_LEN_SHIFT 46 -#define MIC_DMA_STAT_INTR_SHIFT 59 - -/* high-water mark for pushing dma descriptors */ -static int mic_dma_pending_level = 4; - -/* Status descriptor is used to write a 64 bit value to a memory location */ -enum mic_dma_desc_format_type { - MIC_DMA_MEMCPY = 1, - MIC_DMA_STATUS, -}; - -static inline u32 mic_dma_hw_ring_inc(u32 val) -{ - return (val + 1) % MIC_DMA_DESC_RX_SIZE; -} - -static inline u32 mic_dma_hw_ring_dec(u32 val) -{ - return val ? val - 1 : MIC_DMA_DESC_RX_SIZE - 1; -} - -static inline void mic_dma_hw_ring_inc_head(struct mic_dma_chan *ch) -{ - ch->head = mic_dma_hw_ring_inc(ch->head); -} - -/* Prepare a memcpy desc */ -static inline void mic_dma_memcpy_desc(struct mic_dma_desc *desc, - dma_addr_t src_phys, dma_addr_t dst_phys, u64 size) -{ - u64 qw0, qw1; - - qw0 = src_phys; - qw0 |= (size >> MIC_DMA_ALIGN_SHIFT) << MIC_DMA_MEMCPY_LEN_SHIFT; - qw1 = MIC_DMA_MEMCPY; - qw1 <<= MIC_DMA_DESC_TYPE_SHIFT; - qw1 |= dst_phys; - desc->qw0 = qw0; - desc->qw1 = qw1; -} - -/* Prepare a status desc. with @data to be written at @dst_phys */ -static inline void mic_dma_prep_status_desc(struct mic_dma_desc *desc, u64 data, - dma_addr_t dst_phys, bool generate_intr) -{ - u64 qw0, qw1; - - qw0 = data; - qw1 = (u64) MIC_DMA_STATUS << MIC_DMA_DESC_TYPE_SHIFT | dst_phys; - if (generate_intr) - qw1 |= (1ULL << MIC_DMA_STAT_INTR_SHIFT); - desc->qw0 = qw0; - desc->qw1 = qw1; -} - -static void mic_dma_cleanup(struct mic_dma_chan *ch) -{ - struct dma_async_tx_descriptor *tx; - u32 tail; - u32 last_tail; - - spin_lock(&ch->cleanup_lock); - tail = mic_dma_read_cmp_cnt(ch); - /* - * This is the barrier pair for smp_wmb() in fn. - * mic_dma_tx_submit_unlock. It's required so that we read the - * updated cookie value from tx->cookie. - */ - smp_rmb(); - for (last_tail = ch->last_tail; tail != last_tail;) { - tx = &ch->tx_array[last_tail]; - if (tx->cookie) { - dma_cookie_complete(tx); - dmaengine_desc_get_callback_invoke(tx, NULL); - tx->callback = NULL; - } - last_tail = mic_dma_hw_ring_inc(last_tail); - } - /* finish all completion callbacks before incrementing tail */ - smp_mb(); - ch->last_tail = last_tail; - spin_unlock(&ch->cleanup_lock); -} - -static u32 mic_dma_ring_count(u32 head, u32 tail) -{ - u32 count; - - if (head >= tail) - count = (tail - 0) + (MIC_DMA_DESC_RX_SIZE - head); - else - count = tail - head; - return count - 1; -} - -/* Returns the num. of free descriptors on success, -ENOMEM on failure */ -static int mic_dma_avail_desc_ring_space(struct mic_dma_chan *ch, int required) -{ - struct device *dev = mic_dma_ch_to_device(ch); - u32 count; - - count = mic_dma_ring_count(ch->head, ch->last_tail); - if (count < required) { - mic_dma_cleanup(ch); - count = mic_dma_ring_count(ch->head, ch->last_tail); - } - - if (count < required) { - dev_dbg(dev, "Not enough desc space"); - dev_dbg(dev, "%s %d required=%u, avail=%u\n", - __func__, __LINE__, required, count); - return -ENOMEM; - } else { - return count; - } -} - -/* Program memcpy descriptors into the descriptor ring and update s/w head ptr*/ -static int mic_dma_prog_memcpy_desc(struct mic_dma_chan *ch, dma_addr_t src, - dma_addr_t dst, size_t len) -{ - size_t current_transfer_len; - size_t max_xfer_size = to_mic_dma_dev(ch)->max_xfer_size; - /* 3 is added to make sure we have enough space for status desc */ - int num_desc = len / max_xfer_size + 3; - int ret; - - if (len % max_xfer_size) - num_desc++; - - ret = mic_dma_avail_desc_ring_space(ch, num_desc); - if (ret < 0) - return ret; - do { - current_transfer_len = min(len, max_xfer_size); - mic_dma_memcpy_desc(&ch->desc_ring[ch->head], - src, dst, current_transfer_len); - mic_dma_hw_ring_inc_head(ch); - len -= current_transfer_len; - dst = dst + current_transfer_len; - src = src + current_transfer_len; - } while (len > 0); - return 0; -} - -/* It's a h/w quirk and h/w needs 2 status descriptors for every status desc */ -static void mic_dma_prog_intr(struct mic_dma_chan *ch) -{ - mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, - ch->status_dest_micpa, false); - mic_dma_hw_ring_inc_head(ch); - mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, - ch->status_dest_micpa, true); - mic_dma_hw_ring_inc_head(ch); -} - -/* Wrapper function to program memcpy descriptors/status descriptors */ -static int mic_dma_do_dma(struct mic_dma_chan *ch, int flags, dma_addr_t src, - dma_addr_t dst, size_t len) -{ - if (len && -ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len)) { - return -ENOMEM; - } else { - /* 3 is the maximum number of status descriptors */ - int ret = mic_dma_avail_desc_ring_space(ch, 3); - - if (ret < 0) - return ret; - } - - /* Above mic_dma_prog_memcpy_desc() makes sure we have enough space */ - if (flags & DMA_PREP_FENCE) { - mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, - ch->status_dest_micpa, false); - mic_dma_hw_ring_inc_head(ch); - } - - if (flags & DMA_PREP_INTERRUPT) - mic_dma_prog_intr(ch); - - return 0; -} - -static inline void mic_dma_issue_pending(struct dma_chan *ch) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - - spin_lock(&mic_ch->issue_lock); - /* - * Write to head triggers h/w to act on the descriptors. - * On MIC, writing the same head value twice causes - * a h/w error. On second write, h/w assumes we filled - * the entire ring & overwrote some of the descriptors. - */ - if (mic_ch->issued == mic_ch->submitted) - goto out; - mic_ch->issued = mic_ch->submitted; - /* - * make descriptor updates visible before advancing head, - * this is purposefully not smp_wmb() since we are also - * publishing the descriptor updates to a dma device - */ - wmb(); - mic_dma_write_reg(mic_ch, MIC_DMA_REG_DHPR, mic_ch->issued); -out: - spin_unlock(&mic_ch->issue_lock); -} - -static inline void mic_dma_update_pending(struct mic_dma_chan *ch) -{ - if (mic_dma_ring_count(ch->issued, ch->submitted) - > mic_dma_pending_level) - mic_dma_issue_pending(&ch->api_ch); -} - -static dma_cookie_t mic_dma_tx_submit_unlock(struct dma_async_tx_descriptor *tx) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(tx->chan); - dma_cookie_t cookie; - - dma_cookie_assign(tx); - cookie = tx->cookie; - /* - * We need an smp write barrier here because another CPU might see - * an update to submitted and update h/w head even before we - * assigned a cookie to this tx. - */ - smp_wmb(); - mic_ch->submitted = mic_ch->head; - spin_unlock(&mic_ch->prep_lock); - mic_dma_update_pending(mic_ch); - return cookie; -} - -static inline struct dma_async_tx_descriptor * -allocate_tx(struct mic_dma_chan *ch) -{ - u32 idx = mic_dma_hw_ring_dec(ch->head); - struct dma_async_tx_descriptor *tx = &ch->tx_array[idx]; - - dma_async_tx_descriptor_init(tx, &ch->api_ch); - tx->tx_submit = mic_dma_tx_submit_unlock; - return tx; -} - -/* Program a status descriptor with dst as address and value to be written */ -static struct dma_async_tx_descriptor * -mic_dma_prep_status_lock(struct dma_chan *ch, dma_addr_t dst, u64 src_val, - unsigned long flags) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - int result; - - spin_lock(&mic_ch->prep_lock); - result = mic_dma_avail_desc_ring_space(mic_ch, 4); - if (result < 0) - goto error; - mic_dma_prep_status_desc(&mic_ch->desc_ring[mic_ch->head], src_val, dst, - false); - mic_dma_hw_ring_inc_head(mic_ch); - result = mic_dma_do_dma(mic_ch, flags, 0, 0, 0); - if (result < 0) - goto error; - - return allocate_tx(mic_ch); -error: - dev_err(mic_dma_ch_to_device(mic_ch), - "Error enqueueing dma status descriptor, error=%d\n", result); - spin_unlock(&mic_ch->prep_lock); - return NULL; -} - -/* - * Prepare a memcpy descriptor to be added to the ring. - * Note that the temporary descriptor adds an extra overhead of copying the - * descriptor to ring. So, we copy directly to the descriptor ring - */ -static struct dma_async_tx_descriptor * -mic_dma_prep_memcpy_lock(struct dma_chan *ch, dma_addr_t dma_dest, - dma_addr_t dma_src, size_t len, unsigned long flags) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - struct device *dev = mic_dma_ch_to_device(mic_ch); - int result; - - if (!len && !flags) - return NULL; - - spin_lock(&mic_ch->prep_lock); - result = mic_dma_do_dma(mic_ch, flags, dma_src, dma_dest, len); - if (result >= 0) - return allocate_tx(mic_ch); - dev_err(dev, "Error enqueueing dma, error=%d\n", result); - spin_unlock(&mic_ch->prep_lock); - return NULL; -} - -static struct dma_async_tx_descriptor * -mic_dma_prep_interrupt_lock(struct dma_chan *ch, unsigned long flags) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - int ret; - - spin_lock(&mic_ch->prep_lock); - ret = mic_dma_do_dma(mic_ch, flags, 0, 0, 0); - if (!ret) - return allocate_tx(mic_ch); - spin_unlock(&mic_ch->prep_lock); - return NULL; -} - -/* Return the status of the transaction */ -static enum dma_status -mic_dma_tx_status(struct dma_chan *ch, dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - - if (DMA_COMPLETE != dma_cookie_status(ch, cookie, txstate)) - mic_dma_cleanup(mic_ch); - - return dma_cookie_status(ch, cookie, txstate); -} - -static irqreturn_t mic_dma_thread_fn(int irq, void *data) -{ - mic_dma_cleanup((struct mic_dma_chan *)data); - return IRQ_HANDLED; -} - -static irqreturn_t mic_dma_intr_handler(int irq, void *data) -{ - struct mic_dma_chan *ch = ((struct mic_dma_chan *)data); - - mic_dma_ack_interrupt(ch); - return IRQ_WAKE_THREAD; -} - -static int mic_dma_alloc_desc_ring(struct mic_dma_chan *ch) -{ - u64 desc_ring_size = MIC_DMA_DESC_RX_SIZE * sizeof(*ch->desc_ring); - struct device *dev = &to_mbus_device(ch)->dev; - - desc_ring_size = ALIGN(desc_ring_size, MIC_DMA_ALIGN_BYTES); - ch->desc_ring = kzalloc(desc_ring_size, GFP_KERNEL); - - if (!ch->desc_ring) - return -ENOMEM; - - ch->desc_ring_micpa = dma_map_single(dev, ch->desc_ring, - desc_ring_size, DMA_BIDIRECTIONAL); - if (dma_mapping_error(dev, ch->desc_ring_micpa)) - goto map_error; - - ch->tx_array = vzalloc(array_size(MIC_DMA_DESC_RX_SIZE, - sizeof(*ch->tx_array))); - if (!ch->tx_array) - goto tx_error; - return 0; -tx_error: - dma_unmap_single(dev, ch->desc_ring_micpa, desc_ring_size, - DMA_BIDIRECTIONAL); -map_error: - kfree(ch->desc_ring); - return -ENOMEM; -} - -static void mic_dma_free_desc_ring(struct mic_dma_chan *ch) -{ - u64 desc_ring_size = MIC_DMA_DESC_RX_SIZE * sizeof(*ch->desc_ring); - - vfree(ch->tx_array); - desc_ring_size = ALIGN(desc_ring_size, MIC_DMA_ALIGN_BYTES); - dma_unmap_single(&to_mbus_device(ch)->dev, ch->desc_ring_micpa, - desc_ring_size, DMA_BIDIRECTIONAL); - kfree(ch->desc_ring); - ch->desc_ring = NULL; -} - -static void mic_dma_free_status_dest(struct mic_dma_chan *ch) -{ - dma_unmap_single(&to_mbus_device(ch)->dev, ch->status_dest_micpa, - L1_CACHE_BYTES, DMA_BIDIRECTIONAL); - kfree(ch->status_dest); -} - -static int mic_dma_alloc_status_dest(struct mic_dma_chan *ch) -{ - struct device *dev = &to_mbus_device(ch)->dev; - - ch->status_dest = kzalloc(L1_CACHE_BYTES, GFP_KERNEL); - if (!ch->status_dest) - return -ENOMEM; - ch->status_dest_micpa = dma_map_single(dev, ch->status_dest, - L1_CACHE_BYTES, DMA_BIDIRECTIONAL); - if (dma_mapping_error(dev, ch->status_dest_micpa)) { - kfree(ch->status_dest); - ch->status_dest = NULL; - return -ENOMEM; - } - return 0; -} - -static int mic_dma_check_chan(struct mic_dma_chan *ch) -{ - if (mic_dma_read_reg(ch, MIC_DMA_REG_DCHERR) || - mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) & MIC_DMA_CHAN_QUIESCE) { - mic_dma_disable_chan(ch); - mic_dma_chan_mask_intr(ch); - dev_err(mic_dma_ch_to_device(ch), - "%s %d error setting up mic dma chan %d\n", - __func__, __LINE__, ch->ch_num); - return -EBUSY; - } - return 0; -} - -static int mic_dma_chan_setup(struct mic_dma_chan *ch) -{ - if (MIC_DMA_CHAN_MIC == ch->owner) - mic_dma_chan_set_owner(ch); - mic_dma_disable_chan(ch); - mic_dma_chan_mask_intr(ch); - mic_dma_write_reg(ch, MIC_DMA_REG_DCHERRMSK, 0); - mic_dma_chan_set_desc_ring(ch); - ch->last_tail = mic_dma_read_reg(ch, MIC_DMA_REG_DTPR); - ch->head = ch->last_tail; - ch->issued = 0; - mic_dma_chan_unmask_intr(ch); - mic_dma_enable_chan(ch); - return mic_dma_check_chan(ch); -} - -static void mic_dma_chan_destroy(struct mic_dma_chan *ch) -{ - mic_dma_disable_chan(ch); - mic_dma_chan_mask_intr(ch); -} - -static int mic_dma_setup_irq(struct mic_dma_chan *ch) -{ - ch->cookie = - to_mbus_hw_ops(ch)->request_threaded_irq(to_mbus_device(ch), - mic_dma_intr_handler, mic_dma_thread_fn, - "mic dma_channel", ch, ch->ch_num); - return PTR_ERR_OR_ZERO(ch->cookie); -} - -static inline void mic_dma_free_irq(struct mic_dma_chan *ch) -{ - to_mbus_hw_ops(ch)->free_irq(to_mbus_device(ch), ch->cookie, ch); -} - -static int mic_dma_chan_init(struct mic_dma_chan *ch) -{ - int ret = mic_dma_alloc_desc_ring(ch); - - if (ret) - goto ring_error; - ret = mic_dma_alloc_status_dest(ch); - if (ret) - goto status_error; - ret = mic_dma_chan_setup(ch); - if (ret) - goto chan_error; - return ret; -chan_error: - mic_dma_free_status_dest(ch); -status_error: - mic_dma_free_desc_ring(ch); -ring_error: - return ret; -} - -static int mic_dma_drain_chan(struct mic_dma_chan *ch) -{ - struct dma_async_tx_descriptor *tx; - int err = 0; - dma_cookie_t cookie; - - tx = mic_dma_prep_memcpy_lock(&ch->api_ch, 0, 0, 0, DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - goto error; - } - - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) - err = -ENOMEM; - else - err = dma_sync_wait(&ch->api_ch, cookie); - if (err) { - dev_err(mic_dma_ch_to_device(ch), "%s %d TO chan 0x%x\n", - __func__, __LINE__, ch->ch_num); - err = -EIO; - } -error: - mic_dma_cleanup(ch); - return err; -} - -static inline void mic_dma_chan_uninit(struct mic_dma_chan *ch) -{ - mic_dma_chan_destroy(ch); - mic_dma_cleanup(ch); - mic_dma_free_status_dest(ch); - mic_dma_free_desc_ring(ch); -} - -static int mic_dma_init(struct mic_dma_device *mic_dma_dev, - enum mic_dma_chan_owner owner) -{ - int i, first_chan = mic_dma_dev->start_ch; - struct mic_dma_chan *ch; - int ret; - - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - ch = &mic_dma_dev->mic_ch[i]; - ch->ch_num = i; - ch->owner = owner; - spin_lock_init(&ch->cleanup_lock); - spin_lock_init(&ch->prep_lock); - spin_lock_init(&ch->issue_lock); - ret = mic_dma_setup_irq(ch); - if (ret) - goto error; - } - return 0; -error: - for (i = i - 1; i >= first_chan; i--) - mic_dma_free_irq(ch); - return ret; -} - -static void mic_dma_uninit(struct mic_dma_device *mic_dma_dev) -{ - int i, first_chan = mic_dma_dev->start_ch; - struct mic_dma_chan *ch; - - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - ch = &mic_dma_dev->mic_ch[i]; - mic_dma_free_irq(ch); - } -} - -static int mic_dma_alloc_chan_resources(struct dma_chan *ch) -{ - int ret = mic_dma_chan_init(to_mic_dma_chan(ch)); - if (ret) - return ret; - return MIC_DMA_DESC_RX_SIZE; -} - -static void mic_dma_free_chan_resources(struct dma_chan *ch) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - mic_dma_drain_chan(mic_ch); - mic_dma_chan_uninit(mic_ch); -} - -/* Set the fn. handlers and register the dma device with dma api */ -static int mic_dma_register_dma_device(struct mic_dma_device *mic_dma_dev, - enum mic_dma_chan_owner owner) -{ - int i, first_chan = mic_dma_dev->start_ch; - - dma_cap_zero(mic_dma_dev->dma_dev.cap_mask); - /* - * This dma engine is not capable of host memory to host memory - * transfers - */ - dma_cap_set(DMA_MEMCPY, mic_dma_dev->dma_dev.cap_mask); - - if (MIC_DMA_CHAN_HOST == owner) - dma_cap_set(DMA_PRIVATE, mic_dma_dev->dma_dev.cap_mask); - mic_dma_dev->dma_dev.device_alloc_chan_resources = - mic_dma_alloc_chan_resources; - mic_dma_dev->dma_dev.device_free_chan_resources = - mic_dma_free_chan_resources; - mic_dma_dev->dma_dev.device_tx_status = mic_dma_tx_status; - mic_dma_dev->dma_dev.device_prep_dma_memcpy = mic_dma_prep_memcpy_lock; - mic_dma_dev->dma_dev.device_prep_dma_imm_data = - mic_dma_prep_status_lock; - mic_dma_dev->dma_dev.device_prep_dma_interrupt = - mic_dma_prep_interrupt_lock; - mic_dma_dev->dma_dev.device_issue_pending = mic_dma_issue_pending; - mic_dma_dev->dma_dev.copy_align = MIC_DMA_ALIGN_SHIFT; - INIT_LIST_HEAD(&mic_dma_dev->dma_dev.channels); - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - mic_dma_dev->mic_ch[i].api_ch.device = &mic_dma_dev->dma_dev; - dma_cookie_init(&mic_dma_dev->mic_ch[i].api_ch); - list_add_tail(&mic_dma_dev->mic_ch[i].api_ch.device_node, - &mic_dma_dev->dma_dev.channels); - } - return dmaenginem_async_device_register(&mic_dma_dev->dma_dev); -} - -/* - * Initializes dma channels and registers the dma device with the - * dma engine api. - */ -static struct mic_dma_device *mic_dma_dev_reg(struct mbus_device *mbdev, - enum mic_dma_chan_owner owner) -{ - struct mic_dma_device *mic_dma_dev; - int ret; - struct device *dev = &mbdev->dev; - - mic_dma_dev = devm_kzalloc(dev, sizeof(*mic_dma_dev), GFP_KERNEL); - if (!mic_dma_dev) { - ret = -ENOMEM; - goto alloc_error; - } - mic_dma_dev->mbdev = mbdev; - mic_dma_dev->dma_dev.dev = dev; - mic_dma_dev->mmio = mbdev->mmio_va; - if (MIC_DMA_CHAN_HOST == owner) { - mic_dma_dev->start_ch = 0; - mic_dma_dev->max_xfer_size = MIC_DMA_MAX_XFER_SIZE_HOST; - } else { - mic_dma_dev->start_ch = 4; - mic_dma_dev->max_xfer_size = MIC_DMA_MAX_XFER_SIZE_CARD; - } - ret = mic_dma_init(mic_dma_dev, owner); - if (ret) - goto init_error; - ret = mic_dma_register_dma_device(mic_dma_dev, owner); - if (ret) - goto reg_error; - return mic_dma_dev; -reg_error: - mic_dma_uninit(mic_dma_dev); -init_error: - mic_dma_dev = NULL; -alloc_error: - dev_err(dev, "Error at %s %d ret=%d\n", __func__, __LINE__, ret); - return mic_dma_dev; -} - -static void mic_dma_dev_unreg(struct mic_dma_device *mic_dma_dev) -{ - mic_dma_uninit(mic_dma_dev); -} - -/* DEBUGFS CODE */ -static int mic_dma_reg_show(struct seq_file *s, void *pos) -{ - struct mic_dma_device *mic_dma_dev = s->private; - int i, chan_num, first_chan = mic_dma_dev->start_ch; - struct mic_dma_chan *ch; - - seq_printf(s, "SBOX_DCR: %#x\n", - mic_dma_mmio_read(&mic_dma_dev->mic_ch[first_chan], - MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR)); - seq_puts(s, "DMA Channel Registers\n"); - seq_printf(s, "%-10s| %-10s %-10s %-10s %-10s %-10s", - "Channel", "DCAR", "DTPR", "DHPR", "DRAR_HI", "DRAR_LO"); - seq_printf(s, " %-11s %-14s %-10s\n", "DCHERR", "DCHERRMSK", "DSTAT"); - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - ch = &mic_dma_dev->mic_ch[i]; - chan_num = ch->ch_num; - seq_printf(s, "%-10i| %-#10x %-#10x %-#10x %-#10x", - chan_num, - mic_dma_read_reg(ch, MIC_DMA_REG_DCAR), - mic_dma_read_reg(ch, MIC_DMA_REG_DTPR), - mic_dma_read_reg(ch, MIC_DMA_REG_DHPR), - mic_dma_read_reg(ch, MIC_DMA_REG_DRAR_HI)); - seq_printf(s, " %-#10x %-#10x %-#14x %-#10x\n", - mic_dma_read_reg(ch, MIC_DMA_REG_DRAR_LO), - mic_dma_read_reg(ch, MIC_DMA_REG_DCHERR), - mic_dma_read_reg(ch, MIC_DMA_REG_DCHERRMSK), - mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT)); - } - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_dma_reg); - -/* Debugfs parent dir */ -static struct dentry *mic_dma_dbg; - -static int mic_dma_driver_probe(struct mbus_device *mbdev) -{ - struct mic_dma_device *mic_dma_dev; - enum mic_dma_chan_owner owner; - - if (MBUS_DEV_DMA_MIC == mbdev->id.device) - owner = MIC_DMA_CHAN_MIC; - else - owner = MIC_DMA_CHAN_HOST; - - mic_dma_dev = mic_dma_dev_reg(mbdev, owner); - dev_set_drvdata(&mbdev->dev, mic_dma_dev); - - if (mic_dma_dbg) { - mic_dma_dev->dbg_dir = debugfs_create_dir(dev_name(&mbdev->dev), - mic_dma_dbg); - debugfs_create_file("mic_dma_reg", 0444, mic_dma_dev->dbg_dir, - mic_dma_dev, &mic_dma_reg_fops); - } - return 0; -} - -static void mic_dma_driver_remove(struct mbus_device *mbdev) -{ - struct mic_dma_device *mic_dma_dev; - - mic_dma_dev = dev_get_drvdata(&mbdev->dev); - debugfs_remove_recursive(mic_dma_dev->dbg_dir); - mic_dma_dev_unreg(mic_dma_dev); -} - -static struct mbus_device_id id_table[] = { - {MBUS_DEV_DMA_MIC, MBUS_DEV_ANY_ID}, - {MBUS_DEV_DMA_HOST, MBUS_DEV_ANY_ID}, - {0}, -}; - -static struct mbus_driver mic_dma_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = mic_dma_driver_probe, - .remove = mic_dma_driver_remove, -}; - -static int __init mic_x100_dma_init(void) -{ - int rc = mbus_register_driver(&mic_dma_driver); - if (rc) - return rc; - mic_dma_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); - return 0; -} - -static void __exit mic_x100_dma_exit(void) -{ - debugfs_remove_recursive(mic_dma_dbg); - mbus_unregister_driver(&mic_dma_driver); -} - -module_init(mic_x100_dma_init); -module_exit(mic_x100_dma_exit); - -MODULE_DEVICE_TABLE(mbus, id_table); -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC X100 DMA Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/mic_x100_dma.h b/drivers/dma/mic_x100_dma.h deleted file mode 100644 index 68ef43a91714..000000000000 --- a/drivers/dma/mic_x100_dma.h +++ /dev/null @@ -1,275 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC X100 DMA Driver. - * - * Adapted from IOAT dma driver. - */ -#ifndef _MIC_X100_DMA_H_ -#define _MIC_X100_DMA_H_ - -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/debugfs.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/mic_bus.h> - -#include "dmaengine.h" - -/* - * MIC has a total of 8 dma channels. - * Four channels are assigned for host SW use & the remaining for MIC SW. - * MIC DMA transfer size & addresses need to be 64 byte aligned. - */ -#define MIC_DMA_MAX_NUM_CHAN 8 -#define MIC_DMA_NUM_CHAN 4 -#define MIC_DMA_ALIGN_SHIFT DMAENGINE_ALIGN_64_BYTES -#define MIC_DMA_ALIGN_BYTES (1 << MIC_DMA_ALIGN_SHIFT) -#define MIC_DMA_DESC_RX_SIZE (128 * 1024 - 4) - -/* - * Register descriptions - * All the registers are 32 bit registers. - * DCR is a global register and all others are per-channel. - * DCR - bits 0, 2, 4, 6, 8, 10, 12, 14 - enable bits for channels 0 to 7 - * bits 1, 3, 5, 7, 9, 11, 13, 15 - owner bits for channels 0 to 7 - * DCAR - bit 24 & 25 interrupt masks for mic owned & host owned channels - * DHPR - head of the descriptor ring updated by s/w - * DTPR - tail of the descriptor ring updated by h/w - * DRAR_LO - lower 32 bits of descriptor ring's mic address - * DRAR_HI - 3:0 - remaining 4 bits of descriptor ring's mic address - * 20:4 descriptor ring size - * 25:21 mic smpt entry number - * DSTAT - 16:0 h/w completion count; 31:28 dma engine status - * DCHERR - this register is non-zero on error - * DCHERRMSK - interrupt mask register - */ -#define MIC_DMA_HW_CMP_CNT_MASK 0x1ffff -#define MIC_DMA_CHAN_QUIESCE 0x20000000 -#define MIC_DMA_SBOX_BASE 0x00010000 -#define MIC_DMA_SBOX_DCR 0x0000A280 -#define MIC_DMA_SBOX_CH_BASE 0x0001A000 -#define MIC_DMA_SBOX_CHAN_OFF 0x40 -#define MIC_DMA_SBOX_DCAR_IM0 (0x1 << 24) -#define MIC_DMA_SBOX_DCAR_IM1 (0x1 << 25) -#define MIC_DMA_SBOX_DRARHI_SYS_MASK (0x1 << 26) -#define MIC_DMA_REG_DCAR 0 -#define MIC_DMA_REG_DHPR 4 -#define MIC_DMA_REG_DTPR 8 -#define MIC_DMA_REG_DRAR_LO 20 -#define MIC_DMA_REG_DRAR_HI 24 -#define MIC_DMA_REG_DSTAT 32 -#define MIC_DMA_REG_DCHERR 44 -#define MIC_DMA_REG_DCHERRMSK 48 - -/* HW dma desc */ -struct mic_dma_desc { - u64 qw0; - u64 qw1; -}; - -enum mic_dma_chan_owner { - MIC_DMA_CHAN_MIC = 0, - MIC_DMA_CHAN_HOST -}; - -/* - * mic_dma_chan - channel specific information - * @ch_num: channel number - * @owner: owner of this channel - * @last_tail: cached value of descriptor ring tail - * @head: index of next descriptor in desc_ring - * @issued: hardware notification point - * @submitted: index that will be used to submit descriptors to h/w - * @api_ch: dma engine api channel - * @desc_ring: dma descriptor ring - * @desc_ring_micpa: mic physical address of desc_ring - * @status_dest: destination for status (fence) descriptor - * @status_dest_micpa: mic address for status_dest, - * DMA controller uses this address - * @tx_array: array of async_tx - * @cleanup_lock: lock held when processing completed tx - * @prep_lock: lock held in prep_memcpy & released in tx_submit - * @issue_lock: lock used to synchronize writes to head - * @cookie: mic_irq cookie used with mic irq request - */ -struct mic_dma_chan { - int ch_num; - enum mic_dma_chan_owner owner; - u32 last_tail; - u32 head; - u32 issued; - u32 submitted; - struct dma_chan api_ch; - struct mic_dma_desc *desc_ring; - dma_addr_t desc_ring_micpa; - u64 *status_dest; - dma_addr_t status_dest_micpa; - struct dma_async_tx_descriptor *tx_array; - spinlock_t cleanup_lock; - spinlock_t prep_lock; - spinlock_t issue_lock; - struct mic_irq *cookie; -}; - -/* - * struct mic_dma_device - per mic device - * @mic_ch: dma channels - * @dma_dev: underlying dma device - * @mbdev: mic bus dma device - * @mmio: virtual address of the mmio space - * @dbg_dir: debugfs directory - * @start_ch: first channel number that can be used - * @max_xfer_size: maximum transfer size per dma descriptor - */ -struct mic_dma_device { - struct mic_dma_chan mic_ch[MIC_DMA_MAX_NUM_CHAN]; - struct dma_device dma_dev; - struct mbus_device *mbdev; - void __iomem *mmio; - struct dentry *dbg_dir; - int start_ch; - size_t max_xfer_size; -}; - -static inline struct mic_dma_chan *to_mic_dma_chan(struct dma_chan *ch) -{ - return container_of(ch, struct mic_dma_chan, api_ch); -} - -static inline struct mic_dma_device *to_mic_dma_dev(struct mic_dma_chan *ch) -{ - return - container_of((const typeof(((struct mic_dma_device *)0)->mic_ch)*) - (ch - ch->ch_num), struct mic_dma_device, mic_ch); -} - -static inline struct mbus_device *to_mbus_device(struct mic_dma_chan *ch) -{ - return to_mic_dma_dev(ch)->mbdev; -} - -static inline struct mbus_hw_ops *to_mbus_hw_ops(struct mic_dma_chan *ch) -{ - return to_mbus_device(ch)->hw_ops; -} - -static inline struct device *mic_dma_ch_to_device(struct mic_dma_chan *ch) -{ - return to_mic_dma_dev(ch)->dma_dev.dev; -} - -static inline void __iomem *mic_dma_chan_to_mmio(struct mic_dma_chan *ch) -{ - return to_mic_dma_dev(ch)->mmio; -} - -static inline u32 mic_dma_read_reg(struct mic_dma_chan *ch, u32 reg) -{ - return ioread32(mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE + - ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg); -} - -static inline void mic_dma_write_reg(struct mic_dma_chan *ch, u32 reg, u32 val) -{ - iowrite32(val, mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE + - ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg); -} - -static inline u32 mic_dma_mmio_read(struct mic_dma_chan *ch, u32 offset) -{ - return ioread32(mic_dma_chan_to_mmio(ch) + offset); -} - -static inline void mic_dma_mmio_write(struct mic_dma_chan *ch, u32 val, - u32 offset) -{ - iowrite32(val, mic_dma_chan_to_mmio(ch) + offset); -} - -static inline u32 mic_dma_read_cmp_cnt(struct mic_dma_chan *ch) -{ - return mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) & - MIC_DMA_HW_CMP_CNT_MASK; -} - -static inline void mic_dma_chan_set_owner(struct mic_dma_chan *ch) -{ - u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); - u32 chan_num = ch->ch_num; - - dcr = (dcr & ~(0x1 << (chan_num * 2))) | (ch->owner << (chan_num * 2)); - mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); -} - -static inline void mic_dma_enable_chan(struct mic_dma_chan *ch) -{ - u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); - - dcr |= 2 << (ch->ch_num << 1); - mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); -} - -static inline void mic_dma_disable_chan(struct mic_dma_chan *ch) -{ - u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); - - dcr &= ~(2 << (ch->ch_num << 1)); - mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); -} - -static void mic_dma_chan_set_desc_ring(struct mic_dma_chan *ch) -{ - u32 drar_hi; - dma_addr_t desc_ring_micpa = ch->desc_ring_micpa; - - drar_hi = (MIC_DMA_DESC_RX_SIZE & 0x1ffff) << 4; - if (MIC_DMA_CHAN_MIC == ch->owner) { - drar_hi |= (desc_ring_micpa >> 32) & 0xf; - } else { - drar_hi |= MIC_DMA_SBOX_DRARHI_SYS_MASK; - drar_hi |= ((desc_ring_micpa >> 34) - & 0x1f) << 21; - drar_hi |= (desc_ring_micpa >> 32) & 0x3; - } - mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_LO, (u32) desc_ring_micpa); - mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_HI, drar_hi); -} - -static inline void mic_dma_chan_mask_intr(struct mic_dma_chan *ch) -{ - u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR); - - if (MIC_DMA_CHAN_MIC == ch->owner) - dcar |= MIC_DMA_SBOX_DCAR_IM0; - else - dcar |= MIC_DMA_SBOX_DCAR_IM1; - mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar); -} - -static inline void mic_dma_chan_unmask_intr(struct mic_dma_chan *ch) -{ - u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR); - - if (MIC_DMA_CHAN_MIC == ch->owner) - dcar &= ~MIC_DMA_SBOX_DCAR_IM0; - else - dcar &= ~MIC_DMA_SBOX_DCAR_IM1; - mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar); -} - -static void mic_dma_ack_interrupt(struct mic_dma_chan *ch) -{ - if (MIC_DMA_CHAN_MIC == ch->owner) { - /* HW errata */ - mic_dma_chan_mask_intr(ch); - mic_dma_chan_unmask_intr(ch); - } - to_mbus_hw_ops(ch)->ack_interrupt(to_mbus_device(ch), ch->ch_num); -} -#endif diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index bc0d54f8e861..6a2ef63306d6 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ $(scmi-transport-y) obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 9853bd3c4d45..017e5d8bd869 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -197,6 +197,8 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle, protocols_imp[tot_num_ret + loop] = *(list + loop); tot_num_ret += loop_num_ret; + + scmi_reset_rx_to_maxsz(handle, t); } while (loop_num_ret); scmi_xfer_put(handle, t); diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index c1cfe3ee3d55..4645677d86f1 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -192,6 +192,8 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, } tot_rate_cnt += num_returned; + + scmi_reset_rx_to_maxsz(handle, t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 37fb583f1bf5..c0fb45e7c3e8 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -147,6 +147,8 @@ int scmi_do_xfer_with_response(const struct scmi_handle *h, struct scmi_xfer *xfer); int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id, size_t tx_size, size_t rx_size, struct scmi_xfer **p); +void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle, + struct scmi_xfer *xfer); int scmi_handle_put(const struct scmi_handle *handle); struct scmi_handle *scmi_handle_get(struct device *dev); void scmi_set_handle(struct scmi_device *scmi_dev); @@ -167,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf); DECLARE_SCMI_REGISTER_UNREGISTER(power); DECLARE_SCMI_REGISTER_UNREGISTER(reset); DECLARE_SCMI_REGISTER_UNREGISTER(sensors); +DECLARE_SCMI_REGISTER_UNREGISTER(voltage); DECLARE_SCMI_REGISTER_UNREGISTER(system); #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index c5dea87edf8f..5392e1fc6b4e 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -402,6 +402,14 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) return ret; } +void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle, + struct scmi_xfer *xfer) +{ + struct scmi_info *info = handle_to_scmi_info(handle); + + xfer->rx.len = info->desc->max_msg_size; +} + #define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC) /** @@ -735,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = { { SCMI_PROTOCOL_CLOCK, { "clocks" },}, { SCMI_PROTOCOL_SENSOR, { "hwmon" },}, { SCMI_PROTOCOL_RESET, { "reset" },}, + { SCMI_PROTOCOL_VOLTAGE, { "regulator" },}, }; static inline void @@ -938,6 +947,7 @@ static int __init scmi_driver_init(void) scmi_power_register(); scmi_reset_register(); scmi_sensors_register(); + scmi_voltage_register(); scmi_system_register(); return platform_driver_register(&scmi_driver); @@ -953,6 +963,7 @@ static void __exit scmi_driver_exit(void) scmi_power_unregister(); scmi_reset_unregister(); scmi_sensors_unregister(); + scmi_voltage_unregister(); scmi_system_unregister(); platform_driver_unregister(&scmi_driver); diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 2754f9d01636..ce336899d636 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1403,15 +1403,21 @@ static void scmi_protocols_late_init(struct work_struct *work) "finalized PENDING handler - key:%X\n", hndl->key); ret = scmi_event_handler_enable_events(hndl); + if (ret) { + dev_dbg(ni->handle->dev, + "purging INVALID handler - key:%X\n", + hndl->key); + scmi_put_active_handler(ni, hndl); + } } else { ret = scmi_valid_pending_handler(ni, hndl); - } - if (ret) { - dev_dbg(ni->handle->dev, - "purging PENDING handler - key:%X\n", - hndl->key); - /* this hndl can be only a pending one */ - scmi_put_handler_unlocked(ni, hndl); + if (ret) { + dev_dbg(ni->handle->dev, + "purging PENDING handler - key:%X\n", + hndl->key); + /* this hndl can be only a pending one */ + scmi_put_handler_unlocked(ni, hndl); + } } } mutex_unlock(&ni->pending_mtx); @@ -1468,7 +1474,7 @@ int scmi_notification_init(struct scmi_handle *handle) ni->gid = gid; ni->handle = handle; - ni->notify_wq = alloc_workqueue("scmi_notify", + ni->notify_wq = alloc_workqueue(dev_name(handle->dev), WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, 0); if (!ni->notify_wq) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index ed475b40bd08..82fb3babff72 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -304,6 +304,8 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain, } tot_opp_cnt += num_returned; + + scmi_reset_rx_to_maxsz(handle, t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index f063cfe17e02..a981a22cfe89 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -36,9 +36,7 @@ struct scmi_msg_reset_domain_reset { #define EXPLICIT_RESET_ASSERT BIT(1) #define ASYNCHRONOUS_RESET BIT(2) __le32 reset_state; -#define ARCH_RESET_TYPE BIT(31) -#define COLD_RESET_STATE BIT(0) -#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE) +#define ARCH_COLD_RESET 0 }; struct scmi_msg_reset_notify { diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 9703cf6356a0..b4232d611033 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -166,6 +166,8 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, } desc_index += num_returned; + + scmi_reset_rx_to_maxsz(handle, t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 1a03c3ec0230..82a82a5dc86a 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -149,6 +149,6 @@ static const struct scmi_transport_ops scmi_smc_ops = { const struct scmi_desc scmi_smc_desc = { .ops = &scmi_smc_ops, .max_rx_timeout_ms = 30, - .max_msg = 1, + .max_msg = 20, .max_msg_size = 128, }; diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c new file mode 100644 index 000000000000..e794e4349ae6 --- /dev/null +++ b/drivers/firmware/arm_scmi/voltage.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Voltage Protocol + * + * Copyright (C) 2020 ARM Ltd. + */ + +#include <linux/scmi_protocol.h> + +#include "common.h" + +#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0) +#define REMAINING_LEVELS_MASK GENMASK(31, 16) +#define RETURNED_LEVELS_MASK GENMASK(11, 0) + +enum scmi_voltage_protocol_cmd { + VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, + VOLTAGE_DESCRIBE_LEVELS = 0x4, + VOLTAGE_CONFIG_SET = 0x5, + VOLTAGE_CONFIG_GET = 0x6, + VOLTAGE_LEVEL_SET = 0x7, + VOLTAGE_LEVEL_GET = 0x8, +}; + +#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x)))) + +struct scmi_msg_resp_domain_attributes { + __le32 attr; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_cmd_describe_levels { + __le32 domain_id; + __le32 level_index; +}; + +struct scmi_msg_resp_describe_levels { + __le32 flags; +#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f)))) +#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f)))) +#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12)) + __le32 voltage[]; +}; + +struct scmi_msg_cmd_config_set { + __le32 domain_id; + __le32 config; +}; + +struct scmi_msg_cmd_level_set { + __le32 domain_id; + __le32 flags; + __le32 voltage_level; +}; + +struct voltage_info { + unsigned int version; + unsigned int num_domains; + struct scmi_voltage_info *domains; +}; + +static int scmi_protocol_attributes_get(const struct scmi_handle *handle, + struct voltage_info *vinfo) +{ + int ret; + struct scmi_xfer *t; + + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, + SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t); + if (ret) + return ret; + + ret = scmi_do_xfer(handle, t); + if (!ret) + vinfo->num_domains = + NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf)); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_init_voltage_levels(struct device *dev, + struct scmi_voltage_info *v, + u32 num_returned, u32 num_remaining, + bool segmented) +{ + u32 num_levels; + + num_levels = num_returned + num_remaining; + /* + * segmented levels entries are represented by a single triplet + * returned all in one go. + */ + if (!num_levels || + (segmented && (num_remaining || num_returned != 3))) { + dev_err(dev, + "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n", + num_levels, num_returned, num_remaining, v->id); + return -EINVAL; + } + + v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL); + if (!v->levels_uv) + return -ENOMEM; + + v->num_levels = num_levels; + v->segmented = segmented; + + return 0; +} + +static int scmi_voltage_descriptors_get(const struct scmi_handle *handle, + struct voltage_info *vinfo) +{ + int ret, dom; + struct scmi_xfer *td, *tl; + struct device *dev = handle->dev; + struct scmi_msg_resp_domain_attributes *resp_dom; + struct scmi_msg_resp_describe_levels *resp_levels; + + ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES, + SCMI_PROTOCOL_VOLTAGE, sizeof(__le32), + sizeof(*resp_dom), &td); + if (ret) + return ret; + resp_dom = td->rx.buf; + + ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS, + SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl); + if (ret) + goto outd; + resp_levels = tl->rx.buf; + + for (dom = 0; dom < vinfo->num_domains; dom++) { + u32 desc_index = 0; + u16 num_returned = 0, num_remaining = 0; + struct scmi_msg_cmd_describe_levels *cmd; + struct scmi_voltage_info *v; + + /* Retrieve domain attributes at first ... */ + put_unaligned_le32(dom, td->tx.buf); + ret = scmi_do_xfer(handle, td); + /* Skip domain on comms error */ + if (ret) + continue; + + v = vinfo->domains + dom; + v->id = dom; + v->attributes = le32_to_cpu(resp_dom->attr); + strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); + + cmd = tl->tx.buf; + /* ...then retrieve domain levels descriptions */ + do { + u32 flags; + int cnt; + + cmd->domain_id = cpu_to_le32(v->id); + cmd->level_index = desc_index; + ret = scmi_do_xfer(handle, tl); + if (ret) + break; + + flags = le32_to_cpu(resp_levels->flags); + num_returned = NUM_RETURNED_LEVELS(flags); + num_remaining = NUM_REMAINING_LEVELS(flags); + + /* Allocate space for num_levels if not already done */ + if (!v->num_levels) { + ret = scmi_init_voltage_levels(dev, v, + num_returned, + num_remaining, + SUPPORTS_SEGMENTED_LEVELS(flags)); + if (ret) + break; + } + + if (desc_index + num_returned > v->num_levels) { + dev_err(handle->dev, + "No. of voltage levels can't exceed %d\n", + v->num_levels); + ret = -EINVAL; + break; + } + + for (cnt = 0; cnt < num_returned; cnt++) { + s32 val; + + val = + (s32)le32_to_cpu(resp_levels->voltage[cnt]); + v->levels_uv[desc_index + cnt] = val; + if (val < 0) + v->negative_volts_allowed = true; + } + + desc_index += num_returned; + + scmi_reset_rx_to_maxsz(handle, tl); + /* check both to avoid infinite loop due to buggy fw */ + } while (num_returned && num_remaining); + + if (ret) { + v->num_levels = 0; + devm_kfree(dev, v->levels_uv); + } + + scmi_reset_rx_to_maxsz(handle, td); + } + + scmi_xfer_put(handle, tl); +outd: + scmi_xfer_put(handle, td); + + return ret; +} + +static int __scmi_voltage_get_u32(const struct scmi_handle *handle, + u8 cmd_id, u32 domain_id, u32 *value) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, cmd_id, + SCMI_PROTOCOL_VOLTAGE, + sizeof(__le32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(domain_id, t->tx.buf); + ret = scmi_do_xfer(handle, t); + if (!ret) + *value = get_unaligned_le32(t->rx.buf); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_config_set(const struct scmi_handle *handle, + u32 domain_id, u32 config) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + struct scmi_msg_cmd_config_set *cmd; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET, + SCMI_PROTOCOL_VOLTAGE, + sizeof(*cmd), 0, &t); + if (ret) + return ret; + + cmd = t->tx.buf; + cmd->domain_id = cpu_to_le32(domain_id); + cmd->config = cpu_to_le32(config & GENMASK(3, 0)); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_config_get(const struct scmi_handle *handle, + u32 domain_id, u32 *config) +{ + return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET, + domain_id, config); +} + +static int scmi_voltage_level_set(const struct scmi_handle *handle, + u32 domain_id, u32 flags, s32 volt_uV) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + struct scmi_msg_cmd_level_set *cmd; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET, + SCMI_PROTOCOL_VOLTAGE, + sizeof(*cmd), 0, &t); + if (ret) + return ret; + + cmd = t->tx.buf; + cmd->domain_id = cpu_to_le32(domain_id); + cmd->flags = cpu_to_le32(flags); + cmd->voltage_level = cpu_to_le32(volt_uV); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_level_get(const struct scmi_handle *handle, + u32 domain_id, s32 *volt_uV) +{ + return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET, + domain_id, (u32 *)volt_uV); +} + +static const struct scmi_voltage_info * __must_check +scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id) +{ + struct voltage_info *vinfo = handle->voltage_priv; + + if (domain_id >= vinfo->num_domains || + !vinfo->domains[domain_id].num_levels) + return NULL; + + return vinfo->domains + domain_id; +} + +static int scmi_voltage_domains_num_get(const struct scmi_handle *handle) +{ + struct voltage_info *vinfo = handle->voltage_priv; + + return vinfo->num_domains; +} + +static struct scmi_voltage_ops voltage_ops = { + .num_domains_get = scmi_voltage_domains_num_get, + .info_get = scmi_voltage_info_get, + .config_set = scmi_voltage_config_set, + .config_get = scmi_voltage_config_get, + .level_set = scmi_voltage_level_set, + .level_get = scmi_voltage_level_get, +}; + +static int scmi_voltage_protocol_init(struct scmi_handle *handle) +{ + int ret; + u32 version; + struct voltage_info *vinfo; + + ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version); + if (ret) + return ret; + + dev_dbg(handle->dev, "Voltage Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL); + if (!vinfo) + return -ENOMEM; + vinfo->version = version; + + ret = scmi_protocol_attributes_get(handle, vinfo); + if (ret) + return ret; + + if (vinfo->num_domains) { + vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains, + sizeof(*vinfo->domains), + GFP_KERNEL); + if (!vinfo->domains) + return -ENOMEM; + ret = scmi_voltage_descriptors_get(handle, vinfo); + if (ret) + return ret; + } else { + dev_warn(handle->dev, "No Voltage domains found.\n"); + } + + handle->voltage_ops = &voltage_ops; + handle->voltage_priv = vinfo; + + return 0; +} + +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index c241317edee7..42d9748921f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -1066,6 +1066,7 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x7319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, {0x1002, 0x731A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, {0x1002, 0x731B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, + {0x1002, 0x731E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, {0x1002, 0x731F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, /* Navi14 */ {0x1002, 0x7340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI14}, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index aa7f230c71bf..7e8265da9f25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -596,6 +596,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct ww_acquire_ctx ticket; struct list_head list, duplicates; uint64_t va_flags; + uint64_t vm_size; int r = 0; if (args->va_address < AMDGPU_VA_RESERVED_SIZE) { @@ -616,6 +617,15 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, args->va_address &= AMDGPU_GMC_HOLE_MASK; + vm_size = adev->vm_manager.max_pfn * AMDGPU_GPU_PAGE_SIZE; + vm_size -= AMDGPU_VA_RESERVED_SIZE; + if (args->va_address + args->map_size > vm_size) { + dev_dbg(&dev->pdev->dev, + "va_address 0x%llx is in top reserved area 0x%llx\n", + args->va_address + args->map_size, vm_size); + return -EINVAL; + } + if ((args->flags & ~valid_flags) && (args->flags & ~prt_flags)) { dev_dbg(&dev->pdev->dev, "invalid flags combination 0x%08X\n", args->flags); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index c6abb16e8018..58c83a7ad0fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -112,8 +112,8 @@ struct amdgpu_bo_list_entry; #define AMDGPU_MMHUB_0 1 #define AMDGPU_MMHUB_1 2 -/* hardcode that limit for now */ -#define AMDGPU_VA_RESERVED_SIZE (1ULL << 20) +/* Reserve 2MB at top/bottom of address space for kernel use */ +#define AMDGPU_VA_RESERVED_SIZE (2ULL << 20) /* max vmids dedicated for process */ #define AMDGPU_VM_MAX_RESERVED_VMID 1 diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c index 1ce741a0c6a7..03462c857498 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.c +++ b/drivers/gpu/drm/amd/amdgpu/nv.c @@ -455,6 +455,14 @@ void nv_set_virt_ops(struct amdgpu_device *adev) adev->virt.ops = &xgpu_nv_virt_ops; } +static bool nv_is_blockchain_sku(struct pci_dev *pdev) +{ + if (pdev->device == 0x731E && + (pdev->revision == 0xC6 || pdev->revision == 0xC7)) + return true; + return false; +} + int nv_set_ip_blocks(struct amdgpu_device *adev) { int r; @@ -483,7 +491,8 @@ int nv_set_ip_blocks(struct amdgpu_device *adev) if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_device_ip_block_add(adev, &dce_virtual_ip_block); #if defined(CONFIG_DRM_AMD_DC) - else if (amdgpu_device_has_dc_support(adev)) + else if (amdgpu_device_has_dc_support(adev) && + !nv_is_blockchain_sku(adev->pdev)) amdgpu_device_ip_block_add(adev, &dm_ip_block); #endif amdgpu_device_ip_block_add(adev, &gfx_v10_0_ip_block); @@ -491,7 +500,8 @@ int nv_set_ip_blocks(struct amdgpu_device *adev) if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT && !amdgpu_sriov_vf(adev)) amdgpu_device_ip_block_add(adev, &smu_v11_0_ip_block); - amdgpu_device_ip_block_add(adev, &vcn_v2_0_ip_block); + if (!nv_is_blockchain_sku(adev->pdev)) + amdgpu_device_ip_block_add(adev, &vcn_v2_0_ip_block); amdgpu_device_ip_block_add(adev, &jpeg_v2_0_ip_block); if (adev->enable_mes) amdgpu_device_ip_block_add(adev, &mes_v10_1_ip_block); diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig index f24abf428534..60dfdd432aba 100644 --- a/drivers/gpu/drm/amd/display/Kconfig +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -42,6 +42,7 @@ config DRM_AMD_DC_SI config DEBUG_KERNEL_DC bool "Enable kgdb break in DC" depends on DRM_AMD_DC + depends on KGDB help Choose this option if you want to hit kdgb_break in assert. diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 1eb29c362122..45ad05f6e03b 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1571,8 +1571,8 @@ static void init_state(struct dc *dc, struct dc_state *context) struct dc_state *dc_create_state(struct dc *dc) { - struct dc_state *context = kzalloc(sizeof(struct dc_state), - GFP_KERNEL); + struct dc_state *context = kvzalloc(sizeof(struct dc_state), + GFP_KERNEL); if (!context) return NULL; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 9cc65dc1970f..49ae5ff12da6 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -1149,7 +1149,8 @@ static uint32_t dcn3_get_pix_clk_dividers( static const struct clock_source_funcs dcn3_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dcn3_program_pix_clk, - .get_pix_clk_dividers = dcn3_get_pix_clk_dividers + .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz }; #endif /*****************************************/ diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c index 24fb39a11e5d..2455d210ccf6 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c @@ -2105,12 +2105,12 @@ static bool dcn30_internal_validate_bw( if (split[i]) { if (odm) { - if (split[i] == 4 && old_pipe->next_odm_pipe->next_odm_pipe) + if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; else if (old_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->pipe_idx; } else { - if (split[i] == 4 && old_pipe->bottom_pipe->bottom_pipe && + if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx; else if (old_pipe->bottom_pipe && @@ -2150,10 +2150,12 @@ static bool dcn30_internal_validate_bw( goto validate_fail; newly_split[pipe_4to1->pipe_idx] = true; - if (odm && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) + if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe + && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; - else if (!odm && old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && - old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) + else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx; else old_index = -1; diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c index 3be2c90b0c61..21583699f992 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c @@ -117,6 +117,12 @@ static const struct ddc_registers ddc_data_regs_dcn[] = { ddc_data_regs_dcn2(4), ddc_data_regs_dcn2(5), ddc_data_regs_dcn2(6), + { + DDC_GPIO_VGA_REG_LIST(DATA), + .ddc_setup = 0, + .phy_aux_cntl = 0, + .dc_gpio_aux_ctrl_5 = 0 + } }; static const struct ddc_registers ddc_clk_regs_dcn[] = { @@ -126,6 +132,12 @@ static const struct ddc_registers ddc_clk_regs_dcn[] = { ddc_clk_regs_dcn2(4), ddc_clk_regs_dcn2(5), ddc_clk_regs_dcn2(6), + { + DDC_GPIO_VGA_REG_LIST(CLK), + .ddc_setup = 0, + .phy_aux_cntl = 0, + .dc_gpio_aux_ctrl_5 = 0 + } }; static const struct ddc_sh_mask ddc_shift[] = { diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c b/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c index f67c18375bfd..dac427b68fd7 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c @@ -63,13 +63,13 @@ enum gpio_result dal_gpio_open_ex( enum gpio_mode mode) { if (gpio->pin) { - ASSERT_CRITICAL(false); + BREAK_TO_DEBUGGER(); return GPIO_RESULT_ALREADY_OPENED; } // No action if allocation failed during gpio construct if (!gpio->hw_container.ddc) { - ASSERT_CRITICAL(false); + BREAK_TO_DEBUGGER(); return GPIO_RESULT_NON_SPECIFIC_ERROR; } gpio->mode = mode; diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h index 330acaaed79a..95cb56929e79 100644 --- a/drivers/gpu/drm/amd/display/dc/os_types.h +++ b/drivers/gpu/drm/amd/display/dc/os_types.h @@ -94,36 +94,27 @@ * general debug capabilities * */ -#if defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB) -#define ASSERT_CRITICAL(expr) do { \ - if (WARN_ON(!(expr))) { \ - kgdb_breakpoint(); \ - } \ -} while (0) +#ifdef CONFIG_DEBUG_KERNEL_DC +#define dc_breakpoint() kgdb_breakpoint() #else -#define ASSERT_CRITICAL(expr) do { \ - if (WARN_ON(!(expr))) { \ - ; \ - } \ -} while (0) +#define dc_breakpoint() do {} while (0) #endif -#if defined(CONFIG_DEBUG_KERNEL_DC) -#define ASSERT(expr) ASSERT_CRITICAL(expr) +#define ASSERT_CRITICAL(expr) do { \ + if (WARN_ON(!(expr))) \ + dc_breakpoint(); \ + } while (0) -#else -#define ASSERT(expr) WARN_ON_ONCE(!(expr)) -#endif +#define ASSERT(expr) do { \ + if (WARN_ON_ONCE(!(expr))) \ + dc_breakpoint(); \ + } while (0) -#if defined(CONFIG_DEBUG_KERNEL_DC) && (defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB)) #define BREAK_TO_DEBUGGER() \ do { \ DRM_DEBUG_DRIVER("%s():%d\n", __func__, __LINE__); \ - kgdb_breakpoint(); \ + dc_breakpoint(); \ } while (0) -#else -#define BREAK_TO_DEBUGGER() DRM_DEBUG_DRIVER("%s():%d\n", __func__, __LINE__) -#endif #define DC_ERR(...) do { \ dm_error(__VA_ARGS__); \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 8d8081c6bd38..ef1a62e86a0e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -1361,14 +1361,9 @@ static int navi10_get_fan_speed_rpm(struct smu_context *smu, if (!speed) return -EINVAL; - switch (smu_v11_0_get_fan_control_mode(smu)) { - case AMD_FAN_CTRL_AUTO: - return navi10_get_smu_metrics_data(smu, - METRICS_CURR_FANSPEED, - speed); - default: - return smu_v11_0_get_fan_speed_rpm(smu, speed); - } + return navi10_get_smu_metrics_data(smu, + METRICS_CURR_FANSPEED, + speed); } static int navi10_get_fan_parameters(struct smu_context *smu) @@ -2534,29 +2529,6 @@ static const struct i2c_algorithm navi10_i2c_algo = { .functionality = navi10_i2c_func, }; -static int navi10_i2c_control_init(struct smu_context *smu, struct i2c_adapter *control) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - int res; - - control->owner = THIS_MODULE; - control->class = I2C_CLASS_SPD; - control->dev.parent = &adev->pdev->dev; - control->algo = &navi10_i2c_algo; - snprintf(control->name, sizeof(control->name), "AMDGPU SMU"); - - res = i2c_add_adapter(control); - if (res) - DRM_ERROR("Failed to register hw i2c, err: %d\n", res); - - return res; -} - -static void navi10_i2c_control_fini(struct smu_context *smu, struct i2c_adapter *control) -{ - i2c_del_adapter(control); -} - static ssize_t navi10_get_gpu_metrics(struct smu_context *smu, void **table) { @@ -2687,8 +2659,6 @@ static const struct pptable_funcs navi10_ppt_funcs = { .set_default_dpm_table = navi10_set_default_dpm_table, .dpm_set_vcn_enable = navi10_dpm_set_vcn_enable, .dpm_set_jpeg_enable = navi10_dpm_set_jpeg_enable, - .i2c_init = navi10_i2c_control_init, - .i2c_fini = navi10_i2c_control_fini, .print_clk_levels = navi10_print_clk_levels, .force_clk_levels = navi10_force_clk_levels, .populate_umd_state_clk = navi10_populate_umd_state_clk, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index 685a8a3b25d4..895d89bea7fa 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -1177,14 +1177,9 @@ static int sienna_cichlid_get_fan_speed_rpm(struct smu_context *smu, if (!speed) return -EINVAL; - switch (smu_v11_0_get_fan_control_mode(smu)) { - case AMD_FAN_CTRL_AUTO: - return sienna_cichlid_get_smu_metrics_data(smu, - METRICS_CURR_FANSPEED, - speed); - default: - return smu_v11_0_get_fan_speed_rpm(smu, speed); - } + return sienna_cichlid_get_smu_metrics_data(smu, + METRICS_CURR_FANSPEED, + speed); } static int sienna_cichlid_get_fan_parameters(struct smu_context *smu) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 90807a6b415c..deeed73f4ed6 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -374,6 +374,10 @@ static bool is_edid_digital_input_dp(const struct edid *edid) * drm_dp_downstream_is_type() - is the downstream facing port of certain type? * @dpcd: DisplayPort configuration data * @port_cap: port capabilities + * @type: port type to be checked. Can be: + * %DP_DS_PORT_TYPE_DP, %DP_DS_PORT_TYPE_VGA, %DP_DS_PORT_TYPE_DVI, + * %DP_DS_PORT_TYPE_HDMI, %DP_DS_PORT_TYPE_NON_EDID, + * %DP_DS_PORT_TYPE_DP_DUALMODE or %DP_DS_PORT_TYPE_WIRELESS. * * Caveat: Only works with DPCD 1.1+ port caps. * @@ -870,6 +874,7 @@ EXPORT_SYMBOL(drm_dp_downstream_444_to_420_conversion); /** * drm_dp_downstream_mode() - return a mode for downstream facing port + * @dev: DRM device * @dpcd: DisplayPort configuration data * @port_cap: port capabilities * @@ -1028,7 +1033,8 @@ EXPORT_SYMBOL(drm_dp_downstream_debug); /** * drm_dp_subconnector_type() - get DP branch device type - * + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities */ enum drm_mode_subconnector drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], @@ -1079,6 +1085,10 @@ EXPORT_SYMBOL(drm_dp_subconnector_type); /** * drm_mode_set_dp_subconnector_property - set subconnector for DP connector + * @connector: connector to set property on + * @status: connector status + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities * * Called by a driver on every detect event. */ diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a82f37d44258..631125b46e04 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3741,7 +3741,7 @@ drm_add_cmdb_modes(struct drm_connector *connector, u8 svd) /** * drm_display_mode_from_cea_vic() - return a mode for CEA VIC * @dev: DRM device - * @vic: CEA VIC of the mode + * @video_code: CEA VIC of the mode * * Creates a new mode matching the specified CEA VIC. * diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 19d73868490e..69c2c079d803 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1085,6 +1085,8 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, */ drm_gem_object_get(obj); + vma->vm_private_data = obj; + if (obj->funcs && obj->funcs->mmap) { ret = obj->funcs->mmap(obj, vma); if (ret) { @@ -1107,8 +1109,6 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); } - vma->vm_private_data = obj; - return 0; } EXPORT_SYMBOL(drm_gem_mmap_obj); diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index d77c9f8ff26c..e00616d94f26 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -593,8 +593,13 @@ int drm_gem_shmem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) /* Remove the fake offset */ vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node); - if (obj->import_attach) + if (obj->import_attach) { + /* Drop the reference drm_gem_mmap_obj() acquired.*/ + drm_gem_object_put(obj); + vma->vm_private_data = NULL; + return dma_buf_mmap(obj->dma_buf, vma, 0); + } shmem = to_drm_gem_shmem_obj(obj); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index d6808f678db5..9f955f2010c2 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -794,6 +794,7 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { /** * drm_prime_pages_to_sg - converts a page array into an sg list + * @dev: DRM device * @pages: pointer to the array of page pointers to convert * @nr_pages: length of the page vector * diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 829b2a40a482..31337d2a2cde 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -10636,6 +10636,10 @@ skl_get_initial_plane_config(struct intel_crtc *crtc, val & PLANE_CTL_FLIP_HORIZONTAL) plane_config->rotation |= DRM_MODE_REFLECT_X; + /* 90/270 degree rotation would require extra work */ + if (drm_rotation_90_or_270(plane_config->rotation)) + goto error; + base = intel_de_read(dev_priv, PLANE_SURF(pipe, plane_id)) & 0xfffff000; plane_config->base = base; diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 366ddfc8df6b..fb5e30de78c2 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -389,6 +389,7 @@ static const struct intel_device_info ilk_m_info = { GEN5_FEATURES, PLATFORM(INTEL_IRONLAKE), .is_mobile = 1, + .has_rps = true, .display.has_fbc = 1, }; diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c index 6b5e9d88646d..180e1078ef7c 100644 --- a/drivers/gpu/drm/i915/intel_memory_region.c +++ b/drivers/gpu/drm/i915/intel_memory_region.c @@ -87,7 +87,7 @@ __intel_memory_region_get_pages_buddy(struct intel_memory_region *mem, min_order = ilog2(size) - ilog2(mem->mm.chunk_size); } - if (size > BIT(mem->mm.max_order) * mem->mm.chunk_size) + if (size > mem->mm.size) return -E2BIG; n_pages = size >> ilog2(mem->mm.chunk_size); diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c index 334b0648e253..0aeba8e3af28 100644 --- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c +++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c @@ -261,6 +261,82 @@ err_close_objects: return err; } +static int igt_mock_splintered_region(void *arg) +{ + struct intel_memory_region *mem = arg; + struct drm_i915_private *i915 = mem->i915; + struct drm_i915_gem_object *obj; + unsigned int expected_order; + LIST_HEAD(objects); + u64 size; + int err = 0; + + /* + * Sanity check we can still allocate everything even if the + * mm.max_order != mm.size. i.e our starting address space size is not a + * power-of-two. + */ + + size = (SZ_4G - 1) & PAGE_MASK; + mem = mock_region_create(i915, 0, size, PAGE_SIZE, 0); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + if (mem->mm.size != size) { + pr_err("%s size mismatch(%llu != %llu)\n", + __func__, mem->mm.size, size); + err = -EINVAL; + goto out_put; + } + + expected_order = get_order(rounddown_pow_of_two(size)); + if (mem->mm.max_order != expected_order) { + pr_err("%s order mismatch(%u != %u)\n", + __func__, mem->mm.max_order, expected_order); + err = -EINVAL; + goto out_put; + } + + obj = igt_object_create(mem, &objects, size, 0); + if (IS_ERR(obj)) { + err = PTR_ERR(obj); + goto out_close; + } + + close_objects(mem, &objects); + + /* + * While we should be able allocate everything without any flag + * restrictions, if we consider I915_BO_ALLOC_CONTIGUOUS then we are + * actually limited to the largest power-of-two for the region size i.e + * max_order, due to the inner workings of the buddy allocator. So make + * sure that does indeed hold true. + */ + + obj = igt_object_create(mem, &objects, size, I915_BO_ALLOC_CONTIGUOUS); + if (!IS_ERR(obj)) { + pr_err("%s too large contiguous allocation was not rejected\n", + __func__); + err = -EINVAL; + goto out_close; + } + + obj = igt_object_create(mem, &objects, rounddown_pow_of_two(size), + I915_BO_ALLOC_CONTIGUOUS); + if (IS_ERR(obj)) { + pr_err("%s largest possible contiguous allocation failed\n", + __func__); + err = PTR_ERR(obj); + goto out_close; + } + +out_close: + close_objects(mem, &objects); +out_put: + intel_memory_region_put(mem); + return err; +} + static int igt_gpu_write_dw(struct intel_context *ce, struct i915_vma *vma, u32 dword, @@ -771,6 +847,7 @@ int intel_memory_region_mock_selftests(void) static const struct i915_subtest tests[] = { SUBTEST(igt_mock_fill), SUBTEST(igt_mock_contiguous), + SUBTEST(igt_mock_splintered_region), }; struct intel_memory_region *mem; struct drm_i915_private *i915; diff --git a/drivers/gpu/drm/i915/selftests/mock_region.c b/drivers/gpu/drm/i915/selftests/mock_region.c index 09660f5a0a4c..979d96f27c43 100644 --- a/drivers/gpu/drm/i915/selftests/mock_region.c +++ b/drivers/gpu/drm/i915/selftests/mock_region.c @@ -24,7 +24,7 @@ mock_object_create(struct intel_memory_region *mem, struct drm_i915_private *i915 = mem->i915; struct drm_i915_gem_object *obj; - if (size > BIT(mem->mm.max_order) * mem->mm.chunk_size) + if (size > mem->mm.size) return ERR_PTR(-E2BIG); obj = i915_gem_object_alloc(); diff --git a/drivers/gpu/drm/nouveau/dispnv50/core.h b/drivers/gpu/drm/nouveau/dispnv50/core.h index 498622c0c670..f75088186fba 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core.h +++ b/drivers/gpu/drm/nouveau/dispnv50/core.h @@ -44,6 +44,7 @@ int core507d_new_(const struct nv50_core_func *, struct nouveau_drm *, s32, struct nv50_core **); int core507d_init(struct nv50_core *); void core507d_ntfy_init(struct nouveau_bo *, u32); +int core507d_read_caps(struct nv50_disp *disp); int core507d_caps_init(struct nouveau_drm *, struct nv50_disp *); int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *); int core507d_update(struct nv50_core *, u32 *, bool); @@ -55,6 +56,7 @@ extern const struct nv50_outp_func pior507d; int core827d_new(struct nouveau_drm *, s32, struct nv50_core **); int core907d_new(struct nouveau_drm *, s32, struct nv50_core **); +int core907d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp); extern const struct nv50_outp_func dac907d; extern const struct nv50_outp_func sor907d; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core507d.c b/drivers/gpu/drm/nouveau/dispnv50/core507d.c index 248edf69e168..e6f16a7750f0 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core507d.c @@ -78,19 +78,56 @@ core507d_ntfy_init(struct nouveau_bo *bo, u32 offset) } int -core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +core507d_read_caps(struct nv50_disp *disp) { struct nvif_push *push = disp->core->chan.push; int ret; - if ((ret = PUSH_WAIT(push, 2))) + ret = PUSH_WAIT(push, 6); + if (ret) return ret; + PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL, + NVDEF(NV507D, SET_NOTIFIER_CONTROL, MODE, WRITE) | + NVVAL(NV507D, SET_NOTIFIER_CONTROL, OFFSET, NV50_DISP_CORE_NTFY >> 2) | + NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, ENABLE)); + PUSH_MTHD(push, NV507D, GET_CAPABILITIES, 0x00000000); + + PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL, + NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, DISABLE)); + return PUSH_KICK(push); } int +core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +{ + struct nv50_core *core = disp->core; + struct nouveau_bo *bo = disp->sync; + s64 time; + int ret; + + NVBO_WR32(bo, NV50_DISP_CORE_NTFY, NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, + NVDEF(NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, DONE, FALSE)); + + ret = core507d_read_caps(disp); + if (ret < 0) + return ret; + + time = nvif_msec(core->chan.base.device, 2000ULL, + if (NVBO_TD32(bo, NV50_DISP_CORE_NTFY, + NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, DONE, ==, TRUE)) + break; + usleep_range(1, 2); + ); + if (time < 0) + NV_ERROR(drm, "core caps notifier timeout\n"); + + return 0; +} + +int core507d_init(struct nv50_core *core) { struct nvif_push *push = core->chan.push; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core907d.c b/drivers/gpu/drm/nouveau/dispnv50/core907d.c index b17c03529c78..8564d4dffaff 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core907d.c @@ -22,11 +22,45 @@ #include "core.h" #include "head.h" +#include <nvif/push507c.h> +#include <nvif/timer.h> + +#include <nvhw/class/cl907d.h> + +#include "nouveau_bo.h" + +int +core907d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +{ + struct nv50_core *core = disp->core; + struct nouveau_bo *bo = disp->sync; + s64 time; + int ret; + + NVBO_WR32(bo, NV50_DISP_CORE_NTFY, NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, + NVDEF(NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, DONE, FALSE)); + + ret = core507d_read_caps(disp); + if (ret < 0) + return ret; + + time = nvif_msec(core->chan.base.device, 2000ULL, + if (NVBO_TD32(bo, NV50_DISP_CORE_NTFY, + NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, DONE, ==, TRUE)) + break; + usleep_range(1, 2); + ); + if (time < 0) + NV_ERROR(drm, "core caps notifier timeout\n"); + + return 0; +} + static const struct nv50_core_func core907d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, - .caps_init = core507d_caps_init, + .caps_init = core907d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head907d, diff --git a/drivers/gpu/drm/nouveau/dispnv50/core917d.c b/drivers/gpu/drm/nouveau/dispnv50/core917d.c index 66846f372080..1cd3a2a35dfb 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core917d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core917d.c @@ -26,7 +26,7 @@ static const struct nv50_core_func core917d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, - .caps_init = core507d_caps_init, + .caps_init = core907d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head917d, diff --git a/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h b/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h index 2e444bac701d..6a463f308b64 100644 --- a/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h +++ b/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h @@ -32,7 +32,10 @@ #define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_DONE_TRUE 0x00000001 #define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_R0 15:1 #define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_TIMESTAMP 29:16 - +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1 0x00000001 +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE 0:0 +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE_FALSE 0x00000000 +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE_TRUE 0x00000001 // class methods #define NV507D_UPDATE (0x00000080) diff --git a/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h b/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h index 34bc3eafac7d..79aff6ff3138 100644 --- a/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h +++ b/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h @@ -24,6 +24,10 @@ #ifndef _cl907d_h_ #define _cl907d_h_ +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4 0x00000004 +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE 0:0 +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE_FALSE 0x00000000 +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE_TRUE 0x00000001 #define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20 0x00000014 #define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18 0:0 #define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18_FALSE 0x00000000 diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 49dd0cbc332f..6f21f36719fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1023,29 +1023,6 @@ get_tmds_link_bandwidth(struct drm_connector *connector) return 112000 * duallink_scale; } -enum drm_mode_status -nouveau_conn_mode_clock_valid(const struct drm_display_mode *mode, - const unsigned min_clock, - const unsigned max_clock, - unsigned int *clock_out) -{ - unsigned int clock = mode->clock; - - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == - DRM_MODE_FLAG_3D_FRAME_PACKING) - clock *= 2; - - if (clock < min_clock) - return MODE_CLOCK_LOW; - if (clock > max_clock) - return MODE_CLOCK_HIGH; - - if (clock_out) - *clock_out = clock; - - return MODE_OK; -} - static enum drm_mode_status nouveau_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -1053,7 +1030,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); - unsigned min_clock = 25000, max_clock = min_clock; + unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock; switch (nv_encoder->dcb->type) { case DCB_OUTPUT_LVDS: @@ -1082,8 +1059,15 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } - return nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, - NULL); + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + + if (clock < min_clock) + return MODE_CLOCK_LOW; + if (clock > max_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; } static struct drm_encoder * diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7b640e05bd4c..040ed88d362d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -231,23 +231,30 @@ nv50_dp_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode, unsigned *out_clock) { - const unsigned min_clock = 25000; - unsigned max_clock, ds_clock, clock; - enum drm_mode_status ret; + const unsigned int min_clock = 25000; + unsigned int max_rate, mode_rate, ds_max_dotclock, clock = mode->clock; + const u8 bpp = connector->display_info.bpc * 3; if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace) return MODE_NO_INTERLACE; - max_clock = outp->dp.link_nr * outp->dp.link_bw; - ds_clock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, - outp->dp.downstream_ports); - if (ds_clock) - max_clock = min(max_clock, ds_clock); + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + + max_rate = outp->dp.link_nr * outp->dp.link_bw; + mode_rate = DIV_ROUND_UP(clock * bpp, 8); + if (mode_rate > max_rate) + return MODE_CLOCK_HIGH; + + ds_max_dotclock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, outp->dp.downstream_ports); + if (ds_max_dotclock && clock > ds_max_dotclock) + return MODE_CLOCK_HIGH; + + if (clock < min_clock) + return MODE_CLOCK_LOW; - clock = mode->clock * (connector->display_info.bpc * 3) / 10; - ret = nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, - &clock); if (out_clock) *out_clock = clock; - return ret; + + return MODE_OK; } diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 89adadf4706b..549bc67feabb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -190,7 +190,8 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain, * to the caller, instead of a normal nouveau_bo ttm reference. */ ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, size); if (ret) { - nouveau_bo_ref(NULL, &nvbo); + drm_gem_object_release(&nvbo->bo.base); + kfree(nvbo); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c index 2df1c0460559..4f69e4c3dafd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_svm.c +++ b/drivers/gpu/drm/nouveau/nouveau_svm.c @@ -105,11 +105,11 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, struct nouveau_cli *cli = nouveau_cli(file_priv); struct drm_nouveau_svm_bind *args = data; unsigned target, cmd, priority; - unsigned long addr, end, size; + unsigned long addr, end; struct mm_struct *mm; args->va_start &= PAGE_MASK; - args->va_end &= PAGE_MASK; + args->va_end = ALIGN(args->va_end, PAGE_SIZE); /* Sanity check arguments */ if (args->reserved0 || args->reserved1) @@ -118,8 +118,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, return -EINVAL; if (args->va_start >= args->va_end) return -EINVAL; - if (!args->npages) - return -EINVAL; cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT; cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK; @@ -151,12 +149,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, if (args->stride) return -EINVAL; - size = ((unsigned long)args->npages) << PAGE_SHIFT; - if ((args->va_start + size) <= args->va_start) - return -EINVAL; - if ((args->va_start + size) > args->va_end) - return -EINVAL; - /* * Ok we are ask to do something sane, for now we only support migrate * commands but we will add things like memory policy (what to do on @@ -171,7 +163,7 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, return -EINVAL; } - for (addr = args->va_start, end = args->va_start + size; addr < end;) { + for (addr = args->va_start, end = args->va_end; addr < end;) { struct vm_area_struct *vma; unsigned long next; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index dcb70677d0ac..7851bec5f0e5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2924,17 +2924,34 @@ nvkm_device_del(struct nvkm_device **pdevice) } } +/* returns true if the GPU is in the CPU native byte order */ static inline bool nvkm_device_endianness(struct nvkm_device *device) { - u32 boot1 = nvkm_rd32(device, 0x000004) & 0x01000001; #ifdef __BIG_ENDIAN - if (!boot1) - return false; + const bool big_endian = true; #else - if (boot1) - return false; + const bool big_endian = false; #endif + + /* Read NV_PMC_BOOT_1, and assume non-functional endian switch if it + * doesn't contain the expected values. + */ + u32 pmc_boot_1 = nvkm_rd32(device, 0x000004); + if (pmc_boot_1 && pmc_boot_1 != 0x01000001) + return !big_endian; /* Assume GPU is LE in this case. */ + + /* 0 means LE and 0x01000001 means BE GPU. Condition is true when + * GPU/CPU endianness don't match. + */ + if (big_endian == !pmc_boot_1) { + nvkm_wr32(device, 0x000004, 0x01000001); + nvkm_rd32(device, 0x000000); + if (nvkm_rd32(device, 0x000004) != (big_endian ? 0x01000001 : 0x00000000)) + return !big_endian; /* Assume GPU is LE on any unexpected read-back. */ + } + + /* CPU/GPU endianness should (hopefully) match. */ return true; } @@ -2987,14 +3004,10 @@ nvkm_device_ctor(const struct nvkm_device_func *func, if (detect) { /* switch mmio to cpu's native endianness */ if (!nvkm_device_endianness(device)) { - nvkm_wr32(device, 0x000004, 0x01000001); - nvkm_rd32(device, 0x000000); - if (!nvkm_device_endianness(device)) { - nvdev_error(device, - "GPU not supported on big-endian\n"); - ret = -ENOSYS; - goto done; - } + nvdev_error(device, + "Couldn't switch GPU to CPUs endianess\n"); + ret = -ENOSYS; + goto done; } boot0 = nvkm_rd32(device, 0x000000); diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 3482e28e30fc..0c5f22e95c2d 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -26,7 +26,9 @@ struct mantix { struct device *dev; struct drm_panel panel; + struct gpio_desc *reset_gpio; + struct gpio_desc *tp_rstn_gpio; struct regulator *avdd; struct regulator *avee; @@ -124,6 +126,10 @@ static int mantix_unprepare(struct drm_panel *panel) { struct mantix *ctx = panel_to_mantix(panel); + gpiod_set_value_cansleep(ctx->tp_rstn_gpio, 1); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->avee); regulator_disable(ctx->avdd); /* T11 */ @@ -165,13 +171,10 @@ static int mantix_prepare(struct drm_panel *panel) return ret; } - /* T3+T5 */ - usleep_range(10000, 12000); - - gpiod_set_value_cansleep(ctx->reset_gpio, 1); - usleep_range(5150, 7000); - + /* T3 + T4 + time for voltage to become stable: */ + usleep_range(6000, 7000); gpiod_set_value_cansleep(ctx->reset_gpio, 0); + gpiod_set_value_cansleep(ctx->tp_rstn_gpio, 0); /* T6 */ msleep(50); @@ -204,7 +207,7 @@ static int mantix_get_modes(struct drm_panel *panel, if (!mode) { dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n", default_mode.hdisplay, default_mode.vdisplay, - drm_mode_vrefresh(mode)); + drm_mode_vrefresh(&default_mode)); return -ENOMEM; } @@ -236,12 +239,18 @@ static int mantix_probe(struct mipi_dsi_device *dsi) if (!ctx) return -ENOMEM; - ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) { dev_err(dev, "cannot get reset gpio\n"); return PTR_ERR(ctx->reset_gpio); } + ctx->tp_rstn_gpio = devm_gpiod_get(dev, "mantix,tp-rstn", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->tp_rstn_gpio)) { + dev_err(dev, "cannot get tp-rstn gpio\n"); + return PTR_ERR(ctx->tp_rstn_gpio); + } + mipi_dsi_set_drvdata(dsi, ctx); ctx->dev = dev; diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c index b51cc685c13a..edb60ae0a9b7 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.c +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -407,6 +407,7 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, struct drm_framebuffer *fb = state->fb; const struct drm_format_info *format = fb->format; uint64_t modifier = fb->modifier; + unsigned int ch1_phase_idx; u32 out_fmt_val; u32 in_fmt_val, in_mod_val, in_ps_val; unsigned int i; @@ -442,18 +443,19 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, * I have no idea what this does exactly, but it seems to be * related to the scaler FIR filter phase parameters. */ + ch1_phase_idx = (format->num_planes > 1) ? 1 : 0; regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, - frontend->data->ch_phase[0].horzphase); + frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, - frontend->data->ch_phase[1].horzphase); + frontend->data->ch_phase[ch1_phase_idx]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, - frontend->data->ch_phase[0].vertphase[0]); + frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, - frontend->data->ch_phase[1].vertphase[0]); + frontend->data->ch_phase[ch1_phase_idx]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, - frontend->data->ch_phase[0].vertphase[1]); + frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, - frontend->data->ch_phase[1].vertphase[1]); + frontend->data->ch_phase[ch1_phase_idx]); /* * Checking the input format is sufficient since we currently only @@ -687,30 +689,12 @@ static const struct dev_pm_ops sun4i_frontend_pm_ops = { }; static const struct sun4i_frontend_data sun4i_a10_frontend = { - .ch_phase = { - { - .horzphase = 0, - .vertphase = { 0, 0 }, - }, - { - .horzphase = 0xfc000, - .vertphase = { 0xfc000, 0xfc000 }, - }, - }, + .ch_phase = { 0x000, 0xfc000 }, .has_coef_rdy = true, }; static const struct sun4i_frontend_data sun8i_a33_frontend = { - .ch_phase = { - { - .horzphase = 0x400, - .vertphase = { 0x400, 0x400 }, - }, - { - .horzphase = 0x400, - .vertphase = { 0x400, 0x400 }, - }, - }, + .ch_phase = { 0x400, 0xfc400 }, .has_coef_access_ctrl = true, }; diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.h b/drivers/gpu/drm/sun4i/sun4i_frontend.h index 0c382c1ddb0f..2e7b76e50c2b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.h +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.h @@ -115,11 +115,7 @@ struct reset_control; struct sun4i_frontend_data { bool has_coef_access_ctrl; bool has_coef_rdy; - - struct { - u32 horzphase; - u32 vertphase[2]; - } ch_phase[2]; + u32 ch_phase[2]; }; struct sun4i_frontend { diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 915f8bfdb58c..182c586525eb 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -568,7 +568,6 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, &bin->base, v3d_job_free, args->in_sync_bcl); if (ret) { - kfree(bin); v3d_job_put(&render->base); kfree(bin); return ret; diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index f1a5fd5dab6f..a17aa1db11b6 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -314,6 +314,7 @@ unbind_all: component_unbind_all(dev, drm); gem_destroy: vc4_gem_destroy(drm); + drm_mode_config_cleanup(drm); vc4_bo_cache_destroy(drm); dev_put: drm_dev_put(drm); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 90b911fd2a7f..66d4fb16db8f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -287,7 +287,7 @@ struct vc4_bo { static inline struct vc4_bo * to_vc4_bo(struct drm_gem_object *bo) { - return (struct vc4_bo *)bo; + return container_of(to_drm_gem_cma_obj(bo), struct vc4_bo, base); } struct vc4_fence { @@ -300,7 +300,7 @@ struct vc4_fence { static inline struct vc4_fence * to_vc4_fence(struct dma_fence *fence) { - return (struct vc4_fence *)fence; + return container_of(fence, struct vc4_fence, base); } struct vc4_seqno_cb { @@ -347,7 +347,7 @@ struct vc4_plane { static inline struct vc4_plane * to_vc4_plane(struct drm_plane *plane) { - return (struct vc4_plane *)plane; + return container_of(plane, struct vc4_plane, base); } enum vc4_scaling_mode { @@ -423,7 +423,7 @@ struct vc4_plane_state { static inline struct vc4_plane_state * to_vc4_plane_state(struct drm_plane_state *state) { - return (struct vc4_plane_state *)state; + return container_of(state, struct vc4_plane_state, base); } enum vc4_encoder_type { @@ -499,7 +499,7 @@ struct vc4_crtc { static inline struct vc4_crtc * to_vc4_crtc(struct drm_crtc *crtc) { - return (struct vc4_crtc *)crtc; + return container_of(crtc, struct vc4_crtc, base); } static inline const struct vc4_crtc_data * @@ -537,7 +537,7 @@ struct vc4_crtc_state { static inline struct vc4_crtc_state * to_vc4_crtc_state(struct drm_crtc_state *crtc_state) { - return (struct vc4_crtc_state *)crtc_state; + return container_of(crtc_state, struct vc4_crtc_state, base); } #define V3D_READ(offset) readl(vc4->v3d->regs + offset) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e8f99e290655..95779d50cca0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -922,6 +922,7 @@ static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; struct device *dev = &vc4_hdmi->pdev->dev; u32 audio_packet_config, channel_mask; u32 channel_map; @@ -981,6 +982,8 @@ static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); vc4_hdmi_set_n_cts(vc4_hdmi); + vc4_hdmi_set_audio_infoframe(encoder); + return 0; } @@ -988,11 +991,9 @@ static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); - struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - vc4_hdmi_set_audio_infoframe(encoder); vc4_hdmi->audio.streaming = true; if (vc4_hdmi->variant->phy_rng_enable) @@ -1076,6 +1077,7 @@ static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = { }; static const struct snd_soc_component_driver vc4_hdmi_audio_component_drv = { + .name = "vc4-hdmi-codec-dai-component", .controls = vc4_hdmi_audio_controls, .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls), .dapm_widgets = vc4_hdmi_audio_widgets, diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6994c1309b2b..cc9e8025c533 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1689,6 +1689,7 @@ static void __exit coresight_exit(void) module_init(coresight_init); module_exit(coresight_exit); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>"); MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>"); MODULE_DESCRIPTION("Arm CoreSight tracer driver"); diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 392757f3a019..7ff7e7780bbf 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -1065,6 +1065,13 @@ static int cti_create_con_sysfs_attr(struct device *dev, } eattr->var = con; con->con_attrs[attr_idx] = &eattr->attr.attr; + /* + * Initialize the dynamically allocated attribute + * to avoid LOCKDEP splat. See include/linux/sysfs.h + * for more details. + */ + sysfs_attr_init(con->con_attrs[attr_idx]); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index c2c9b127d074..bdc34ca449f7 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -210,7 +210,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, u32 id; int cpu = event->cpu; cpumask_t *mask; - struct coresight_device *sink; + struct coresight_device *sink = NULL; struct etm_event_data *event_data = NULL; event_data = alloc_event_data(cpu); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 56f5b8077cba..01bace49a962 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1239,7 +1239,7 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) struct acpi_processor_cx *cx; struct cpuidle_state *state; - if (intel_idle_max_cstate_reached(cstate)) + if (intel_idle_max_cstate_reached(cstate - 1)) break; cx = &acpi_state_table.states[cstate]; diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 7c2ab1f2fbea..a77750b8954d 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -405,10 +405,10 @@ static int cma_comp_exch(struct rdma_id_private *id_priv, /* * The FSM uses a funny double locking where state is protected by both * the handler_mutex and the spinlock. State is not allowed to change - * away from a handler_mutex protected value without also holding + * to/from a handler_mutex protected value without also holding * handler_mutex. */ - if (comp == RDMA_CM_CONNECT) + if (comp == RDMA_CM_CONNECT || exch == RDMA_CM_CONNECT) lockdep_assert_held(&id_priv->handler_mutex); spin_lock_irqsave(&id_priv->lock, flags); @@ -4038,17 +4038,23 @@ out: return ret; } -int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) +/** + * rdma_connect_locked - Initiate an active connection request. + * @id: Connection identifier to connect. + * @conn_param: Connection information used for connected QPs. + * + * Same as rdma_connect() but can only be called from the + * RDMA_CM_EVENT_ROUTE_RESOLVED handler callback. + */ +int rdma_connect_locked(struct rdma_cm_id *id, + struct rdma_conn_param *conn_param) { struct rdma_id_private *id_priv = container_of(id, struct rdma_id_private, id); int ret; - mutex_lock(&id_priv->handler_mutex); - if (!cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_CONNECT)) { - ret = -EINVAL; - goto err_unlock; - } + if (!cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_CONNECT)) + return -EINVAL; if (!id->qp) { id_priv->qp_num = conn_param->qp_num; @@ -4066,11 +4072,33 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) ret = -ENOSYS; if (ret) goto err_state; - mutex_unlock(&id_priv->handler_mutex); return 0; err_state: cma_comp_exch(id_priv, RDMA_CM_CONNECT, RDMA_CM_ROUTE_RESOLVED); -err_unlock: + return ret; +} +EXPORT_SYMBOL(rdma_connect_locked); + +/** + * rdma_connect - Initiate an active connection request. + * @id: Connection identifier to connect. + * @conn_param: Connection information used for connected QPs. + * + * Users must have resolved a route for the rdma_cm_id to connect with by having + * called rdma_resolve_route before calling this routine. + * + * This call will either connect to a remote QP or obtain remote QP information + * for unconnected rdma_cm_id's. The actual operation is based on the + * rdma_cm_id's port space. + */ +int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) +{ + struct rdma_id_private *id_priv = + container_of(id, struct rdma_id_private, id); + int ret; + + mutex_lock(&id_priv->handler_mutex); + ret = rdma_connect_locked(id, conn_param); mutex_unlock(&id_priv->handler_mutex); return ret; } diff --git a/drivers/infiniband/core/uverbs_std_types_device.c b/drivers/infiniband/core/uverbs_std_types_device.c index f367d523a46b..302f898c5833 100644 --- a/drivers/infiniband/core/uverbs_std_types_device.c +++ b/drivers/infiniband/core/uverbs_std_types_device.c @@ -401,9 +401,6 @@ static int UVERBS_HANDLER(UVERBS_METHOD_QUERY_GID_ENTRY)( if (!rdma_is_port_valid(ib_dev, port_num)) return -EINVAL; - if (!rdma_ib_or_roce(ib_dev, port_num)) - return -EOPNOTSUPP; - gid_attr = rdma_get_gid_attr(ib_dev, port_num, gid_index); if (IS_ERR(gid_attr)) return PTR_ERR(gid_attr); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 89e04ca62ae0..246e3cbe0b2c 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3305,7 +3305,8 @@ static int mlx5_add_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num) int err; dev->port[port_num].roce.nb.notifier_call = mlx5_netdev_event; - err = register_netdevice_notifier(&dev->port[port_num].roce.nb); + err = register_netdevice_notifier_net(mlx5_core_net(dev->mdev), + &dev->port[port_num].roce.nb); if (err) { dev->port[port_num].roce.nb.notifier_call = NULL; return err; @@ -3317,7 +3318,8 @@ static int mlx5_add_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num) static void mlx5_remove_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num) { if (dev->port[port_num].roce.nb.notifier_call) { - unregister_netdevice_notifier(&dev->port[port_num].roce.nb); + unregister_netdevice_notifier_net(mlx5_core_net(dev->mdev), + &dev->port[port_num].roce.nb); dev->port[port_num].roce.nb.notifier_call = NULL; } } diff --git a/drivers/infiniband/hw/qedr/qedr_iw_cm.c b/drivers/infiniband/hw/qedr/qedr_iw_cm.c index c7169d2c69e5..c4bc58736e48 100644 --- a/drivers/infiniband/hw/qedr/qedr_iw_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_iw_cm.c @@ -727,6 +727,7 @@ int qedr_iw_destroy_listen(struct iw_cm_id *cm_id) listener->qed_handle); cm_id->rem_ref(cm_id); + kfree(listener); return rc; } diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c index 38021e2c8688..df0d173d6acb 100644 --- a/drivers/infiniband/sw/rxe/rxe_av.c +++ b/drivers/infiniband/sw/rxe/rxe_av.c @@ -16,15 +16,24 @@ void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av) int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr) { + const struct ib_global_route *grh = rdma_ah_read_grh(attr); struct rxe_port *port; + int type; port = &rxe->port; if (rdma_ah_get_ah_flags(attr) & IB_AH_GRH) { - u8 sgid_index = rdma_ah_read_grh(attr)->sgid_index; + if (grh->sgid_index > port->attr.gid_tbl_len) { + pr_warn("invalid sgid index = %d\n", + grh->sgid_index); + return -EINVAL; + } - if (sgid_index > port->attr.gid_tbl_len) { - pr_warn("invalid sgid index = %d\n", sgid_index); + type = rdma_gid_attr_network_type(grh->sgid_attr); + if (type < RDMA_NETWORK_IPV4 || + type > RDMA_NETWORK_IPV6) { + pr_warn("invalid network type for rdma_rxe = %d\n", + type); return -EINVAL; } } @@ -65,11 +74,29 @@ void rxe_av_to_attr(struct rxe_av *av, struct rdma_ah_attr *attr) void rxe_av_fill_ip_info(struct rxe_av *av, struct rdma_ah_attr *attr) { const struct ib_gid_attr *sgid_attr = attr->grh.sgid_attr; + int ibtype; + int type; rdma_gid2ip((struct sockaddr *)&av->sgid_addr, &sgid_attr->gid); rdma_gid2ip((struct sockaddr *)&av->dgid_addr, &rdma_ah_read_grh(attr)->dgid); - av->network_type = rdma_gid_attr_network_type(sgid_attr); + + ibtype = rdma_gid_attr_network_type(sgid_attr); + + switch (ibtype) { + case RDMA_NETWORK_IPV4: + type = RXE_NETWORK_TYPE_IPV4; + break; + case RDMA_NETWORK_IPV6: + type = RXE_NETWORK_TYPE_IPV4; + break; + default: + /* not reached - checked in rxe_av_chk_attr */ + type = 0; + break; + } + + av->network_type = type; } struct rxe_av *rxe_get_av(struct rxe_pkt_info *pkt) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 575e1a4ec821..34bef7d8e6b4 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -442,7 +442,7 @@ struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av, if (IS_ERR(attr)) return NULL; - if (av->network_type == RXE_NETWORK_TYPE_IPV6) + if (av->network_type == RXE_NETWORK_TYPE_IPV4) hdr_len = ETH_HLEN + sizeof(struct udphdr) + sizeof(struct iphdr); else diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 2f3ebc0a75d9..2bd18b006893 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -620,7 +620,7 @@ static void iser_route_handler(struct rdma_cm_id *cma_id) conn_param.private_data = (void *)&req_hdr; conn_param.private_data_len = sizeof(struct iser_cm_hdr); - ret = rdma_connect(cma_id, &conn_param); + ret = rdma_connect_locked(cma_id, &conn_param); if (ret) { iser_err("failure connecting: %d\n", ret); goto failure; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index 776e89231c52..f298adc02acb 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -1674,9 +1674,9 @@ static int rtrs_rdma_route_resolved(struct rtrs_clt_con *con) uuid_copy(&msg.sess_uuid, &sess->s.uuid); uuid_copy(&msg.paths_uuid, &clt->paths_uuid); - err = rdma_connect(con->c.cm_id, ¶m); + err = rdma_connect_locked(con->c.cm_id, ¶m); if (err) - rtrs_err(clt, "rdma_connect(): %d\n", err); + rtrs_err(clt, "rdma_connect_locked(): %d\n", err); return err; } diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index eea47b4c84aa..974a66725d09 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -971,6 +971,9 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider) } node->avg_bw = node->init_avg; node->peak_bw = node->init_peak; + if (provider->aggregate) + provider->aggregate(node, 0, node->init_avg, node->init_peak, + &node->avg_bw, &node->peak_bw); provider->set(node, node); node->avg_bw = 0; node->peak_bw = 0; diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index cf10a4b9611b..bf01d09dba6c 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -79,6 +79,7 @@ EXPORT_SYMBOL_GPL(qcom_icc_aggregate); int qcom_icc_set(struct icc_node *src, struct icc_node *dst) { struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; struct icc_node *node; if (!src) @@ -87,6 +88,12 @@ int qcom_icc_set(struct icc_node *src, struct icc_node *dst) node = src; qp = to_qcom_provider(node->provider); + qn = node->data; + + qn->sum_avg[QCOM_ICC_BUCKET_AMC] = max_t(u64, qn->sum_avg[QCOM_ICC_BUCKET_AMC], + node->avg_bw); + qn->max_peak[QCOM_ICC_BUCKET_AMC] = max_t(u64, qn->max_peak[QCOM_ICC_BUCKET_AMC], + node->peak_bw); qcom_icc_bcm_voter_commit(qp->voter); diff --git a/drivers/interconnect/qcom/sc7180.c b/drivers/interconnect/qcom/sc7180.c index bf11b82ed55c..8d9044ed18ab 100644 --- a/drivers/interconnect/qcom/sc7180.c +++ b/drivers/interconnect/qcom/sc7180.c @@ -553,6 +553,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -576,9 +579,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index d79e3163e2c3..5304aea3b058 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -151,7 +151,7 @@ DEFINE_QBCM(bcm_mc0, "MC0", true, &ebi); DEFINE_QBCM(bcm_sh0, "SH0", true, &qns_llcc); DEFINE_QBCM(bcm_mm0, "MM0", false, &qns_mem_noc_hf); DEFINE_QBCM(bcm_sh1, "SH1", false, &qns_apps_io); -DEFINE_QBCM(bcm_mm1, "MM1", false, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1); +DEFINE_QBCM(bcm_mm1, "MM1", true, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1); DEFINE_QBCM(bcm_sh2, "SH2", false, &qns_memnoc_snoc); DEFINE_QBCM(bcm_mm2, "MM2", false, &qns2_mem_noc); DEFINE_QBCM(bcm_sh3, "SH3", false, &acm_tcu); @@ -489,6 +489,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -512,9 +515,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; diff --git a/drivers/interconnect/qcom/sm8150.c b/drivers/interconnect/qcom/sm8150.c index 9218efed04a0..c76b2c7f9b10 100644 --- a/drivers/interconnect/qcom/sm8150.c +++ b/drivers/interconnect/qcom/sm8150.c @@ -551,6 +551,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -574,9 +577,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; @@ -627,6 +627,7 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8150", .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c index 9b58946f7898..cc558fec74e3 100644 --- a/drivers/interconnect/qcom/sm8250.c +++ b/drivers/interconnect/qcom/sm8250.c @@ -567,6 +567,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -590,9 +593,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; @@ -643,6 +643,7 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8250", .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index a5ef9faf71c7..e7f0d4ae0f96 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -1176,8 +1176,10 @@ mptscsih_remove(struct pci_dev *pdev) MPT_SCSI_HOST *hd; int sz1; - if((hd = shost_priv(host)) == NULL) - return; + if (host == NULL) + hd = NULL; + else + hd = shost_priv(host); mptscsih_shutdown(pdev); @@ -1193,14 +1195,15 @@ mptscsih_remove(struct pci_dev *pdev) "Free'd ScsiLookup (%d) memory\n", ioc->name, sz1)); - kfree(hd->info_kbuf); + if (hd) + kfree(hd->info_kbuf); /* NULL the Scsi_Host pointer */ ioc->sh = NULL; - scsi_host_put(host); - + if (host) + scsi_host_put(host); mpt_detach(pdev); } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d5ce8082b0a0..fafa8b0d8099 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,7 +474,6 @@ source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" -source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2521359e8ef7..d23231e73330 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_SRAM_EXEC) += sram-exec.o -obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_CXL_BASE) += cxl/ diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 8bac86c4d86b..df2fb9520dd8 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -224,7 +224,7 @@ struct mei_ext_hdr { u8 type; u8 length; u8 ext_payload[2]; - u8 hdr[0]; + u8 hdr[]; }; /** @@ -238,7 +238,7 @@ struct mei_ext_meta_hdr { u8 count; u8 size; u8 reserved[2]; - struct mei_ext_hdr hdrs[0]; + struct mei_ext_hdr hdrs[]; }; /* @@ -308,7 +308,7 @@ struct mei_msg_hdr { u32 dma_ring:1; u32 internal:1; u32 msg_complete:1; - u32 extension[0]; + u32 extension[]; } __packed; /* The length is up to 9 bits */ diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig deleted file mode 100644 index 8a7c2c5711d5..000000000000 --- a/drivers/misc/mic/Kconfig +++ /dev/null @@ -1,141 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -menu "Intel MIC & related support" - -config INTEL_MIC_BUS - tristate "Intel MIC Bus Driver" - depends on 64BIT && PCI && X86 - select DMA_OPS - help - This option is selected by any driver which registers a - device or driver on the MIC Bus, such as CONFIG_INTEL_MIC_HOST, - CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc. - - If you are building a host/card kernel with an Intel MIC device - then say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -config SCIF_BUS - tristate "SCIF Bus Driver" - depends on 64BIT && PCI && X86 - select DMA_OPS - help - This option is selected by any driver which registers a - device or driver on the SCIF Bus, such as CONFIG_INTEL_MIC_HOST - and CONFIG_INTEL_MIC_CARD. - - If you are building a host/card kernel with an Intel MIC device - then say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -config VOP_BUS - tristate "VOP Bus Driver" - depends on HAS_DMA - select DMA_OPS - help - This option is selected by any driver which registers a - device or driver on the VOP Bus, such as CONFIG_INTEL_MIC_HOST - and CONFIG_INTEL_MIC_CARD. - - If you are building a host/card kernel with an Intel MIC device - then say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -config INTEL_MIC_HOST - tristate "Intel MIC Host Driver" - depends on 64BIT && PCI && X86 - depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS - select DMA_OPS - help - This enables Host Driver support for the Intel Many Integrated - Core (MIC) family of PCIe form factor coprocessor devices that - run a 64 bit Linux OS. The driver manages card OS state and - enables communication between host and card. Intel MIC X100 - devices are currently supported. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -config INTEL_MIC_CARD - tristate "Intel MIC Card Driver" - depends on 64BIT && X86 - depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS - select VIRTIO - help - This enables card driver support for the Intel Many Integrated - Core (MIC) device family. The card driver communicates shutdown/ - crash events to the host and allows registration/configuration of - virtio devices. Intel MIC X100 devices are currently supported. - - If you are building a card kernel for an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - For more information see - <http://software.intel.com/en-us/mic-developer>. - -config SCIF - tristate "SCIF Driver" - depends on 64BIT && PCI && X86 && SCIF_BUS && IOMMU_SUPPORT - select IOMMU_IOVA - help - This enables SCIF Driver support for the Intel Many Integrated - Core (MIC) family of PCIe form factor coprocessor devices that - run a 64 bit Linux OS. The Symmetric Communication Interface - (SCIF (pronounced as skiff)) is a low level communications API - across PCIe currently implemented for MIC. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -config MIC_COSM - tristate "Intel MIC Coprocessor State Management (COSM) Drivers" - depends on 64BIT && PCI && X86 && SCIF - help - This enables COSM driver support for the Intel Many - Integrated Core (MIC) family of PCIe form factor coprocessor - devices. COSM drivers implement functions such as boot, - shutdown, reset and reboot of MIC devices. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -config VOP - tristate "VOP Driver" - depends on VOP_BUS - select VHOST_RING - select VIRTIO - help - This enables VOP (Virtio over PCIe) Driver support for the Intel - Many Integrated Core (MIC) family of PCIe form factor coprocessor - devices. The VOP driver allows virtio drivers, e.g. net, console - and block drivers, on the card connect to user space virtio - devices on the host. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - <http://software.intel.com/en-us/mic-developer>. - -endmenu diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile deleted file mode 100644 index 1a43622b183f..000000000000 --- a/drivers/misc/mic/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2013, Intel Corporation. -# -obj-$(CONFIG_INTEL_MIC_HOST) += host/ -obj-$(CONFIG_INTEL_MIC_CARD) += card/ -obj-y += bus/ -obj-$(CONFIG_SCIF) += scif/ -obj-$(CONFIG_MIC_COSM) += cosm/ -obj-$(CONFIG_MIC_COSM) += cosm_client/ -obj-$(CONFIG_VOP) += vop/ diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile deleted file mode 100644 index 0a6aa21b2f67..000000000000 --- a/drivers/misc/mic/bus/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2014, Intel Corporation. -# -obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o -obj-$(CONFIG_SCIF_BUS) += scif_bus.o -obj-$(CONFIG_MIC_COSM) += cosm_bus.o -obj-$(CONFIG_VOP_BUS) += vop_bus.o diff --git a/drivers/misc/mic/bus/cosm_bus.c b/drivers/misc/mic/bus/cosm_bus.c deleted file mode 100644 index 5f2141c71738..000000000000 --- a/drivers/misc/mic/bus/cosm_bus.c +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC COSM Bus Driver - */ -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/idr.h> -#include "cosm_bus.h" - -/* Unique numbering for cosm devices. */ -static DEFINE_IDA(cosm_index_ida); - -static int cosm_dev_probe(struct device *d) -{ - struct cosm_device *dev = dev_to_cosm(d); - struct cosm_driver *drv = drv_to_cosm(dev->dev.driver); - - return drv->probe(dev); -} - -static int cosm_dev_remove(struct device *d) -{ - struct cosm_device *dev = dev_to_cosm(d); - struct cosm_driver *drv = drv_to_cosm(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type cosm_bus = { - .name = "cosm_bus", - .probe = cosm_dev_probe, - .remove = cosm_dev_remove, -}; - -int cosm_register_driver(struct cosm_driver *driver) -{ - driver->driver.bus = &cosm_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(cosm_register_driver); - -void cosm_unregister_driver(struct cosm_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(cosm_unregister_driver); - -static inline void cosm_release_dev(struct device *d) -{ - struct cosm_device *cdev = dev_to_cosm(d); - - kfree(cdev); -} - -struct cosm_device * -cosm_register_device(struct device *pdev, struct cosm_hw_ops *hw_ops) -{ - struct cosm_device *cdev; - int ret; - - cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); - if (!cdev) - return ERR_PTR(-ENOMEM); - - cdev->dev.parent = pdev; - cdev->dev.release = cosm_release_dev; - cdev->hw_ops = hw_ops; - dev_set_drvdata(&cdev->dev, cdev); - cdev->dev.bus = &cosm_bus; - - /* Assign a unique device index and hence name */ - ret = ida_simple_get(&cosm_index_ida, 0, 0, GFP_KERNEL); - if (ret < 0) - goto free_cdev; - - cdev->index = ret; - cdev->dev.id = ret; - dev_set_name(&cdev->dev, "cosm-dev%u", cdev->index); - - ret = device_register(&cdev->dev); - if (ret) - goto ida_remove; - return cdev; -ida_remove: - ida_simple_remove(&cosm_index_ida, cdev->index); -free_cdev: - put_device(&cdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(cosm_register_device); - -void cosm_unregister_device(struct cosm_device *dev) -{ - int index = dev->index; /* save for after device release */ - - device_unregister(&dev->dev); - ida_simple_remove(&cosm_index_ida, index); -} -EXPORT_SYMBOL_GPL(cosm_unregister_device); - -struct cosm_device *cosm_find_cdev_by_id(int id) -{ - struct device *dev = subsys_find_device_by_id(&cosm_bus, id, NULL); - - return dev ? container_of(dev, struct cosm_device, dev) : NULL; -} -EXPORT_SYMBOL_GPL(cosm_find_cdev_by_id); - -static int __init cosm_init(void) -{ - return bus_register(&cosm_bus); -} - -static void __exit cosm_exit(void) -{ - bus_unregister(&cosm_bus); - ida_destroy(&cosm_index_ida); -} - -core_initcall(cosm_init); -module_exit(cosm_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC card OS state management bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/cosm_bus.h b/drivers/misc/mic/bus/cosm_bus.h deleted file mode 100644 index d50d7aea168d..000000000000 --- a/drivers/misc/mic/bus/cosm_bus.h +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC COSM Bus Driver - */ -#ifndef _COSM_BUS_H_ -#define _COSM_BUS_H_ - -#include <linux/scif.h> -#include <linux/mic_common.h> -#include "../common/mic_dev.h" - -/** - * cosm_device - representation of a cosm device - * - * @attr_group: Pointer to list of sysfs attribute groups. - * @sdev: Device for sysfs entries. - * @state: MIC state. - * @prev_state: MIC state previous to MIC_RESETTING - * @shutdown_status: MIC status reported by card for shutdown/crashes. - * @shutdown_status_int: Internal shutdown status maintained by the driver - * @cosm_mutex: Mutex for synchronizing access to data structures. - * @reset_trigger_work: Work for triggering reset requests. - * @scif_work: Work for handling per device SCIF connections - * @cmdline: Kernel command line. - * @firmware: Firmware file name. - * @ramdisk: Ramdisk file name. - * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates. - * @log_buf_addr: Log buffer address for MIC. - * @log_buf_len: Log buffer length address for MIC. - * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. - * @hw_ops: the hardware bus ops for this device. - * @dev: underlying device. - * @index: unique position on the cosm bus - * @dbg_dir: debug fs directory - * @newepd: new endpoint from scif accept to be assigned to this cdev - * @epd: SCIF endpoint for this cdev - * @heartbeat_watchdog_enable: if heartbeat watchdog is enabled for this cdev - * @sysfs_heartbeat_enable: sysfs setting for disabling heartbeat notification - */ -struct cosm_device { - const struct attribute_group **attr_group; - struct device *sdev; - u8 state; - u8 prev_state; - u8 shutdown_status; - u8 shutdown_status_int; - struct mutex cosm_mutex; - struct work_struct reset_trigger_work; - struct work_struct scif_work; - char *cmdline; - char *firmware; - char *ramdisk; - char *bootmode; - void *log_buf_addr; - int *log_buf_len; - struct kernfs_node *state_sysfs; - struct cosm_hw_ops *hw_ops; - struct device dev; - int index; - struct dentry *dbg_dir; - scif_epd_t newepd; - scif_epd_t epd; - bool heartbeat_watchdog_enable; - bool sysfs_heartbeat_enable; -}; - -/** - * cosm_driver - operations for a cosm driver - * - * @driver: underlying device driver (populate name and owner). - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct cosm_driver { - struct device_driver driver; - int (*probe)(struct cosm_device *dev); - void (*remove)(struct cosm_device *dev); -}; - -/** - * cosm_hw_ops - cosm bus ops - * - * @reset: trigger MIC reset - * @force_reset: force MIC reset - * @post_reset: inform MIC reset is complete - * @ready: is MIC ready for OS download - * @start: boot MIC - * @stop: prepare MIC for reset - * @family: return MIC HW family string - * @stepping: return MIC HW stepping string - * @aper: return MIC PCIe aperture - */ -struct cosm_hw_ops { - void (*reset)(struct cosm_device *cdev); - void (*force_reset)(struct cosm_device *cdev); - void (*post_reset)(struct cosm_device *cdev, enum mic_states state); - bool (*ready)(struct cosm_device *cdev); - int (*start)(struct cosm_device *cdev, int id); - void (*stop)(struct cosm_device *cdev, bool force); - ssize_t (*family)(struct cosm_device *cdev, char *buf); - ssize_t (*stepping)(struct cosm_device *cdev, char *buf); - struct mic_mw *(*aper)(struct cosm_device *cdev); -}; - -struct cosm_device * -cosm_register_device(struct device *pdev, struct cosm_hw_ops *hw_ops); -void cosm_unregister_device(struct cosm_device *dev); -int cosm_register_driver(struct cosm_driver *drv); -void cosm_unregister_driver(struct cosm_driver *drv); -struct cosm_device *cosm_find_cdev_by_id(int id); - -static inline struct cosm_device *dev_to_cosm(struct device *dev) -{ - return container_of(dev, struct cosm_device, dev); -} - -static inline struct cosm_driver *drv_to_cosm(struct device_driver *drv) -{ - return container_of(drv, struct cosm_driver, driver); -} -#endif /* _COSM_BUS_H */ diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c deleted file mode 100644 index a08cb29692a8..000000000000 --- a/drivers/misc/mic/bus/mic_bus.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC Bus driver. - * - * This implementation is very similar to the the virtio bus driver - * implementation @ drivers/virtio/virtio.c - */ -#include <linux/dma-map-ops.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/mic_bus.h> - -static ssize_t device_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct mbus_device *dev = dev_to_mbus(d); - return sprintf(buf, "0x%04x\n", dev->id.device); -} -static DEVICE_ATTR_RO(device); - -static ssize_t vendor_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct mbus_device *dev = dev_to_mbus(d); - return sprintf(buf, "0x%04x\n", dev->id.vendor); -} -static DEVICE_ATTR_RO(vendor); - -static ssize_t modalias_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct mbus_device *dev = dev_to_mbus(d); - return sprintf(buf, "mbus:d%08Xv%08X\n", - dev->id.device, dev->id.vendor); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *mbus_dev_attrs[] = { - &dev_attr_device.attr, - &dev_attr_vendor.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mbus_dev); - -static inline int mbus_id_match(const struct mbus_device *dev, - const struct mbus_device_id *id) -{ - if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID) - return 0; - - return id->vendor == MBUS_DEV_ANY_ID || id->vendor == dev->id.vendor; -} - -/* - * This looks through all the IDs a driver claims to support. If any of them - * match, we return 1 and the kernel will call mbus_dev_probe(). - */ -static int mbus_dev_match(struct device *dv, struct device_driver *dr) -{ - unsigned int i; - struct mbus_device *dev = dev_to_mbus(dv); - const struct mbus_device_id *ids; - - ids = drv_to_mbus(dr)->id_table; - for (i = 0; ids[i].device; i++) - if (mbus_id_match(dev, &ids[i])) - return 1; - return 0; -} - -static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env) -{ - struct mbus_device *dev = dev_to_mbus(dv); - - return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X", - dev->id.device, dev->id.vendor); -} - -static int mbus_dev_probe(struct device *d) -{ - int err; - struct mbus_device *dev = dev_to_mbus(d); - struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); - - err = drv->probe(dev); - if (!err) - if (drv->scan) - drv->scan(dev); - return err; -} - -static int mbus_dev_remove(struct device *d) -{ - struct mbus_device *dev = dev_to_mbus(d); - struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type mic_bus = { - .name = "mic_bus", - .match = mbus_dev_match, - .dev_groups = mbus_dev_groups, - .uevent = mbus_uevent, - .probe = mbus_dev_probe, - .remove = mbus_dev_remove, -}; - -int mbus_register_driver(struct mbus_driver *driver) -{ - driver->driver.bus = &mic_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(mbus_register_driver); - -void mbus_unregister_driver(struct mbus_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(mbus_unregister_driver); - -static void mbus_release_dev(struct device *d) -{ - struct mbus_device *mbdev = dev_to_mbus(d); - kfree(mbdev); -} - -struct mbus_device * -mbus_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, - struct mbus_hw_ops *hw_ops, int index, - void __iomem *mmio_va) -{ - int ret; - struct mbus_device *mbdev; - - mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL); - if (!mbdev) - return ERR_PTR(-ENOMEM); - - mbdev->mmio_va = mmio_va; - mbdev->dev.parent = pdev; - mbdev->id.device = id; - mbdev->id.vendor = MBUS_DEV_ANY_ID; - mbdev->dev.dma_ops = dma_ops; - mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; - dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); - mbdev->dev.release = mbus_release_dev; - mbdev->hw_ops = hw_ops; - mbdev->dev.bus = &mic_bus; - mbdev->index = index; - dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index); - /* - * device_register() causes the bus infrastructure to look for a - * matching driver. - */ - ret = device_register(&mbdev->dev); - if (ret) - goto free_mbdev; - return mbdev; -free_mbdev: - put_device(&mbdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(mbus_register_device); - -void mbus_unregister_device(struct mbus_device *mbdev) -{ - device_unregister(&mbdev->dev); -} -EXPORT_SYMBOL_GPL(mbus_unregister_device); - -static int __init mbus_init(void) -{ - return bus_register(&mic_bus); -} - -static void __exit mbus_exit(void) -{ - bus_unregister(&mic_bus); -} - -core_initcall(mbus_init); -module_exit(mbus_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC Bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/scif_bus.c b/drivers/misc/mic/bus/scif_bus.c deleted file mode 100644 index ad7c3604f151..000000000000 --- a/drivers/misc/mic/bus/scif_bus.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel Symmetric Communications Interface Bus driver. - */ -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/dma-map-ops.h> - -#include "scif_bus.h" - -static ssize_t device_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - - return sprintf(buf, "0x%04x\n", dev->id.device); -} -static DEVICE_ATTR_RO(device); - -static ssize_t vendor_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - - return sprintf(buf, "0x%04x\n", dev->id.vendor); -} -static DEVICE_ATTR_RO(vendor); - -static ssize_t modalias_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - - return sprintf(buf, "scif:d%08Xv%08X\n", - dev->id.device, dev->id.vendor); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *scif_dev_attrs[] = { - &dev_attr_device.attr, - &dev_attr_vendor.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(scif_dev); - -static inline int scif_id_match(const struct scif_hw_dev *dev, - const struct scif_hw_dev_id *id) -{ - if (id->device != dev->id.device && id->device != SCIF_DEV_ANY_ID) - return 0; - - return id->vendor == SCIF_DEV_ANY_ID || id->vendor == dev->id.vendor; -} - -/* - * This looks through all the IDs a driver claims to support. If any of them - * match, we return 1 and the kernel will call scif_dev_probe(). - */ -static int scif_dev_match(struct device *dv, struct device_driver *dr) -{ - unsigned int i; - struct scif_hw_dev *dev = dev_to_scif(dv); - const struct scif_hw_dev_id *ids; - - ids = drv_to_scif(dr)->id_table; - for (i = 0; ids[i].device; i++) - if (scif_id_match(dev, &ids[i])) - return 1; - return 0; -} - -static int scif_uevent(struct device *dv, struct kobj_uevent_env *env) -{ - struct scif_hw_dev *dev = dev_to_scif(dv); - - return add_uevent_var(env, "MODALIAS=scif:d%08Xv%08X", - dev->id.device, dev->id.vendor); -} - -static int scif_dev_probe(struct device *d) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - struct scif_driver *drv = drv_to_scif(dev->dev.driver); - - return drv->probe(dev); -} - -static int scif_dev_remove(struct device *d) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - struct scif_driver *drv = drv_to_scif(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type scif_bus = { - .name = "scif_bus", - .match = scif_dev_match, - .dev_groups = scif_dev_groups, - .uevent = scif_uevent, - .probe = scif_dev_probe, - .remove = scif_dev_remove, -}; - -int scif_register_driver(struct scif_driver *driver) -{ - driver->driver.bus = &scif_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(scif_register_driver); - -void scif_unregister_driver(struct scif_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(scif_unregister_driver); - -static void scif_release_dev(struct device *d) -{ - struct scif_hw_dev *sdev = dev_to_scif(d); - - kfree(sdev); -} - -struct scif_hw_dev * -scif_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, - struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, - struct mic_mw *mmio, struct mic_mw *aper, void *dp, - void __iomem *rdp, struct dma_chan **chan, int num_chan, - bool card_rel_da) -{ - int ret; - struct scif_hw_dev *sdev; - - sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); - if (!sdev) - return ERR_PTR(-ENOMEM); - - sdev->dev.parent = pdev; - sdev->id.device = id; - sdev->id.vendor = SCIF_DEV_ANY_ID; - sdev->dev.dma_ops = dma_ops; - sdev->dev.release = scif_release_dev; - sdev->hw_ops = hw_ops; - sdev->dnode = dnode; - sdev->snode = snode; - dev_set_drvdata(&sdev->dev, sdev); - sdev->dev.bus = &scif_bus; - sdev->mmio = mmio; - sdev->aper = aper; - sdev->dp = dp; - sdev->rdp = rdp; - sdev->dev.dma_mask = &sdev->dev.coherent_dma_mask; - dma_set_mask(&sdev->dev, DMA_BIT_MASK(64)); - sdev->dma_ch = chan; - sdev->num_dma_ch = num_chan; - sdev->card_rel_da = card_rel_da; - dev_set_name(&sdev->dev, "scif-dev%u", sdev->dnode); - /* - * device_register() causes the bus infrastructure to look for a - * matching driver. - */ - ret = device_register(&sdev->dev); - if (ret) - goto free_sdev; - return sdev; -free_sdev: - put_device(&sdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(scif_register_device); - -void scif_unregister_device(struct scif_hw_dev *sdev) -{ - device_unregister(&sdev->dev); -} -EXPORT_SYMBOL_GPL(scif_unregister_device); - -static int __init scif_init(void) -{ - return bus_register(&scif_bus); -} - -static void __exit scif_exit(void) -{ - bus_unregister(&scif_bus); -} - -core_initcall(scif_init); -module_exit(scif_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) SCIF Bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/scif_bus.h b/drivers/misc/mic/bus/scif_bus.h deleted file mode 100644 index 4981eb56f879..000000000000 --- a/drivers/misc/mic/bus/scif_bus.h +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel Symmetric Communications Interface Bus driver. - */ -#ifndef _SCIF_BUS_H_ -#define _SCIF_BUS_H_ -/* - * Everything a scif driver needs to work with any particular scif - * hardware abstraction layer. - */ -#include <linux/dma-map-ops.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" - -struct scif_hw_dev_id { - u32 device; - u32 vendor; -}; - -#define MIC_SCIF_DEV 1 -#define SCIF_DEV_ANY_ID 0xffffffff - -/** - * scif_hw_dev - representation of a hardware device abstracted for scif - * @hw_ops: the hardware ops supported by this device - * @id: the device type identification (used to match it with a driver) - * @mmio: MMIO memory window - * @aper: Aperture memory window - * @dev: underlying device - * @dnode - The destination node which this device will communicate with. - * @snode - The source node for this device. - * @dp - Self device page - * @rdp - Remote device page - * @dma_ch - Array of DMA channels - * @num_dma_ch - Number of DMA channels available - * @card_rel_da - Set to true if DMA addresses programmed in the DMA engine - * are relative to the card point of view - */ -struct scif_hw_dev { - struct scif_hw_ops *hw_ops; - struct scif_hw_dev_id id; - struct mic_mw *mmio; - struct mic_mw *aper; - struct device dev; - u8 dnode; - u8 snode; - void *dp; - void __iomem *rdp; - struct dma_chan **dma_ch; - int num_dma_ch; - bool card_rel_da; -}; - -/** - * scif_driver - operations for a scif I/O driver - * @driver: underlying device driver (populate name and owner). - * @id_table: the ids serviced by this driver. - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct scif_driver { - struct device_driver driver; - const struct scif_hw_dev_id *id_table; - int (*probe)(struct scif_hw_dev *dev); - void (*remove)(struct scif_hw_dev *dev); -}; - -/** - * scif_hw_ops - Hardware operations for accessing a SCIF device on the SCIF bus. - * - * @next_db: Obtain the next available doorbell. - * @request_irq: Request an interrupt on a particular doorbell. - * @free_irq: Free an interrupt requested previously. - * @ack_interrupt: acknowledge an interrupt in the ISR. - * @send_intr: Send an interrupt to the remote node on a specified doorbell. - * @send_p2p_intr: Send an interrupt to the peer node on a specified doorbell - * which is specifically targeted for a peer to peer node. - * @remap: Map a buffer with the specified physical address and length. - * @unmap: Unmap a buffer previously mapped. - */ -struct scif_hw_ops { - int (*next_db)(struct scif_hw_dev *sdev); - struct mic_irq * (*request_irq)(struct scif_hw_dev *sdev, - irqreturn_t (*func)(int irq, - void *data), - const char *name, void *data, - int db); - void (*free_irq)(struct scif_hw_dev *sdev, - struct mic_irq *cookie, void *data); - void (*ack_interrupt)(struct scif_hw_dev *sdev, int num); - void (*send_intr)(struct scif_hw_dev *sdev, int db); - void (*send_p2p_intr)(struct scif_hw_dev *sdev, int db, - struct mic_mw *mw); - void __iomem * (*remap)(struct scif_hw_dev *sdev, - phys_addr_t pa, size_t len); - void (*unmap)(struct scif_hw_dev *sdev, void __iomem *va); -}; - -int scif_register_driver(struct scif_driver *driver); -void scif_unregister_driver(struct scif_driver *driver); -struct scif_hw_dev * -scif_register_device(struct device *pdev, int id, - const struct dma_map_ops *dma_ops, - struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, - struct mic_mw *mmio, struct mic_mw *aper, - void *dp, void __iomem *rdp, - struct dma_chan **chan, int num_chan, - bool card_rel_da); -void scif_unregister_device(struct scif_hw_dev *sdev); - -static inline struct scif_hw_dev *dev_to_scif(struct device *dev) -{ - return container_of(dev, struct scif_hw_dev, dev); -} - -static inline struct scif_driver *drv_to_scif(struct device_driver *drv) -{ - return container_of(drv, struct scif_driver, driver); -} -#endif /* _SCIF_BUS_H */ diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c deleted file mode 100644 index 6935ddca1bd5..000000000000 --- a/drivers/misc/mic/bus/vop_bus.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) Bus driver. - */ -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/dma-map-ops.h> - -#include "vop_bus.h" - -static ssize_t device_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct vop_device *dev = dev_to_vop(d); - - return sprintf(buf, "0x%04x\n", dev->id.device); -} -static DEVICE_ATTR_RO(device); - -static ssize_t vendor_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct vop_device *dev = dev_to_vop(d); - - return sprintf(buf, "0x%04x\n", dev->id.vendor); -} -static DEVICE_ATTR_RO(vendor); - -static ssize_t modalias_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct vop_device *dev = dev_to_vop(d); - - return sprintf(buf, "vop:d%08Xv%08X\n", - dev->id.device, dev->id.vendor); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *vop_dev_attrs[] = { - &dev_attr_device.attr, - &dev_attr_vendor.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(vop_dev); - -static inline int vop_id_match(const struct vop_device *dev, - const struct vop_device_id *id) -{ - if (id->device != dev->id.device && id->device != VOP_DEV_ANY_ID) - return 0; - - return id->vendor == VOP_DEV_ANY_ID || id->vendor == dev->id.vendor; -} - -/* - * This looks through all the IDs a driver claims to support. If any of them - * match, we return 1 and the kernel will call vop_dev_probe(). - */ -static int vop_dev_match(struct device *dv, struct device_driver *dr) -{ - unsigned int i; - struct vop_device *dev = dev_to_vop(dv); - const struct vop_device_id *ids; - - ids = drv_to_vop(dr)->id_table; - for (i = 0; ids[i].device; i++) - if (vop_id_match(dev, &ids[i])) - return 1; - return 0; -} - -static int vop_uevent(struct device *dv, struct kobj_uevent_env *env) -{ - struct vop_device *dev = dev_to_vop(dv); - - return add_uevent_var(env, "MODALIAS=vop:d%08Xv%08X", - dev->id.device, dev->id.vendor); -} - -static int vop_dev_probe(struct device *d) -{ - struct vop_device *dev = dev_to_vop(d); - struct vop_driver *drv = drv_to_vop(dev->dev.driver); - - return drv->probe(dev); -} - -static int vop_dev_remove(struct device *d) -{ - struct vop_device *dev = dev_to_vop(d); - struct vop_driver *drv = drv_to_vop(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type vop_bus = { - .name = "vop_bus", - .match = vop_dev_match, - .dev_groups = vop_dev_groups, - .uevent = vop_uevent, - .probe = vop_dev_probe, - .remove = vop_dev_remove, -}; - -int vop_register_driver(struct vop_driver *driver) -{ - driver->driver.bus = &vop_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(vop_register_driver); - -void vop_unregister_driver(struct vop_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(vop_unregister_driver); - -static void vop_release_dev(struct device *d) -{ - struct vop_device *dev = dev_to_vop(d); - - kfree(dev); -} - -struct vop_device * -vop_register_device(struct device *pdev, int id, - const struct dma_map_ops *dma_ops, - struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, - struct dma_chan *chan) -{ - int ret; - struct vop_device *vdev; - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) - return ERR_PTR(-ENOMEM); - - vdev->dev.parent = pdev; - vdev->id.device = id; - vdev->id.vendor = VOP_DEV_ANY_ID; - vdev->dev.dma_ops = dma_ops; - vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask; - dma_set_mask(&vdev->dev, DMA_BIT_MASK(64)); - vdev->dev.release = vop_release_dev; - vdev->hw_ops = hw_ops; - vdev->dev.bus = &vop_bus; - vdev->dnode = dnode; - vdev->aper = aper; - vdev->dma_ch = chan; - vdev->index = dnode - 1; - dev_set_name(&vdev->dev, "vop-dev%u", vdev->index); - /* - * device_register() causes the bus infrastructure to look for a - * matching driver. - */ - ret = device_register(&vdev->dev); - if (ret) - goto free_vdev; - return vdev; -free_vdev: - put_device(&vdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(vop_register_device); - -void vop_unregister_device(struct vop_device *dev) -{ - device_unregister(&dev->dev); -} -EXPORT_SYMBOL_GPL(vop_unregister_device); - -static int __init vop_init(void) -{ - return bus_register(&vop_bus); -} - -static void __exit vop_exit(void) -{ - bus_unregister(&vop_bus); -} - -core_initcall(vop_init); -module_exit(vop_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) VOP Bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/vop_bus.h b/drivers/misc/mic/bus/vop_bus.h deleted file mode 100644 index 4fa02808c1e2..000000000000 --- a/drivers/misc/mic/bus/vop_bus.h +++ /dev/null @@ -1,129 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio over PCIe Bus driver. - */ -#ifndef _VOP_BUS_H_ -#define _VOP_BUS_H_ -/* - * Everything a vop driver needs to work with any particular vop - * implementation. - */ -#include <linux/dmaengine.h> -#include <linux/interrupt.h> - -#include "../common/mic_dev.h" - -struct vop_device_id { - u32 device; - u32 vendor; -}; - -#define VOP_DEV_TRNSP 1 -#define VOP_DEV_ANY_ID 0xffffffff -/* - * Size of the internal buffer used during DMA's as an intermediate buffer - * for copy to/from user. Must be an integral number of pages. - */ -#define VOP_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) - -/** - * vop_device - representation of a device using vop - * @hw_ops: the hardware ops supported by this device. - * @id: the device type identification (used to match it with a driver). - * @dev: underlying device. - * @dnode - The destination node which this device will communicate with. - * @aper: Aperture memory window - * @dma_ch - DMA channel - * @index: unique position on the vop bus - */ -struct vop_device { - struct vop_hw_ops *hw_ops; - struct vop_device_id id; - struct device dev; - u8 dnode; - struct mic_mw *aper; - struct dma_chan *dma_ch; - int index; -}; - -/** - * vop_driver - operations for a vop I/O driver - * @driver: underlying device driver (populate name and owner). - * @id_table: the ids serviced by this driver. - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct vop_driver { - struct device_driver driver; - const struct vop_device_id *id_table; - int (*probe)(struct vop_device *dev); - void (*remove)(struct vop_device *dev); -}; - -/** - * vop_hw_ops - Hardware operations for accessing a VOP device on the VOP bus. - * - * @next_db: Obtain the next available doorbell. - * @request_irq: Request an interrupt on a particular doorbell. - * @free_irq: Free an interrupt requested previously. - * @ack_interrupt: acknowledge an interrupt in the ISR. - * @get_remote_dp: Get access to the virtio device page used by the remote - * node to add/remove/configure virtio devices. - * @get_dp: Get access to the virtio device page used by the self - * node to add/remove/configure virtio devices. - * @send_intr: Send an interrupt to the peer node on a specified doorbell. - * @remap: Map a buffer with the specified DMA address and length. - * @unmap: Unmap a buffer previously mapped. - * @dma_filter: The DMA filter function to use for obtaining access to - * a DMA channel on the peer node. - */ -struct vop_hw_ops { - int (*next_db)(struct vop_device *vpdev); - struct mic_irq *(*request_irq)(struct vop_device *vpdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, - int intr_src); - void (*free_irq)(struct vop_device *vpdev, - struct mic_irq *cookie, void *data); - void (*ack_interrupt)(struct vop_device *vpdev, int num); - void __iomem * (*get_remote_dp)(struct vop_device *vpdev); - void * (*get_dp)(struct vop_device *vpdev); - void (*send_intr)(struct vop_device *vpdev, int db); - void __iomem * (*remap)(struct vop_device *vpdev, - dma_addr_t pa, size_t len); - void (*unmap)(struct vop_device *vpdev, void __iomem *va); -}; - -struct vop_device * -vop_register_device(struct device *pdev, int id, - const struct dma_map_ops *dma_ops, - struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, - struct dma_chan *chan); -void vop_unregister_device(struct vop_device *dev); -int vop_register_driver(struct vop_driver *drv); -void vop_unregister_driver(struct vop_driver *drv); - -/* - * module_vop_driver() - Helper macro for drivers that don't do - * anything special in module init/exit. This eliminates a lot of - * boilerplate. Each module may only use this macro once, and - * calling it replaces module_init() and module_exit() - */ -#define module_vop_driver(__vop_driver) \ - module_driver(__vop_driver, vop_register_driver, \ - vop_unregister_driver) - -static inline struct vop_device *dev_to_vop(struct device *dev) -{ - return container_of(dev, struct vop_device, dev); -} - -static inline struct vop_driver *drv_to_vop(struct device_driver *drv) -{ - return container_of(drv, struct vop_driver, driver); -} -#endif /* _VOP_BUS_H */ diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile deleted file mode 100644 index 921a7e7e0fbd..000000000000 --- a/drivers/misc/mic/card/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2013, Intel Corporation. -# -ccflags-y += -DINTEL_MIC_CARD - -obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o -mic_card-y += mic_x100.o -mic_card-y += mic_device.o -mic_card-y += mic_debugfs.o diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c deleted file mode 100644 index 4c326e8f4d99..000000000000 --- a/drivers/misc/mic/card/mic_debugfs.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#include <linux/debugfs.h> -#include <linux/delay.h> -#include <linux/seq_file.h> -#include <linux/interrupt.h> -#include <linux/device.h> - -#include "../common/mic_dev.h" -#include "mic_device.h" - -/* Debugfs parent dir */ -static struct dentry *mic_dbg; - -/* - * mic_intr_show - Send interrupts to host. - */ -static int mic_intr_show(struct seq_file *s, void *unused) -{ - struct mic_driver *mdrv = s->private; - struct mic_device *mdev = &mdrv->mdev; - - mic_send_intr(mdev, 0); - msleep(1000); - mic_send_intr(mdev, 1); - msleep(1000); - mic_send_intr(mdev, 2); - msleep(1000); - mic_send_intr(mdev, 3); - msleep(1000); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_intr); - -/* - * mic_create_card_debug_dir - Initialize MIC debugfs entries. - */ -void __init mic_create_card_debug_dir(struct mic_driver *mdrv) -{ - if (!mic_dbg) - return; - - mdrv->dbg_dir = debugfs_create_dir(mdrv->name, mic_dbg); - - debugfs_create_file("intr_test", 0444, mdrv->dbg_dir, mdrv, - &mic_intr_fops); -} - -/* - * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries. - */ -void mic_delete_card_debug_dir(struct mic_driver *mdrv) -{ - debugfs_remove_recursive(mdrv->dbg_dir); -} - -/* - * mic_init_card_debugfs - Initialize global debugfs entry. - */ -void __init mic_init_card_debugfs(void) -{ - mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); -} - -/* - * mic_exit_card_debugfs - Uninitialize global debugfs entry - */ -void mic_exit_card_debugfs(void) -{ - debugfs_remove(mic_dbg); -} diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c deleted file mode 100644 index a15606259bdc..000000000000 --- a/drivers/misc/mic/card/mic_device.c +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/reboot.h> -#include <linux/dmaengine.h> -#include <linux/kmod.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" -#include "mic_device.h" - -static struct mic_driver *g_drv; - -static int __init mic_dp_init(void) -{ - struct mic_driver *mdrv = g_drv; - struct mic_device *mdev = &mdrv->mdev; - struct mic_bootparam __iomem *bootparam; - u64 lo, hi, dp_dma_addr; - u32 magic; - - lo = mic_read_spad(&mdrv->mdev, MIC_DPLO_SPAD); - hi = mic_read_spad(&mdrv->mdev, MIC_DPHI_SPAD); - - dp_dma_addr = lo | (hi << 32); - mdrv->dp = mic_card_map(mdev, dp_dma_addr, MIC_DP_SIZE); - if (!mdrv->dp) { - dev_err(mdrv->dev, "Cannot remap Aperture BAR\n"); - return -ENOMEM; - } - bootparam = mdrv->dp; - magic = ioread32(&bootparam->magic); - if (MIC_MAGIC != magic) { - dev_err(mdrv->dev, "bootparam magic mismatch 0x%x\n", magic); - return -EIO; - } - return 0; -} - -/* Uninitialize the device page */ -static void mic_dp_uninit(void) -{ - mic_card_unmap(&g_drv->mdev, g_drv->dp); -} - -/** - * mic_request_card_irq - request an irq. - * - * @handler: interrupt handler passed to request_threaded_irq. - * @thread_fn: thread fn. passed to request_threaded_irq. - * @name: The ASCII name of the callee requesting the irq. - * @data: private data that is returned back when calling the - * function handler. - * @index: The doorbell index of the requester. - * - * returns: The cookie that is transparent to the caller. Passed - * back when calling mic_free_irq. An appropriate error code - * is returned on failure. Caller needs to use IS_ERR(return_val) - * to check for failure and PTR_ERR(return_val) to obtained the - * error code. - * - */ -struct mic_irq * -mic_request_card_irq(irq_handler_t handler, - irq_handler_t thread_fn, const char *name, - void *data, int index) -{ - int rc = 0; - unsigned long cookie; - struct mic_driver *mdrv = g_drv; - - rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler, - thread_fn, 0, name, data); - if (rc) { - dev_err(mdrv->dev, "request_threaded_irq failed rc = %d\n", rc); - goto err; - } - mdrv->irq_info.irq_usage_count[index]++; - cookie = index; - return (struct mic_irq *)cookie; -err: - return ERR_PTR(rc); -} - -/** - * mic_free_card_irq - free irq. - * - * @cookie: cookie obtained during a successful call to mic_request_threaded_irq - * @data: private data specified by the calling function during the - * mic_request_threaded_irq - * - * returns: none. - */ -void mic_free_card_irq(struct mic_irq *cookie, void *data) -{ - int index; - struct mic_driver *mdrv = g_drv; - - index = (unsigned long)cookie & 0xFFFFU; - free_irq(mic_db_to_irq(mdrv, index), data); - mdrv->irq_info.irq_usage_count[index]--; -} - -/** - * mic_next_card_db - Get the doorbell with minimum usage count. - * - * Returns the irq index. - */ -int mic_next_card_db(void) -{ - int i; - int index = 0; - struct mic_driver *mdrv = g_drv; - - for (i = 0; i < mdrv->intr_info.num_intr; i++) { - if (mdrv->irq_info.irq_usage_count[i] < - mdrv->irq_info.irq_usage_count[index]) - index = i; - } - - return index; -} - -/** - * mic_init_irq - Initialize irq information. - * - * Returns 0 in success. Appropriate error code on failure. - */ -static int mic_init_irq(void) -{ - struct mic_driver *mdrv = g_drv; - - mdrv->irq_info.irq_usage_count = kzalloc((sizeof(u32) * - mdrv->intr_info.num_intr), - GFP_KERNEL); - if (!mdrv->irq_info.irq_usage_count) - return -ENOMEM; - return 0; -} - -/** - * mic_uninit_irq - Uninitialize irq information. - * - * None. - */ -static void mic_uninit_irq(void) -{ - struct mic_driver *mdrv = g_drv; - - kfree(mdrv->irq_info.irq_usage_count); -} - -static inline struct mic_driver *scdev_to_mdrv(struct scif_hw_dev *scdev) -{ - return dev_get_drvdata(scdev->dev.parent); -} - -static struct mic_irq * -___mic_request_irq(struct scif_hw_dev *scdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, - int db) -{ - return mic_request_card_irq(func, NULL, name, data, db); -} - -static void -___mic_free_irq(struct scif_hw_dev *scdev, - struct mic_irq *cookie, void *data) -{ - return mic_free_card_irq(cookie, data); -} - -static void ___mic_ack_interrupt(struct scif_hw_dev *scdev, int num) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - mic_ack_interrupt(&mdrv->mdev); -} - -static int ___mic_next_db(struct scif_hw_dev *scdev) -{ - return mic_next_card_db(); -} - -static void ___mic_send_intr(struct scif_hw_dev *scdev, int db) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - mic_send_intr(&mdrv->mdev, db); -} - -static void ___mic_send_p2p_intr(struct scif_hw_dev *scdev, int db, - struct mic_mw *mw) -{ - mic_send_p2p_intr(db, mw); -} - -static void __iomem * -___mic_ioremap(struct scif_hw_dev *scdev, - phys_addr_t pa, size_t len) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - return mic_card_map(&mdrv->mdev, pa, len); -} - -static void ___mic_iounmap(struct scif_hw_dev *scdev, void __iomem *va) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - mic_card_unmap(&mdrv->mdev, va); -} - -static struct scif_hw_ops scif_hw_ops = { - .request_irq = ___mic_request_irq, - .free_irq = ___mic_free_irq, - .ack_interrupt = ___mic_ack_interrupt, - .next_db = ___mic_next_db, - .send_intr = ___mic_send_intr, - .send_p2p_intr = ___mic_send_p2p_intr, - .remap = ___mic_ioremap, - .unmap = ___mic_iounmap, -}; - -static inline struct mic_driver *vpdev_to_mdrv(struct vop_device *vpdev) -{ - return dev_get_drvdata(vpdev->dev.parent); -} - -static struct mic_irq * -__mic_request_irq(struct vop_device *vpdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int intr_src) -{ - return mic_request_card_irq(func, NULL, name, data, intr_src); -} - -static void __mic_free_irq(struct vop_device *vpdev, - struct mic_irq *cookie, void *data) -{ - return mic_free_card_irq(cookie, data); -} - -static void __mic_ack_interrupt(struct vop_device *vpdev, int num) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - mic_ack_interrupt(&mdrv->mdev); -} - -static int __mic_next_db(struct vop_device *vpdev) -{ - return mic_next_card_db(); -} - -static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - return mdrv->dp; -} - -static void __mic_send_intr(struct vop_device *vpdev, int db) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - mic_send_intr(&mdrv->mdev, db); -} - -static void __iomem *__mic_ioremap(struct vop_device *vpdev, - dma_addr_t pa, size_t len) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - return mic_card_map(&mdrv->mdev, pa, len); -} - -static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - mic_card_unmap(&mdrv->mdev, va); -} - -static struct vop_hw_ops vop_hw_ops = { - .request_irq = __mic_request_irq, - .free_irq = __mic_free_irq, - .ack_interrupt = __mic_ack_interrupt, - .next_db = __mic_next_db, - .get_remote_dp = __mic_get_remote_dp, - .send_intr = __mic_send_intr, - .remap = __mic_ioremap, - .unmap = __mic_iounmap, -}; - -static int mic_request_dma_chans(struct mic_driver *mdrv) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - - do { - chan = dma_request_channel(mask, NULL, NULL); - if (chan) { - mdrv->dma_ch[mdrv->num_dma_ch++] = chan; - if (mdrv->num_dma_ch >= MIC_MAX_DMA_CHAN) - break; - } - } while (chan); - dev_info(mdrv->dev, "DMA channels # %d\n", mdrv->num_dma_ch); - return mdrv->num_dma_ch; -} - -static void mic_free_dma_chans(struct mic_driver *mdrv) -{ - int i = 0; - - for (i = 0; i < mdrv->num_dma_ch; i++) { - dma_release_channel(mdrv->dma_ch[i]); - mdrv->dma_ch[i] = NULL; - } - mdrv->num_dma_ch = 0; -} - -/* - * mic_driver_init - MIC driver initialization tasks. - * - * Returns 0 in success. Appropriate error code on failure. - */ -int __init mic_driver_init(struct mic_driver *mdrv) -{ - int rc; - struct mic_bootparam __iomem *bootparam; - u8 node_id; - - g_drv = mdrv; - /* Unloading the card module is not supported. */ - if (!try_module_get(mdrv->dev->driver->owner)) { - rc = -ENODEV; - goto done; - } - rc = mic_dp_init(); - if (rc) - goto put; - rc = mic_init_irq(); - if (rc) - goto dp_uninit; - if (!mic_request_dma_chans(mdrv)) { - rc = -ENODEV; - goto irq_uninit; - } - mdrv->vpdev = vop_register_device(mdrv->dev, VOP_DEV_TRNSP, - NULL, &vop_hw_ops, 0, - NULL, mdrv->dma_ch[0]); - if (IS_ERR(mdrv->vpdev)) { - rc = PTR_ERR(mdrv->vpdev); - goto dma_free; - } - bootparam = mdrv->dp; - node_id = ioread8(&bootparam->node_id); - mdrv->scdev = scif_register_device(mdrv->dev, MIC_SCIF_DEV, - NULL, &scif_hw_ops, - 0, node_id, &mdrv->mdev.mmio, NULL, - NULL, mdrv->dp, mdrv->dma_ch, - mdrv->num_dma_ch, true); - if (IS_ERR(mdrv->scdev)) { - rc = PTR_ERR(mdrv->scdev); - goto vop_remove; - } - mic_create_card_debug_dir(mdrv); -done: - return rc; -vop_remove: - vop_unregister_device(mdrv->vpdev); -dma_free: - mic_free_dma_chans(mdrv); -irq_uninit: - mic_uninit_irq(); -dp_uninit: - mic_dp_uninit(); -put: - module_put(mdrv->dev->driver->owner); - return rc; -} - -/* - * mic_driver_uninit - MIC driver uninitialization tasks. - * - * Returns None - */ -void mic_driver_uninit(struct mic_driver *mdrv) -{ - mic_delete_card_debug_dir(mdrv); - scif_unregister_device(mdrv->scdev); - vop_unregister_device(mdrv->vpdev); - mic_free_dma_chans(mdrv); - mic_uninit_irq(); - mic_dp_uninit(); - module_put(mdrv->dev->driver->owner); -} diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h deleted file mode 100644 index d6cc69a235a3..000000000000 --- a/drivers/misc/mic/card/mic_device.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#ifndef _MIC_CARD_DEVICE_H_ -#define _MIC_CARD_DEVICE_H_ - -#include <linux/workqueue.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/mic_bus.h> -#include "../bus/scif_bus.h" -#include "../bus/vop_bus.h" - -/** - * struct mic_intr_info - Contains h/w specific interrupt sources info - * - * @num_intr: The number of irqs available - */ -struct mic_intr_info { - u32 num_intr; -}; - -/** - * struct mic_irq_info - OS specific irq information - * - * @irq_usage_count: usage count array tracking the number of sources - * assigned for each irq. - */ -struct mic_irq_info { - int *irq_usage_count; -}; - -/** - * struct mic_device - MIC device information. - * - * @mmio: MMIO bar information. - */ -struct mic_device { - struct mic_mw mmio; -}; - -/** - * struct mic_driver - MIC card driver information. - * - * @name: Name for MIC driver. - * @dbg_dir: debugfs directory of this MIC device. - * @dev: The device backing this MIC. - * @dp: The pointer to the virtio device page. - * @mdev: MIC device information for the host. - * @hotplug_work: Hot plug work for adding/removing virtio devices. - * @irq_info: The OS specific irq information - * @intr_info: H/W specific interrupt information. - * @dma_mbdev: dma device on the MIC virtual bus. - * @dma_ch - Array of DMA channels - * @num_dma_ch - Number of DMA channels available - * @scdev: SCIF device on the SCIF virtual bus. - * @vpdev: Virtio over PCIe device on the VOP virtual bus. - */ -struct mic_driver { - char name[20]; - struct dentry *dbg_dir; - struct device *dev; - void __iomem *dp; - struct mic_device mdev; - struct work_struct hotplug_work; - struct mic_irq_info irq_info; - struct mic_intr_info intr_info; - struct mbus_device *dma_mbdev; - struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; - int num_dma_ch; - struct scif_hw_dev *scdev; - struct vop_device *vpdev; -}; - -/** - * struct mic_irq - opaque pointer used as cookie - */ -struct mic_irq; - -/** - * mic_mmio_read - read from an MMIO register. - * @mw: MMIO register base virtual address. - * @offset: register offset. - * - * RETURNS: register value. - */ -static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) -{ - return ioread32(mw->va + offset); -} - -/** - * mic_mmio_write - write to an MMIO register. - * @mw: MMIO register base virtual address. - * @val: the data value to put into the register - * @offset: register offset. - * - * RETURNS: none. - */ -static inline void -mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) -{ - iowrite32(val, mw->va + offset); -} - -int mic_driver_init(struct mic_driver *mdrv); -void mic_driver_uninit(struct mic_driver *mdrv); -int mic_next_card_db(void); -struct mic_irq * -mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int db); -void mic_free_card_irq(struct mic_irq *cookie, void *data); -u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); -void mic_send_intr(struct mic_device *mdev, int doorbell); -void mic_send_p2p_intr(int doorbell, struct mic_mw *mw); -int mic_db_to_irq(struct mic_driver *mdrv, int db); -u32 mic_ack_interrupt(struct mic_device *mdev); -void mic_hw_intr_init(struct mic_driver *mdrv); -void __iomem * -mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size); -void mic_card_unmap(struct mic_device *mdev, void __iomem *addr); -void __init mic_create_card_debug_dir(struct mic_driver *mdrv); -void mic_delete_card_debug_dir(struct mic_driver *mdrv); -void __init mic_init_card_debugfs(void); -void mic_exit_card_debugfs(void); -#endif diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c deleted file mode 100644 index c8bff2916d3d..000000000000 --- a/drivers/misc/mic/card/mic_x100.c +++ /dev/null @@ -1,347 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/platform_device.h> - -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_x100.h" - -static const char mic_driver_name[] = "mic"; - -static struct mic_driver g_drv; - -/** - * mic_read_spad - read from the scratchpad register - * @mdev: pointer to mic_device instance - * @idx: index to scratchpad register, 0 based - * - * This function allows reading of the 32bit scratchpad register. - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -u32 mic_read_spad(struct mic_device *mdev, unsigned int idx) -{ - return mic_mmio_read(&mdev->mmio, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); -} - -/** - * __mic_send_intr - Send interrupt to Host. - * @mdev: pointer to mic_device instance - * @doorbell: Doorbell number. - */ -void mic_send_intr(struct mic_device *mdev, int doorbell) -{ - struct mic_mw *mw = &mdev->mmio; - - if (doorbell > MIC_X100_MAX_DOORBELL_IDX) - return; - /* Ensure that the interrupt is ordered w.r.t previous stores. */ - wmb(); - mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, - MIC_X100_SBOX_BASE_ADDRESS + - (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); -} - -/* - * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. - */ -static void mic_x100_send_sbox_intr(struct mic_mw *mw, int doorbell) -{ - u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; - u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + - apic_icr_offset); - - /* for MIC we need to make sure we "hit" the send_icr bit (13) */ - apicicr_low = (apicicr_low | (1 << 13)); - /* - * Ensure that the interrupt is ordered w.r.t. previous stores - * to main memory. Fence instructions are not implemented in X100 - * since execution is in order but a compiler barrier is still - * required. - */ - wmb(); - mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); -} - -static void mic_x100_send_rdmasr_intr(struct mic_mw *mw, int doorbell) -{ - int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); - /* - * Ensure that the interrupt is ordered w.r.t. previous stores - * to main memory. Fence instructions are not implemented in X100 - * since execution is in order but a compiler barrier is still - * required. - */ - wmb(); - mic_mmio_write(mw, 0, MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); -} - -/** - * mic_ack_interrupt - Device specific interrupt handling. - * @mdev: pointer to mic_device instance - * - * Returns: bitmask of doorbell events triggered. - */ -u32 mic_ack_interrupt(struct mic_device *mdev) -{ - return 0; -} - -static inline int mic_get_sbox_irq(int db) -{ - return MIC_X100_IRQ_BASE + db; -} - -static inline int mic_get_rdmasr_irq(int index) -{ - return MIC_X100_RDMASR_IRQ_BASE + index; -} - -void mic_send_p2p_intr(int db, struct mic_mw *mw) -{ - int rdmasr_index; - - if (db < MIC_X100_NUM_SBOX_IRQ) { - mic_x100_send_sbox_intr(mw, db); - } else { - rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ; - mic_x100_send_rdmasr_intr(mw, rdmasr_index); - } -} - -/** - * mic_hw_intr_init - Initialize h/w specific interrupt - * information. - * @mdrv: pointer to mic_driver - */ -void mic_hw_intr_init(struct mic_driver *mdrv) -{ - mdrv->intr_info.num_intr = MIC_X100_NUM_SBOX_IRQ + - MIC_X100_NUM_RDMASR_IRQ; -} - -/** - * mic_db_to_irq - Retrieve irq number corresponding to a doorbell. - * @mdrv: pointer to mic_driver - * @db: The doorbell obtained for which the irq is needed. Doorbell - * may correspond to an sbox doorbell or an rdmasr index. - * - * Returns the irq corresponding to the doorbell. - */ -int mic_db_to_irq(struct mic_driver *mdrv, int db) -{ - int rdmasr_index; - - /* - * The total number of doorbell interrupts on the card are 16. Indices - * 0-8 falls in the SBOX category and 8-15 fall in the RDMASR category. - */ - if (db < MIC_X100_NUM_SBOX_IRQ) { - return mic_get_sbox_irq(db); - } else { - rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ; - return mic_get_rdmasr_irq(rdmasr_index); - } -} - -/* - * mic_card_map - Allocate virtual address for a remote memory region. - * @mdev: pointer to mic_device instance. - * @addr: Remote DMA address. - * @size: Size of the region. - * - * Returns: Virtual address backing the remote memory region. - */ -void __iomem * -mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size) -{ - return ioremap(addr, size); -} - -/* - * mic_card_unmap - Unmap the virtual address for a remote memory region. - * @mdev: pointer to mic_device instance. - * @addr: Virtual address for remote memory region. - * - * Returns: None. - */ -void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) -{ - iounmap(addr); -} - -static inline struct mic_driver *mbdev_to_mdrv(struct mbus_device *mbdev) -{ - return dev_get_drvdata(mbdev->dev.parent); -} - -static struct mic_irq * -_mic_request_threaded_irq(struct mbus_device *mbdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src) -{ - int rc = 0; - unsigned int irq = intr_src; - unsigned long cookie = irq; - - rc = request_threaded_irq(irq, handler, thread_fn, 0, name, data); - if (rc) { - dev_err(mbdev_to_mdrv(mbdev)->dev, - "request_threaded_irq failed rc = %d\n", rc); - return ERR_PTR(rc); - } - return (struct mic_irq *)cookie; -} - -static void _mic_free_irq(struct mbus_device *mbdev, - struct mic_irq *cookie, void *data) -{ - unsigned long irq = (unsigned long)cookie; - free_irq(irq, data); -} - -static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) -{ - mic_ack_interrupt(&mbdev_to_mdrv(mbdev)->mdev); -} - -static struct mbus_hw_ops mbus_hw_ops = { - .request_threaded_irq = _mic_request_threaded_irq, - .free_irq = _mic_free_irq, - .ack_interrupt = _mic_ack_interrupt, -}; - -static int __init mic_probe(struct platform_device *pdev) -{ - struct mic_driver *mdrv = &g_drv; - struct mic_device *mdev = &mdrv->mdev; - int rc = 0; - - mdrv->dev = &pdev->dev; - snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name); - - /* FIXME: use dma_set_mask_and_coherent() and check result */ - dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - - mdev->mmio.pa = MIC_X100_MMIO_BASE; - mdev->mmio.len = MIC_X100_MMIO_LEN; - mdev->mmio.va = devm_ioremap(&pdev->dev, MIC_X100_MMIO_BASE, - MIC_X100_MMIO_LEN); - if (!mdev->mmio.va) { - dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); - rc = -EIO; - goto done; - } - mic_hw_intr_init(mdrv); - platform_set_drvdata(pdev, mdrv); - mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC, - NULL, &mbus_hw_ops, 0, - mdrv->mdev.mmio.va); - if (IS_ERR(mdrv->dma_mbdev)) { - rc = PTR_ERR(mdrv->dma_mbdev); - dev_err(&pdev->dev, "mbus_add_device failed rc %d\n", rc); - goto done; - } - rc = mic_driver_init(mdrv); - if (rc) { - dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); - goto remove_dma; - } -done: - return rc; -remove_dma: - mbus_unregister_device(mdrv->dma_mbdev); - return rc; -} - -static int mic_remove(struct platform_device *pdev) -{ - struct mic_driver *mdrv = &g_drv; - - mic_driver_uninit(mdrv); - mbus_unregister_device(mdrv->dma_mbdev); - return 0; -} - -static void mic_platform_shutdown(struct platform_device *pdev) -{ - mic_remove(pdev); -} - -static struct platform_driver __refdata mic_platform_driver = { - .probe = mic_probe, - .remove = mic_remove, - .shutdown = mic_platform_shutdown, - .driver = { - .name = mic_driver_name, - }, -}; - -static struct platform_device *mic_platform_dev; - -static int __init mic_init(void) -{ - int ret; - struct cpuinfo_x86 *c = &cpu_data(0); - - if (!(c->x86 == 11 && c->x86_model == 1)) { - ret = -ENODEV; - pr_err("%s not running on X100 ret %d\n", __func__, ret); - goto done; - } - - request_module("mic_x100_dma"); - mic_init_card_debugfs(); - - mic_platform_dev = platform_device_register_simple(mic_driver_name, - 0, NULL, 0); - ret = PTR_ERR_OR_ZERO(mic_platform_dev); - if (ret) { - pr_err("platform_device_register_full ret %d\n", ret); - goto cleanup_debugfs; - } - ret = platform_driver_register(&mic_platform_driver); - if (ret) { - pr_err("platform_driver_register ret %d\n", ret); - goto device_unregister; - } - return ret; - -device_unregister: - platform_device_unregister(mic_platform_dev); -cleanup_debugfs: - mic_exit_card_debugfs(); -done: - return ret; -} - -static void __exit mic_exit(void) -{ - platform_driver_unregister(&mic_platform_driver); - platform_device_unregister(mic_platform_dev); - mic_exit_card_debugfs(); -} - -module_init(mic_init); -module_exit(mic_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC X100 Card driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/card/mic_x100.h b/drivers/misc/mic/card/mic_x100.h deleted file mode 100644 index 46644dde0c07..000000000000 --- a/drivers/misc/mic/card/mic_x100.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#ifndef _MIC_X100_CARD_H_ -#define _MIC_X100_CARD_H_ - -#define MIC_X100_MMIO_BASE 0x08007C0000ULL -#define MIC_X100_MMIO_LEN 0x00020000ULL -#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000ULL - -#define MIC_X100_SBOX_SPAD0 0x0000AB20 -#define MIC_X100_SBOX_SDBIC0 0x0000CC90 -#define MIC_X100_SBOX_SDBIC0_DBREQ_BIT 0x80000000 -#define MIC_X100_SBOX_RDMASR0 0x0000B180 -#define MIC_X100_SBOX_APICICR0 0x0000A9D0 - -#define MIC_X100_MAX_DOORBELL_IDX 8 - -#define MIC_X100_NUM_SBOX_IRQ 8 -#define MIC_X100_NUM_RDMASR_IRQ 8 -#define MIC_X100_SBOX_IRQ_BASE 0 -#define MIC_X100_RDMASR_IRQ_BASE 17 - -#define MIC_X100_IRQ_BASE 26 - -#endif diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h deleted file mode 100644 index f94f08df0260..000000000000 --- a/drivers/misc/mic/common/mic_dev.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC driver. - */ -#ifndef __MIC_DEV_H__ -#define __MIC_DEV_H__ - -/* The maximum number of MIC devices supported in a single host system. */ -#define MIC_MAX_NUM_DEVS 128 - -/** - * enum mic_hw_family - The hardware family to which a device belongs. - */ -enum mic_hw_family { - MIC_FAMILY_X100 = 0, - MIC_FAMILY_X200, - MIC_FAMILY_UNKNOWN, - MIC_FAMILY_LAST -}; - -/** - * struct mic_mw - MIC memory window - * - * @pa: Base physical address. - * @va: Base ioremap'd virtual address. - * @len: Size of the memory window. - */ -struct mic_mw { - phys_addr_t pa; - void __iomem *va; - resource_size_t len; -}; - -/* - * Scratch pad register offsets used by the host to communicate - * device page DMA address to the card. - */ -#define MIC_DPLO_SPAD 14 -#define MIC_DPHI_SPAD 15 - -/* - * These values are supposed to be in the config_change field of the - * device page when the host sends a config change interrupt to the card. - */ -#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 -#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 - -/* Maximum number of DMA channels */ -#define MIC_MAX_DMA_CHAN 4 - -#endif diff --git a/drivers/misc/mic/cosm/Makefile b/drivers/misc/mic/cosm/Makefile deleted file mode 100644 index 97d74cb12030..000000000000 --- a/drivers/misc/mic/cosm/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Coprocessor State Management (COSM) Driver -# Copyright(c) 2015, Intel Corporation. -# -obj-$(CONFIG_MIC_COSM) += mic_cosm.o - -mic_cosm-objs := cosm_main.o -mic_cosm-objs += cosm_debugfs.o -mic_cosm-objs += cosm_sysfs.o -mic_cosm-objs += cosm_scif_server.o diff --git a/drivers/misc/mic/cosm/cosm_debugfs.c b/drivers/misc/mic/cosm/cosm_debugfs.c deleted file mode 100644 index cb55653cf1f9..000000000000 --- a/drivers/misc/mic/cosm/cosm_debugfs.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ - -#include <linux/debugfs.h> -#include <linux/slab.h> -#include <linux/io.h> -#include "cosm_main.h" - -/* Debugfs parent dir */ -static struct dentry *cosm_dbg; - -/* - * log_buf_show - Display MIC kernel log buffer - * - * log_buf addr/len is read from System.map by user space - * and populated in sysfs entries. - */ -static int log_buf_show(struct seq_file *s, void *unused) -{ - void __iomem *log_buf_va; - int __iomem *log_buf_len_va; - struct cosm_device *cdev = s->private; - void *kva; - int size; - u64 aper_offset; - - if (!cdev || !cdev->log_buf_addr || !cdev->log_buf_len) - goto done; - - mutex_lock(&cdev->cosm_mutex); - switch (cdev->state) { - case MIC_BOOTING: - case MIC_ONLINE: - case MIC_SHUTTING_DOWN: - break; - default: - goto unlock; - } - - /* - * Card kernel will never be relocated and any kernel text/data mapping - * can be translated to phys address by subtracting __START_KERNEL_map. - */ - aper_offset = (u64)cdev->log_buf_len - __START_KERNEL_map; - log_buf_len_va = cdev->hw_ops->aper(cdev)->va + aper_offset; - aper_offset = (u64)cdev->log_buf_addr - __START_KERNEL_map; - log_buf_va = cdev->hw_ops->aper(cdev)->va + aper_offset; - - size = ioread32(log_buf_len_va); - kva = kmalloc(size, GFP_KERNEL); - if (!kva) - goto unlock; - - memcpy_fromio(kva, log_buf_va, size); - seq_write(s, kva, size); - kfree(kva); -unlock: - mutex_unlock(&cdev->cosm_mutex); -done: - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(log_buf); - -/* - * force_reset_show - Force MIC reset - * - * Invokes the force_reset COSM bus op instead of the standard reset - * op in case a force reset of the MIC device is required - */ -static int force_reset_show(struct seq_file *s, void *pos) -{ - struct cosm_device *cdev = s->private; - - cosm_stop(cdev, true); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(force_reset); - -void cosm_create_debug_dir(struct cosm_device *cdev) -{ - char name[16]; - - if (!cosm_dbg) - return; - - scnprintf(name, sizeof(name), "mic%d", cdev->index); - cdev->dbg_dir = debugfs_create_dir(name, cosm_dbg); - - debugfs_create_file("log_buf", 0444, cdev->dbg_dir, cdev, - &log_buf_fops); - debugfs_create_file("force_reset", 0444, cdev->dbg_dir, cdev, - &force_reset_fops); -} - -void cosm_delete_debug_dir(struct cosm_device *cdev) -{ - debugfs_remove_recursive(cdev->dbg_dir); -} - -void cosm_init_debugfs(void) -{ - cosm_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); -} - -void cosm_exit_debugfs(void) -{ - debugfs_remove(cosm_dbg); -} diff --git a/drivers/misc/mic/cosm/cosm_main.c b/drivers/misc/mic/cosm/cosm_main.c deleted file mode 100644 index ebb0eac43754..000000000000 --- a/drivers/misc/mic/cosm/cosm_main.c +++ /dev/null @@ -1,382 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/idr.h> -#include <linux/slab.h> -#include <linux/cred.h> -#include "cosm_main.h" - -static const char cosm_driver_name[] = "mic"; - -/* COSM ID allocator */ -static struct ida g_cosm_ida; -/* Class of MIC devices for sysfs accessibility. */ -static struct class *g_cosm_class; -/* Number of MIC devices */ -static atomic_t g_num_dev; - -/** - * cosm_hw_reset - Issue a HW reset for the MIC device - * @cdev: pointer to cosm_device instance - * @force: force a MIC to reset even if it is already reset and ready - */ -static void cosm_hw_reset(struct cosm_device *cdev, bool force) -{ - int i; - -#define MIC_RESET_TO (45) - if (force && cdev->hw_ops->force_reset) - cdev->hw_ops->force_reset(cdev); - else - cdev->hw_ops->reset(cdev); - - for (i = 0; i < MIC_RESET_TO; i++) { - if (cdev->hw_ops->ready(cdev)) { - cosm_set_state(cdev, MIC_READY); - return; - } - /* - * Resets typically take 10s of seconds to complete. - * Since an MMIO read is required to check if the - * firmware is ready or not, a 1 second delay works nicely. - */ - msleep(1000); - } - cosm_set_state(cdev, MIC_RESET_FAILED); -} - -/** - * cosm_start - Start the MIC - * @cdev: pointer to cosm_device instance - * - * This function prepares an MIC for boot and initiates boot. - * RETURNS: An appropriate -ERRNO error value on error, or 0 for success. - */ -int cosm_start(struct cosm_device *cdev) -{ - const struct cred *orig_cred; - struct cred *override_cred; - int rc; - - mutex_lock(&cdev->cosm_mutex); - if (!cdev->bootmode) { - dev_err(&cdev->dev, "%s %d bootmode not set\n", - __func__, __LINE__); - rc = -EINVAL; - goto unlock_ret; - } -retry: - if (cdev->state != MIC_READY) { - dev_err(&cdev->dev, "%s %d MIC state not READY\n", - __func__, __LINE__); - rc = -EINVAL; - goto unlock_ret; - } - if (!cdev->hw_ops->ready(cdev)) { - cosm_hw_reset(cdev, false); - /* - * The state will either be MIC_READY if the reset succeeded - * or MIC_RESET_FAILED if the firmware reset failed. - */ - goto retry; - } - - /* - * Set credentials to root to allow non-root user to download initramsfs - * with 600 permissions - */ - override_cred = prepare_creds(); - if (!override_cred) { - dev_err(&cdev->dev, "%s %d prepare_creds failed\n", - __func__, __LINE__); - rc = -ENOMEM; - goto unlock_ret; - } - override_cred->fsuid = GLOBAL_ROOT_UID; - orig_cred = override_creds(override_cred); - - rc = cdev->hw_ops->start(cdev, cdev->index); - - revert_creds(orig_cred); - put_cred(override_cred); - if (rc) - goto unlock_ret; - - /* - * If linux is being booted, card is treated 'online' only - * when the scif interface in the card is up. If anything else - * is booted, we set card to 'online' immediately. - */ - if (!strcmp(cdev->bootmode, "linux")) - cosm_set_state(cdev, MIC_BOOTING); - else - cosm_set_state(cdev, MIC_ONLINE); -unlock_ret: - mutex_unlock(&cdev->cosm_mutex); - if (rc) - dev_err(&cdev->dev, "cosm_start failed rc %d\n", rc); - return rc; -} - -/** - * cosm_stop - Prepare the MIC for reset and trigger reset - * @cdev: pointer to cosm_device instance - * @force: force a MIC to reset even if it is already reset and ready. - * - * RETURNS: None - */ -void cosm_stop(struct cosm_device *cdev, bool force) -{ - mutex_lock(&cdev->cosm_mutex); - if (cdev->state != MIC_READY || force) { - /* - * Don't call hw_ops if they have been called previously. - * stop(..) calls device_unregister and will crash the system if - * called multiple times. - */ - u8 state = cdev->state == MIC_RESETTING ? - cdev->prev_state : cdev->state; - bool call_hw_ops = state != MIC_RESET_FAILED && - state != MIC_READY; - - if (cdev->state != MIC_RESETTING) - cosm_set_state(cdev, MIC_RESETTING); - cdev->heartbeat_watchdog_enable = false; - if (call_hw_ops) - cdev->hw_ops->stop(cdev, force); - cosm_hw_reset(cdev, force); - cosm_set_shutdown_status(cdev, MIC_NOP); - if (call_hw_ops && cdev->hw_ops->post_reset) - cdev->hw_ops->post_reset(cdev, cdev->state); - } - mutex_unlock(&cdev->cosm_mutex); - flush_work(&cdev->scif_work); -} - -/** - * cosm_reset_trigger_work - Trigger MIC reset - * @work: The work structure - * - * This work is scheduled whenever the host wants to reset the MIC. - */ -static void cosm_reset_trigger_work(struct work_struct *work) -{ - struct cosm_device *cdev = container_of(work, struct cosm_device, - reset_trigger_work); - cosm_stop(cdev, false); -} - -/** - * cosm_reset - Schedule MIC reset - * @cdev: pointer to cosm_device instance - * - * RETURNS: An -EINVAL if the card is already READY or 0 for success. - */ -int cosm_reset(struct cosm_device *cdev) -{ - int rc = 0; - - mutex_lock(&cdev->cosm_mutex); - if (cdev->state != MIC_READY) { - if (cdev->state != MIC_RESETTING) { - cdev->prev_state = cdev->state; - cosm_set_state(cdev, MIC_RESETTING); - schedule_work(&cdev->reset_trigger_work); - } - } else { - dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__); - rc = -EINVAL; - } - mutex_unlock(&cdev->cosm_mutex); - return rc; -} - -/** - * cosm_shutdown - Initiate MIC shutdown. - * @cdev: pointer to cosm_device instance - * - * RETURNS: None - */ -int cosm_shutdown(struct cosm_device *cdev) -{ - struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN }; - int rc = 0; - - mutex_lock(&cdev->cosm_mutex); - if (cdev->state != MIC_ONLINE) { - rc = -EINVAL; - dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n", - __func__, __LINE__, cosm_state_string[cdev->state]); - goto err; - } - - if (!cdev->epd) { - rc = -ENOTCONN; - dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n", - __func__, __LINE__, rc); - goto err; - } - - rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) { - dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n", - __func__, __LINE__, rc); - goto err; - } - cdev->heartbeat_watchdog_enable = false; - cosm_set_state(cdev, MIC_SHUTTING_DOWN); - rc = 0; -err: - mutex_unlock(&cdev->cosm_mutex); - return rc; -} - -static int cosm_driver_probe(struct cosm_device *cdev) -{ - int rc; - - /* Initialize SCIF server at first probe */ - if (atomic_add_return(1, &g_num_dev) == 1) { - rc = cosm_scif_init(); - if (rc) - goto scif_exit; - } - mutex_init(&cdev->cosm_mutex); - INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work); - INIT_WORK(&cdev->scif_work, cosm_scif_work); - cdev->sysfs_heartbeat_enable = true; - cosm_sysfs_init(cdev); - cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent, - MKDEV(0, cdev->index), cdev, cdev->attr_group, - "mic%d", cdev->index); - if (IS_ERR(cdev->sdev)) { - rc = PTR_ERR(cdev->sdev); - dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n", - rc); - goto scif_exit; - } - - cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd, - "state"); - if (!cdev->state_sysfs) { - rc = -ENODEV; - dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc); - goto destroy_device; - } - cosm_create_debug_dir(cdev); - return 0; -destroy_device: - device_destroy(g_cosm_class, MKDEV(0, cdev->index)); -scif_exit: - if (atomic_dec_and_test(&g_num_dev)) - cosm_scif_exit(); - return rc; -} - -static void cosm_driver_remove(struct cosm_device *cdev) -{ - cosm_delete_debug_dir(cdev); - sysfs_put(cdev->state_sysfs); - device_destroy(g_cosm_class, MKDEV(0, cdev->index)); - flush_work(&cdev->reset_trigger_work); - cosm_stop(cdev, false); - if (atomic_dec_and_test(&g_num_dev)) - cosm_scif_exit(); - - /* These sysfs entries might have allocated */ - kfree(cdev->cmdline); - kfree(cdev->firmware); - kfree(cdev->ramdisk); - kfree(cdev->bootmode); -} - -static int cosm_suspend(struct device *dev) -{ - struct cosm_device *cdev = dev_to_cosm(dev); - - mutex_lock(&cdev->cosm_mutex); - switch (cdev->state) { - /** - * Suspend/freeze hooks in userspace have already shutdown the card. - * Card should be 'ready' in most cases. It is however possible that - * some userspace application initiated a boot. In those cases, we - * simply reset the card. - */ - case MIC_ONLINE: - case MIC_BOOTING: - case MIC_SHUTTING_DOWN: - mutex_unlock(&cdev->cosm_mutex); - cosm_stop(cdev, false); - break; - default: - mutex_unlock(&cdev->cosm_mutex); - break; - } - return 0; -} - -static const struct dev_pm_ops cosm_pm_ops = { - .suspend = cosm_suspend, - .freeze = cosm_suspend -}; - -static struct cosm_driver cosm_driver = { - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - .pm = &cosm_pm_ops, - }, - .probe = cosm_driver_probe, - .remove = cosm_driver_remove -}; - -static int __init cosm_init(void) -{ - int ret; - - cosm_init_debugfs(); - - g_cosm_class = class_create(THIS_MODULE, cosm_driver_name); - if (IS_ERR(g_cosm_class)) { - ret = PTR_ERR(g_cosm_class); - pr_err("class_create failed ret %d\n", ret); - goto cleanup_debugfs; - } - - ida_init(&g_cosm_ida); - ret = cosm_register_driver(&cosm_driver); - if (ret) { - pr_err("cosm_register_driver failed ret %d\n", ret); - goto ida_destroy; - } - return 0; -ida_destroy: - ida_destroy(&g_cosm_ida); - class_destroy(g_cosm_class); -cleanup_debugfs: - cosm_exit_debugfs(); - return ret; -} - -static void __exit cosm_exit(void) -{ - cosm_unregister_driver(&cosm_driver); - ida_destroy(&g_cosm_ida); - class_destroy(g_cosm_class); - cosm_exit_debugfs(); -} - -module_init(cosm_init); -module_exit(cosm_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/cosm/cosm_main.h b/drivers/misc/mic/cosm/cosm_main.h deleted file mode 100644 index 5188ad245814..000000000000 --- a/drivers/misc/mic/cosm/cosm_main.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ -#ifndef _COSM_COSM_H_ -#define _COSM_COSM_H_ - -#include <linux/scif.h> -#include "../bus/cosm_bus.h" - -#define COSM_HEARTBEAT_SEND_SEC 30 -#define SCIF_COSM_LISTEN_PORT 201 - -/** - * enum COSM msg id's - * @COSM_MSG_SHUTDOWN: host->card trigger shutdown - * @COSM_MSG_SYNC_TIME: host->card send host time to card to sync time - * @COSM_MSG_HEARTBEAT: card->host heartbeat - * @COSM_MSG_SHUTDOWN_STATUS: card->host with shutdown status as payload - */ -enum cosm_msg_id { - COSM_MSG_SHUTDOWN, - COSM_MSG_SYNC_TIME, - COSM_MSG_HEARTBEAT, - COSM_MSG_SHUTDOWN_STATUS, -}; - -struct cosm_msg { - u64 id; - union { - u64 shutdown_status; - struct { - u64 tv_sec; - u64 tv_nsec; - } timespec; - }; -}; - -extern const char * const cosm_state_string[]; -extern const char * const cosm_shutdown_status_string[]; - -void cosm_sysfs_init(struct cosm_device *cdev); -int cosm_start(struct cosm_device *cdev); -void cosm_stop(struct cosm_device *cdev, bool force); -int cosm_reset(struct cosm_device *cdev); -int cosm_shutdown(struct cosm_device *cdev); -void cosm_set_state(struct cosm_device *cdev, u8 state); -void cosm_set_shutdown_status(struct cosm_device *cdev, u8 status); -void cosm_init_debugfs(void); -void cosm_exit_debugfs(void); -void cosm_create_debug_dir(struct cosm_device *cdev); -void cosm_delete_debug_dir(struct cosm_device *cdev); -int cosm_scif_init(void); -void cosm_scif_exit(void); -void cosm_scif_work(struct work_struct *work); - -#endif diff --git a/drivers/misc/mic/cosm/cosm_scif_server.c b/drivers/misc/mic/cosm/cosm_scif_server.c deleted file mode 100644 index 7baec9fd8756..000000000000 --- a/drivers/misc/mic/cosm/cosm_scif_server.c +++ /dev/null @@ -1,399 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ -#include <linux/kthread.h> -#include <linux/sched/signal.h> - -#include "cosm_main.h" - -/* - * The COSM driver uses SCIF to communicate between the management node and the - * MIC cards. SCIF is used to (a) Send a shutdown command to the card (b) - * receive a shutdown status back from the card upon completion of shutdown and - * (c) receive periodic heartbeat messages from the card used to deduce if the - * card has crashed. - * - * A COSM server consisting of a SCIF listening endpoint waits for incoming - * connections from the card. Upon acceptance of the connection, a separate - * work-item is scheduled to handle SCIF message processing for that card. The - * life-time of this work-item is therefore the time from which the connection - * from a card is accepted to the time at which the connection is closed. A new - * work-item starts each time the card boots and is alive till the card (a) - * shuts down (b) is reset (c) crashes (d) cosm_client driver on the card is - * unloaded. - * - * From the point of view of COSM interactions with SCIF during card - * shutdown, reset and crash are as follows: - * - * Card shutdown - * ------------- - * 1. COSM client on the card invokes orderly_poweroff() in response to SHUTDOWN - * message from the host. - * 2. Card driver shutdown callback invokes scif_unregister_device(..) resulting - * in scif_remove(..) getting called on the card - * 3. scif_remove -> scif_stop -> scif_handle_remove_node -> - * scif_peer_unregister_device -> device_unregister for the host peer device - * 4. During device_unregister remove(..) method of cosm_client is invoked which - * closes the COSM SCIF endpoint on the card. This results in a SCIF_DISCNCT - * message being sent to host SCIF. SCIF_DISCNCT message processing on the - * host SCIF sets the host COSM SCIF endpoint state to DISCONNECTED and wakes - * up the host COSM thread blocked in scif_poll(..) resulting in - * scif_poll(..) returning EPOLLHUP. - * 5. On the card, scif_peer_release_dev is next called which results in an - * SCIF_EXIT message being sent to the host and after receiving the - * SCIF_EXIT_ACK from the host the peer device teardown on the card is - * complete. - * 6. As part of the SCIF_EXIT message processing on the host, host sends a - * SCIF_REMOVE_NODE to itself corresponding to the card being removed. This - * starts a similar SCIF peer device teardown sequence on the host - * corresponding to the card being shut down. - * - * Card reset - * ---------- - * The case of interest here is when the card has not been previously shut down - * since most of the steps below are skipped in that case: - - * 1. cosm_stop(..) invokes hw_ops->stop(..) method of the base PCIe driver - * which unregisters the SCIF HW device resulting in scif_remove(..) being - * called on the host. - * 2. scif_remove(..) calls scif_disconnect_node(..) which results in a - * SCIF_EXIT message being sent to the card. - * 3. The card executes scif_stop() as part of SCIF_EXIT message - * processing. This results in the COSM endpoint on the card being closed and - * the SCIF host peer device on the card getting unregistered similar to - * steps 3, 4 and 5 for the card shutdown case above. scif_poll(..) on the - * host returns EPOLLHUP as a result. - * 4. On the host, card peer device unregister and SCIF HW remove(..) also - * subsequently complete. - * - * Card crash - * ---------- - * If a reset is issued after the card has crashed, there is no SCIF_DISCNT - * message from the card which would result in scif_poll(..) returning - * EPOLLHUP. In this case when the host SCIF driver sends a SCIF_REMOVE_NODE - * message to itself resulting in the card SCIF peer device being unregistered, - * this results in a scif_peer_release_dev -> scif_cleanup_scifdev-> - * scif_invalidate_ep call sequence which sets the endpoint state to - * DISCONNECTED and results in scif_poll(..) returning EPOLLHUP. - */ - -#define COSM_SCIF_BACKLOG 16 -#define COSM_HEARTBEAT_CHECK_DELTA_SEC 10 -#define COSM_HEARTBEAT_TIMEOUT_SEC \ - (COSM_HEARTBEAT_SEND_SEC + COSM_HEARTBEAT_CHECK_DELTA_SEC) -#define COSM_HEARTBEAT_TIMEOUT_MSEC (COSM_HEARTBEAT_TIMEOUT_SEC * MSEC_PER_SEC) - -static struct task_struct *server_thread; -static scif_epd_t listen_epd; - -/* Publish MIC card's shutdown status to user space MIC daemon */ -static void cosm_update_mic_status(struct cosm_device *cdev) -{ - if (cdev->shutdown_status_int != MIC_NOP) { - cosm_set_shutdown_status(cdev, cdev->shutdown_status_int); - cdev->shutdown_status_int = MIC_NOP; - } -} - -/* Store MIC card's shutdown status internally when it is received */ -static void cosm_shutdown_status_int(struct cosm_device *cdev, - enum mic_status shutdown_status) -{ - switch (shutdown_status) { - case MIC_HALTED: - case MIC_POWER_OFF: - case MIC_RESTART: - case MIC_CRASHED: - break; - default: - dev_err(&cdev->dev, "%s %d Unexpected shutdown_status %d\n", - __func__, __LINE__, shutdown_status); - return; - }; - cdev->shutdown_status_int = shutdown_status; - cdev->heartbeat_watchdog_enable = false; - - if (cdev->state != MIC_SHUTTING_DOWN) - cosm_set_state(cdev, MIC_SHUTTING_DOWN); -} - -/* Non-blocking recv. Read and process all available messages */ -static void cosm_scif_recv(struct cosm_device *cdev) -{ - struct cosm_msg msg; - int rc; - - while (1) { - rc = scif_recv(cdev->epd, &msg, sizeof(msg), 0); - if (!rc) { - break; - } else if (rc < 0) { - dev_dbg(&cdev->dev, "%s: %d rc %d\n", - __func__, __LINE__, rc); - break; - } - dev_dbg(&cdev->dev, "%s: %d rc %d id 0x%llx\n", - __func__, __LINE__, rc, msg.id); - - switch (msg.id) { - case COSM_MSG_SHUTDOWN_STATUS: - cosm_shutdown_status_int(cdev, msg.shutdown_status); - break; - case COSM_MSG_HEARTBEAT: - /* Nothing to do, heartbeat only unblocks scif_poll */ - break; - default: - dev_err(&cdev->dev, "%s: %d unknown msg.id %lld\n", - __func__, __LINE__, msg.id); - break; - } - } -} - -/* Publish crashed status for this MIC card */ -static void cosm_set_crashed(struct cosm_device *cdev) -{ - dev_err(&cdev->dev, "node alive timeout\n"); - cosm_shutdown_status_int(cdev, MIC_CRASHED); - cosm_update_mic_status(cdev); -} - -/* Send host time to the MIC card to sync system time between host and MIC */ -static void cosm_send_time(struct cosm_device *cdev) -{ - struct cosm_msg msg = { .id = COSM_MSG_SYNC_TIME }; - struct timespec64 ts; - int rc; - - ktime_get_real_ts64(&ts); - msg.timespec.tv_sec = ts.tv_sec; - msg.timespec.tv_nsec = ts.tv_nsec; - - rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) - dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n", - __func__, __LINE__, rc); -} - -/* - * Close this cosm_device's endpoint after its peer endpoint on the card has - * been closed. In all cases except MIC card crash EPOLLHUP on the host is - * triggered by the client's endpoint being closed. - */ -static void cosm_scif_close(struct cosm_device *cdev) -{ - /* - * Because SHUTDOWN_STATUS message is sent by the MIC cards in the - * reboot notifier when shutdown is still not complete, we notify mpssd - * to reset the card when SCIF endpoint is closed. - */ - cosm_update_mic_status(cdev); - scif_close(cdev->epd); - cdev->epd = NULL; - dev_dbg(&cdev->dev, "%s %d\n", __func__, __LINE__); -} - -/* - * Set card state to ONLINE when a new SCIF connection from a MIC card is - * received. Normally the state is BOOTING when the connection comes in, but can - * be ONLINE if cosm_client driver on the card was unloaded and then reloaded. - */ -static int cosm_set_online(struct cosm_device *cdev) -{ - int rc = 0; - - if (MIC_BOOTING == cdev->state || MIC_ONLINE == cdev->state) { - cdev->heartbeat_watchdog_enable = cdev->sysfs_heartbeat_enable; - cdev->epd = cdev->newepd; - if (cdev->state == MIC_BOOTING) - cosm_set_state(cdev, MIC_ONLINE); - cosm_send_time(cdev); - dev_dbg(&cdev->dev, "%s %d\n", __func__, __LINE__); - } else { - dev_warn(&cdev->dev, "%s %d not going online in state: %s\n", - __func__, __LINE__, cosm_state_string[cdev->state]); - rc = -EINVAL; - } - /* Drop reference acquired by bus_find_device in the server thread */ - put_device(&cdev->dev); - return rc; -} - -/* - * Work function for handling work for a SCIF connection from a particular MIC - * card. It first sets the card state to ONLINE and then calls scif_poll to - * block on activity such as incoming messages on the SCIF endpoint. When the - * endpoint is closed, the work function exits, completing its life cycle, from - * MIC card boot to card shutdown/reset/crash. - */ -void cosm_scif_work(struct work_struct *work) -{ - struct cosm_device *cdev = container_of(work, struct cosm_device, - scif_work); - struct scif_pollepd pollepd; - int rc; - - mutex_lock(&cdev->cosm_mutex); - if (cosm_set_online(cdev)) - goto exit; - - while (1) { - pollepd.epd = cdev->epd; - pollepd.events = EPOLLIN; - - /* Drop the mutex before blocking in scif_poll(..) */ - mutex_unlock(&cdev->cosm_mutex); - /* poll(..) with timeout on our endpoint */ - rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_TIMEOUT_MSEC); - mutex_lock(&cdev->cosm_mutex); - if (rc < 0) { - dev_err(&cdev->dev, "%s %d scif_poll rc %d\n", - __func__, __LINE__, rc); - continue; - } - - /* There is a message from the card */ - if (pollepd.revents & EPOLLIN) - cosm_scif_recv(cdev); - - /* The peer endpoint is closed or this endpoint disconnected */ - if (pollepd.revents & EPOLLHUP) { - cosm_scif_close(cdev); - break; - } - - /* Did we timeout from poll? */ - if (!rc && cdev->heartbeat_watchdog_enable) - cosm_set_crashed(cdev); - } -exit: - dev_dbg(&cdev->dev, "%s %d exiting\n", __func__, __LINE__); - mutex_unlock(&cdev->cosm_mutex); -} - -/* - * COSM SCIF server thread function. Accepts incoming SCIF connections from MIC - * cards, finds the correct cosm_device to associate that connection with and - * schedules individual work items for each MIC card. - */ -static int cosm_scif_server(void *unused) -{ - struct cosm_device *cdev; - scif_epd_t newepd; - struct scif_port_id port_id; - int rc; - - allow_signal(SIGKILL); - - while (!kthread_should_stop()) { - rc = scif_accept(listen_epd, &port_id, &newepd, - SCIF_ACCEPT_SYNC); - if (rc < 0) { - if (-ERESTARTSYS != rc) - pr_err("%s %d rc %d\n", __func__, __LINE__, rc); - continue; - } - - /* - * Associate the incoming connection with a particular - * cosm_device, COSM device ID == SCIF node ID - 1 - */ - cdev = cosm_find_cdev_by_id(port_id.node - 1); - if (!cdev) - continue; - cdev->newepd = newepd; - schedule_work(&cdev->scif_work); - } - - pr_debug("%s %d Server thread stopped\n", __func__, __LINE__); - return 0; -} - -static int cosm_scif_listen(void) -{ - int rc; - - listen_epd = scif_open(); - if (!listen_epd) { - pr_err("%s %d scif_open failed\n", __func__, __LINE__); - return -ENOMEM; - } - - rc = scif_bind(listen_epd, SCIF_COSM_LISTEN_PORT); - if (rc < 0) { - pr_err("%s %d scif_bind failed rc %d\n", - __func__, __LINE__, rc); - goto err; - } - - rc = scif_listen(listen_epd, COSM_SCIF_BACKLOG); - if (rc < 0) { - pr_err("%s %d scif_listen rc %d\n", __func__, __LINE__, rc); - goto err; - } - pr_debug("%s %d listen_epd set up\n", __func__, __LINE__); - return 0; -err: - scif_close(listen_epd); - listen_epd = NULL; - return rc; -} - -static void cosm_scif_listen_exit(void) -{ - pr_debug("%s %d closing listen_epd\n", __func__, __LINE__); - if (listen_epd) { - scif_close(listen_epd); - listen_epd = NULL; - } -} - -/* - * Create a listening SCIF endpoint and a server kthread which accepts incoming - * SCIF connections from MIC cards - */ -int cosm_scif_init(void) -{ - int rc = cosm_scif_listen(); - - if (rc) { - pr_err("%s %d cosm_scif_listen rc %d\n", - __func__, __LINE__, rc); - goto err; - } - - server_thread = kthread_run(cosm_scif_server, NULL, "cosm_server"); - if (IS_ERR(server_thread)) { - rc = PTR_ERR(server_thread); - pr_err("%s %d kthread_run rc %d\n", __func__, __LINE__, rc); - goto listen_exit; - } - return 0; -listen_exit: - cosm_scif_listen_exit(); -err: - return rc; -} - -/* Stop the running server thread and close the listening SCIF endpoint */ -void cosm_scif_exit(void) -{ - int rc; - - if (!IS_ERR_OR_NULL(server_thread)) { - rc = send_sig(SIGKILL, server_thread, 0); - if (rc) { - pr_err("%s %d send_sig rc %d\n", - __func__, __LINE__, rc); - return; - } - kthread_stop(server_thread); - } - - cosm_scif_listen_exit(); -} diff --git a/drivers/misc/mic/cosm/cosm_sysfs.c b/drivers/misc/mic/cosm/cosm_sysfs.c deleted file mode 100644 index e6dac967c1af..000000000000 --- a/drivers/misc/mic/cosm/cosm_sysfs.c +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ -#include <linux/slab.h> -#include "cosm_main.h" - -/* - * A state-to-string lookup table, for exposing a human readable state - * via sysfs. Always keep in sync with enum cosm_states - */ -const char * const cosm_state_string[] = { - [MIC_READY] = "ready", - [MIC_BOOTING] = "booting", - [MIC_ONLINE] = "online", - [MIC_SHUTTING_DOWN] = "shutting_down", - [MIC_RESETTING] = "resetting", - [MIC_RESET_FAILED] = "reset_failed", -}; - -/* - * A shutdown-status-to-string lookup table, for exposing a human - * readable state via sysfs. Always keep in sync with enum cosm_shutdown_status - */ -const char * const cosm_shutdown_status_string[] = { - [MIC_NOP] = "nop", - [MIC_CRASHED] = "crashed", - [MIC_HALTED] = "halted", - [MIC_POWER_OFF] = "poweroff", - [MIC_RESTART] = "restart", -}; - -void cosm_set_shutdown_status(struct cosm_device *cdev, u8 shutdown_status) -{ - dev_dbg(&cdev->dev, "Shutdown Status %s -> %s\n", - cosm_shutdown_status_string[cdev->shutdown_status], - cosm_shutdown_status_string[shutdown_status]); - cdev->shutdown_status = shutdown_status; -} - -void cosm_set_state(struct cosm_device *cdev, u8 state) -{ - dev_dbg(&cdev->dev, "State %s -> %s\n", - cosm_state_string[cdev->state], - cosm_state_string[state]); - cdev->state = state; - sysfs_notify_dirent(cdev->state_sysfs); -} - -static ssize_t -family_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return cdev->hw_ops->family(cdev, buf); -} -static DEVICE_ATTR_RO(family); - -static ssize_t -stepping_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return cdev->hw_ops->stepping(cdev, buf); -} -static DEVICE_ATTR_RO(stepping); - -static ssize_t -state_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev || cdev->state >= MIC_LAST) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%s\n", - cosm_state_string[cdev->state]); -} - -static ssize_t -state_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int rc; - - if (!cdev) - return -EINVAL; - - if (sysfs_streq(buf, "boot")) { - rc = cosm_start(cdev); - goto done; - } - if (sysfs_streq(buf, "reset")) { - rc = cosm_reset(cdev); - goto done; - } - - if (sysfs_streq(buf, "shutdown")) { - rc = cosm_shutdown(cdev); - goto done; - } - rc = -EINVAL; -done: - if (rc) - count = rc; - return count; -} -static DEVICE_ATTR_RW(state); - -static ssize_t shutdown_status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev || cdev->shutdown_status >= MIC_STATUS_LAST) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%s\n", - cosm_shutdown_status_string[cdev->shutdown_status]); -} -static DEVICE_ATTR_RO(shutdown_status); - -static ssize_t -heartbeat_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%d\n", cdev->sysfs_heartbeat_enable); -} - -static ssize_t -heartbeat_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int enable; - int ret; - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - ret = kstrtoint(buf, 10, &enable); - if (ret) - goto unlock; - - cdev->sysfs_heartbeat_enable = enable; - /* if state is not online, cdev->heartbeat_watchdog_enable is 0 */ - if (cdev->state == MIC_ONLINE) - cdev->heartbeat_watchdog_enable = enable; - ret = count; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return ret; -} -static DEVICE_ATTR_RW(heartbeat_enable); - -static ssize_t -cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *cmdline; - - if (!cdev) - return -EINVAL; - - cmdline = cdev->cmdline; - - if (cmdline) - return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); - return 0; -} - -static ssize_t -cmdline_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->cmdline); - - cdev->cmdline = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->cmdline) { - count = -ENOMEM; - goto unlock; - } - - strncpy(cdev->cmdline, buf, count); - - if (cdev->cmdline[count - 1] == '\n') - cdev->cmdline[count - 1] = '\0'; - else - cdev->cmdline[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(cmdline); - -static ssize_t -firmware_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *firmware; - - if (!cdev) - return -EINVAL; - - firmware = cdev->firmware; - - if (firmware) - return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); - return 0; -} - -static ssize_t -firmware_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->firmware); - - cdev->firmware = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->firmware) { - count = -ENOMEM; - goto unlock; - } - strncpy(cdev->firmware, buf, count); - - if (cdev->firmware[count - 1] == '\n') - cdev->firmware[count - 1] = '\0'; - else - cdev->firmware[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(firmware); - -static ssize_t -ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *ramdisk; - - if (!cdev) - return -EINVAL; - - ramdisk = cdev->ramdisk; - - if (ramdisk) - return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); - return 0; -} - -static ssize_t -ramdisk_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->ramdisk); - - cdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->ramdisk) { - count = -ENOMEM; - goto unlock; - } - - strncpy(cdev->ramdisk, buf, count); - - if (cdev->ramdisk[count - 1] == '\n') - cdev->ramdisk[count - 1] = '\0'; - else - cdev->ramdisk[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(ramdisk); - -static ssize_t -bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *bootmode; - - if (!cdev) - return -EINVAL; - - bootmode = cdev->bootmode; - - if (bootmode) - return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); - return 0; -} - -static ssize_t -bootmode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "flash")) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->bootmode); - - cdev->bootmode = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->bootmode) { - count = -ENOMEM; - goto unlock; - } - - strncpy(cdev->bootmode, buf, count); - - if (cdev->bootmode[count - 1] == '\n') - cdev->bootmode[count - 1] = '\0'; - else - cdev->bootmode[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(bootmode); - -static ssize_t -log_buf_addr_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_addr); -} - -static ssize_t -log_buf_addr_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int ret; - unsigned long addr; - - if (!cdev) - return -EINVAL; - - ret = kstrtoul(buf, 16, &addr); - if (ret) - goto exit; - - cdev->log_buf_addr = (void *)addr; - ret = count; -exit: - return ret; -} -static DEVICE_ATTR_RW(log_buf_addr); - -static ssize_t -log_buf_len_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_len); -} - -static ssize_t -log_buf_len_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int ret; - unsigned long addr; - - if (!cdev) - return -EINVAL; - - ret = kstrtoul(buf, 16, &addr); - if (ret) - goto exit; - - cdev->log_buf_len = (int *)addr; - ret = count; -exit: - return ret; -} -static DEVICE_ATTR_RW(log_buf_len); - -static struct attribute *cosm_default_attrs[] = { - &dev_attr_family.attr, - &dev_attr_stepping.attr, - &dev_attr_state.attr, - &dev_attr_shutdown_status.attr, - &dev_attr_heartbeat_enable.attr, - &dev_attr_cmdline.attr, - &dev_attr_firmware.attr, - &dev_attr_ramdisk.attr, - &dev_attr_bootmode.attr, - &dev_attr_log_buf_addr.attr, - &dev_attr_log_buf_len.attr, - - NULL -}; - -ATTRIBUTE_GROUPS(cosm_default); - -void cosm_sysfs_init(struct cosm_device *cdev) -{ - cdev->attr_group = cosm_default_groups; -} diff --git a/drivers/misc/mic/cosm_client/Makefile b/drivers/misc/mic/cosm_client/Makefile deleted file mode 100644 index 5b62270bc2ab..000000000000 --- a/drivers/misc/mic/cosm_client/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile - Intel MIC COSM Client Driver -# Copyright(c) 2015, Intel Corporation. -# -obj-$(CONFIG_MIC_COSM) += cosm_client.o - -cosm_client-objs += cosm_scif_client.o diff --git a/drivers/misc/mic/cosm_client/cosm_scif_client.c b/drivers/misc/mic/cosm_client/cosm_scif_client.c deleted file mode 100644 index a03213dd9319..000000000000 --- a/drivers/misc/mic/cosm_client/cosm_scif_client.c +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC COSM Client Driver - */ -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/reboot.h> -#include <linux/kthread.h> -#include <linux/sched/signal.h> - -#include "../cosm/cosm_main.h" - -#define COSM_SCIF_MAX_RETRIES 10 -#define COSM_HEARTBEAT_SEND_MSEC (COSM_HEARTBEAT_SEND_SEC * MSEC_PER_SEC) - -static struct task_struct *client_thread; -static scif_epd_t client_epd; -static struct scif_peer_dev *client_spdev; - -/* - * Reboot notifier: receives shutdown status from the OS and communicates it - * back to the COSM process on the host - */ -static int cosm_reboot_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN_STATUS }; - int rc; - - event = (event == SYS_RESTART) ? SYSTEM_RESTART : event; - dev_info(&client_spdev->dev, "%s %d received event %ld\n", - __func__, __LINE__, event); - - msg.shutdown_status = event; - rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) - dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n", - __func__, __LINE__, rc); - - return NOTIFY_DONE; -} - -static struct notifier_block cosm_reboot = { - .notifier_call = cosm_reboot_event, -}; - -/* Set system time from timespec value received from the host */ -static void cosm_set_time(struct cosm_msg *msg) -{ - struct timespec64 ts = { - .tv_sec = msg->timespec.tv_sec, - .tv_nsec = msg->timespec.tv_nsec, - }; - int rc = do_settimeofday64(&ts); - - if (rc) - dev_err(&client_spdev->dev, "%s: %d settimeofday rc %d\n", - __func__, __LINE__, rc); -} - -/* COSM client receive message processing */ -static void cosm_client_recv(void) -{ - struct cosm_msg msg; - int rc; - - while (1) { - rc = scif_recv(client_epd, &msg, sizeof(msg), 0); - if (!rc) { - return; - } else if (rc < 0) { - dev_err(&client_spdev->dev, "%s: %d rc %d\n", - __func__, __LINE__, rc); - return; - } - - dev_dbg(&client_spdev->dev, "%s: %d rc %d id 0x%llx\n", - __func__, __LINE__, rc, msg.id); - - switch (msg.id) { - case COSM_MSG_SYNC_TIME: - cosm_set_time(&msg); - break; - case COSM_MSG_SHUTDOWN: - orderly_poweroff(true); - break; - default: - dev_err(&client_spdev->dev, "%s: %d unknown id %lld\n", - __func__, __LINE__, msg.id); - break; - } - } -} - -/* Initiate connection to the COSM server on the host */ -static int cosm_scif_connect(void) -{ - struct scif_port_id port_id; - int i, rc; - - client_epd = scif_open(); - if (!client_epd) { - dev_err(&client_spdev->dev, "%s %d scif_open failed\n", - __func__, __LINE__); - return -ENOMEM; - } - - port_id.node = 0; - port_id.port = SCIF_COSM_LISTEN_PORT; - - for (i = 0; i < COSM_SCIF_MAX_RETRIES; i++) { - rc = scif_connect(client_epd, &port_id); - if (rc < 0) - msleep(1000); - else - break; - } - - if (rc < 0) { - dev_err(&client_spdev->dev, "%s %d scif_connect rc %d\n", - __func__, __LINE__, rc); - scif_close(client_epd); - client_epd = NULL; - } - return rc < 0 ? rc : 0; -} - -/* Close host SCIF connection */ -static void cosm_scif_connect_exit(void) -{ - if (client_epd) { - scif_close(client_epd); - client_epd = NULL; - } -} - -/* - * COSM SCIF client thread function: waits for messages from the host and sends - * a heartbeat to the host - */ -static int cosm_scif_client(void *unused) -{ - struct cosm_msg msg = { .id = COSM_MSG_HEARTBEAT }; - struct scif_pollepd pollepd; - int rc; - - allow_signal(SIGKILL); - - while (!kthread_should_stop()) { - pollepd.epd = client_epd; - pollepd.events = EPOLLIN; - - rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_SEND_MSEC); - if (rc < 0) { - if (-EINTR != rc) - dev_err(&client_spdev->dev, - "%s %d scif_poll rc %d\n", - __func__, __LINE__, rc); - continue; - } - - if (pollepd.revents & EPOLLIN) - cosm_client_recv(); - - msg.id = COSM_MSG_HEARTBEAT; - rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) - dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n", - __func__, __LINE__, rc); - } - - dev_dbg(&client_spdev->dev, "%s %d Client thread stopped\n", - __func__, __LINE__); - return 0; -} - -static void cosm_scif_probe(struct scif_peer_dev *spdev) -{ - int rc; - - dev_dbg(&spdev->dev, "%s %d: dnode %d\n", - __func__, __LINE__, spdev->dnode); - - /* We are only interested in the host with spdev->dnode == 0 */ - if (spdev->dnode) - return; - - client_spdev = spdev; - rc = cosm_scif_connect(); - if (rc) - goto exit; - - rc = register_reboot_notifier(&cosm_reboot); - if (rc) { - dev_err(&spdev->dev, - "reboot notifier registration failed rc %d\n", rc); - goto connect_exit; - } - - client_thread = kthread_run(cosm_scif_client, NULL, "cosm_client"); - if (IS_ERR(client_thread)) { - rc = PTR_ERR(client_thread); - dev_err(&spdev->dev, "%s %d kthread_run rc %d\n", - __func__, __LINE__, rc); - goto unreg_reboot; - } - return; -unreg_reboot: - unregister_reboot_notifier(&cosm_reboot); -connect_exit: - cosm_scif_connect_exit(); -exit: - client_spdev = NULL; -} - -static void cosm_scif_remove(struct scif_peer_dev *spdev) -{ - int rc; - - dev_dbg(&spdev->dev, "%s %d: dnode %d\n", - __func__, __LINE__, spdev->dnode); - - if (spdev->dnode) - return; - - if (!IS_ERR_OR_NULL(client_thread)) { - rc = send_sig(SIGKILL, client_thread, 0); - if (rc) { - pr_err("%s %d send_sig rc %d\n", - __func__, __LINE__, rc); - return; - } - kthread_stop(client_thread); - } - unregister_reboot_notifier(&cosm_reboot); - cosm_scif_connect_exit(); - client_spdev = NULL; -} - -static struct scif_client scif_client_cosm = { - .name = KBUILD_MODNAME, - .probe = cosm_scif_probe, - .remove = cosm_scif_remove, -}; - -static int __init cosm_client_init(void) -{ - int rc = scif_client_register(&scif_client_cosm); - - if (rc) - pr_err("scif_client_register failed rc %d\n", rc); - return rc; -} - -static void __exit cosm_client_exit(void) -{ - scif_client_unregister(&scif_client_cosm); -} - -module_init(cosm_client_init); -module_exit(cosm_client_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC card OS state management client driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile deleted file mode 100644 index 25f153367980..000000000000 --- a/drivers/misc/mic/host/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2013, Intel Corporation. -# -obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o -mic_host-objs := mic_main.o -mic_host-objs += mic_x100.o -mic_host-objs += mic_smpt.o -mic_host-objs += mic_intr.o -mic_host-objs += mic_boot.o -mic_host-objs += mic_debugfs.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c deleted file mode 100644 index 8cb85b8b3e19..000000000000 --- a/drivers/misc/mic/host/mic_boot.c +++ /dev/null @@ -1,588 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include <linux/delay.h> -#include <linux/firmware.h> -#include <linux/pci.h> -#include <linux/kmod.h> -#include <linux/dma-map-ops.h> -#include <linux/mic_common.h> -#include <linux/mic_bus.h> -#include "../bus/scif_bus.h" -#include "../bus/vop_bus.h" -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" - -static inline struct mic_device *vpdev_to_mdev(struct device *dev) -{ - return dev_get_drvdata(dev->parent); -} - -static dma_addr_t -_mic_dma_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, unsigned long attrs) -{ - void *va = phys_to_virt(page_to_phys(page)) + offset; - struct mic_device *mdev = vpdev_to_mdev(dev); - - return mic_map_single(mdev, va, size); -} - -static void _mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - struct mic_device *mdev = vpdev_to_mdev(dev); - - mic_unmap_single(mdev, dma_addr, size); -} - -static const struct dma_map_ops _mic_dma_ops = { - .map_page = _mic_dma_map_page, - .unmap_page = _mic_dma_unmap_page, -}; - -static struct mic_irq * -__mic_request_irq(struct vop_device *vpdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int intr_src) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mic_request_threaded_irq(mdev, func, NULL, name, data, - intr_src, MIC_INTR_DB); -} - -static void __mic_free_irq(struct vop_device *vpdev, - struct mic_irq *cookie, void *data) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - mic_free_irq(mdev, cookie, data); -} - -static void __mic_ack_interrupt(struct vop_device *vpdev, int num) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - mdev->ops->intr_workarounds(mdev); -} - -static int __mic_next_db(struct vop_device *vpdev) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mic_next_db(mdev); -} - -static void *__mic_get_dp(struct vop_device *vpdev) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mdev->dp; -} - -static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) -{ - return NULL; -} - -static void __mic_send_intr(struct vop_device *vpdev, int db) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - mdev->ops->send_intr(mdev, db); -} - -static void __iomem *__mic_ioremap(struct vop_device *vpdev, - dma_addr_t pa, size_t len) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mdev->aper.va + pa; -} - -static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) -{ - /* nothing to do */ -} - -static struct vop_hw_ops vop_hw_ops = { - .request_irq = __mic_request_irq, - .free_irq = __mic_free_irq, - .ack_interrupt = __mic_ack_interrupt, - .next_db = __mic_next_db, - .get_dp = __mic_get_dp, - .get_remote_dp = __mic_get_remote_dp, - .send_intr = __mic_send_intr, - .remap = __mic_ioremap, - .unmap = __mic_iounmap, -}; - -static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev) -{ - return dev_get_drvdata(scdev->dev.parent); -} - -static void *__mic_dma_alloc(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - dma_addr_t tmp; - void *va = kzalloc(size, gfp); - - if (va) { - tmp = mic_map_single(mdev, va, size); - if (dma_mapping_error(dev, tmp)) { - kfree(va); - va = NULL; - } else { - *dma_handle = tmp; - } - } - return va; -} - -static void __mic_dma_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle, unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - - mic_unmap_single(mdev, dma_handle, size); - kfree(vaddr); -} - -static dma_addr_t -__mic_dma_map_page(struct device *dev, struct page *page, unsigned long offset, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - void *va = phys_to_virt(page_to_phys(page)) + offset; - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mic_map_single(mdev, va, size); -} - -static void -__mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - - mic_unmap_single(mdev, dma_addr, size); -} - -static int __mic_dma_map_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - struct scatterlist *s; - int i, j, ret; - dma_addr_t da; - - ret = dma_map_sg(&mdev->pdev->dev, sg, nents, dir); - if (ret <= 0) - return 0; - - for_each_sg(sg, s, nents, i) { - da = mic_map(mdev, sg_dma_address(s) + s->offset, s->length); - if (!da) - goto err; - sg_dma_address(s) = da; - } - return nents; -err: - for_each_sg(sg, s, i, j) { - mic_unmap(mdev, sg_dma_address(s), s->length); - sg_dma_address(s) = mic_to_dma_addr(mdev, sg_dma_address(s)); - } - dma_unmap_sg(&mdev->pdev->dev, sg, nents, dir); - return 0; -} - -static void __mic_dma_unmap_sg(struct device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction dir, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - struct scatterlist *s; - dma_addr_t da; - int i; - - for_each_sg(sg, s, nents, i) { - da = mic_to_dma_addr(mdev, sg_dma_address(s)); - mic_unmap(mdev, sg_dma_address(s), s->length); - sg_dma_address(s) = da; - } - dma_unmap_sg(&mdev->pdev->dev, sg, nents, dir); -} - -static const struct dma_map_ops __mic_dma_ops = { - .alloc = __mic_dma_alloc, - .free = __mic_dma_free, - .map_page = __mic_dma_map_page, - .unmap_page = __mic_dma_unmap_page, - .map_sg = __mic_dma_map_sg, - .unmap_sg = __mic_dma_unmap_sg, -}; - -static struct mic_irq * -___mic_request_irq(struct scif_hw_dev *scdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, - void *data, int db) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mic_request_threaded_irq(mdev, func, NULL, name, data, - db, MIC_INTR_DB); -} - -static void -___mic_free_irq(struct scif_hw_dev *scdev, - struct mic_irq *cookie, void *data) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - mic_free_irq(mdev, cookie, data); -} - -static void ___mic_ack_interrupt(struct scif_hw_dev *scdev, int num) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - mdev->ops->intr_workarounds(mdev); -} - -static int ___mic_next_db(struct scif_hw_dev *scdev) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mic_next_db(mdev); -} - -static void ___mic_send_intr(struct scif_hw_dev *scdev, int db) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - mdev->ops->send_intr(mdev, db); -} - -static void __iomem *___mic_ioremap(struct scif_hw_dev *scdev, - phys_addr_t pa, size_t len) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mdev->aper.va + pa; -} - -static void ___mic_iounmap(struct scif_hw_dev *scdev, void __iomem *va) -{ - /* nothing to do */ -} - -static struct scif_hw_ops scif_hw_ops = { - .request_irq = ___mic_request_irq, - .free_irq = ___mic_free_irq, - .ack_interrupt = ___mic_ack_interrupt, - .next_db = ___mic_next_db, - .send_intr = ___mic_send_intr, - .remap = ___mic_ioremap, - .unmap = ___mic_iounmap, -}; - -static inline struct mic_device *mbdev_to_mdev(struct mbus_device *mbdev) -{ - return dev_get_drvdata(mbdev->dev.parent); -} - -static dma_addr_t -mic_dma_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - void *va = phys_to_virt(page_to_phys(page)) + offset; - struct mic_device *mdev = dev_get_drvdata(dev->parent); - - return mic_map_single(mdev, va, size); -} - -static void -mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - struct mic_device *mdev = dev_get_drvdata(dev->parent); - mic_unmap_single(mdev, dma_addr, size); -} - -static const struct dma_map_ops mic_dma_ops = { - .map_page = mic_dma_map_page, - .unmap_page = mic_dma_unmap_page, -}; - -static struct mic_irq * -_mic_request_threaded_irq(struct mbus_device *mbdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src) -{ - return mic_request_threaded_irq(mbdev_to_mdev(mbdev), handler, - thread_fn, name, data, - intr_src, MIC_INTR_DMA); -} - -static void _mic_free_irq(struct mbus_device *mbdev, - struct mic_irq *cookie, void *data) -{ - mic_free_irq(mbdev_to_mdev(mbdev), cookie, data); -} - -static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) -{ - struct mic_device *mdev = mbdev_to_mdev(mbdev); - mdev->ops->intr_workarounds(mdev); -} - -static struct mbus_hw_ops mbus_hw_ops = { - .request_threaded_irq = _mic_request_threaded_irq, - .free_irq = _mic_free_irq, - .ack_interrupt = _mic_ack_interrupt, -}; - -/* Initialize the MIC bootparams */ -void mic_bootparam_init(struct mic_device *mdev) -{ - struct mic_bootparam *bootparam = mdev->dp; - - bootparam->magic = cpu_to_le32(MIC_MAGIC); - bootparam->h2c_config_db = -1; - bootparam->node_id = mdev->id + 1; - bootparam->scif_host_dma_addr = 0x0; - bootparam->scif_card_dma_addr = 0x0; - bootparam->c2h_scif_db = -1; - bootparam->h2c_scif_db = -1; -} - -static inline struct mic_device *cosmdev_to_mdev(struct cosm_device *cdev) -{ - return dev_get_drvdata(cdev->dev.parent); -} - -static void _mic_reset(struct cosm_device *cdev) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - mdev->ops->reset_fw_ready(mdev); - mdev->ops->reset(mdev); -} - -static bool _mic_ready(struct cosm_device *cdev) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - return mdev->ops->is_fw_ready(mdev); -} - -/** - * mic_request_dma_chans - Request DMA channels - * @mdev: pointer to mic_device instance - * - * returns number of DMA channels acquired - */ -static int mic_request_dma_chans(struct mic_device *mdev) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - - do { - chan = dma_request_channel(mask, mdev->ops->dma_filter, - &mdev->pdev->dev); - if (chan) { - mdev->dma_ch[mdev->num_dma_ch++] = chan; - if (mdev->num_dma_ch >= MIC_MAX_DMA_CHAN) - break; - } - } while (chan); - dev_info(&mdev->pdev->dev, "DMA channels # %d\n", mdev->num_dma_ch); - return mdev->num_dma_ch; -} - -/** - * mic_free_dma_chans - release DMA channels - * @mdev: pointer to mic_device instance - * - * returns none - */ -static void mic_free_dma_chans(struct mic_device *mdev) -{ - int i = 0; - - for (i = 0; i < mdev->num_dma_ch; i++) { - dma_release_channel(mdev->dma_ch[i]); - mdev->dma_ch[i] = NULL; - } - mdev->num_dma_ch = 0; -} - -/** - * _mic_start - Start the MIC. - * @cdev: pointer to cosm_device instance - * @id: MIC device id/index provided by COSM used in other drivers like SCIF - * - * This function prepares an MIC for boot and initiates boot. - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - * - * For all cosm_hw_ops the caller holds a mutex to ensure serialization. - */ -static int _mic_start(struct cosm_device *cdev, int id) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - int rc; - - mic_bootparam_init(mdev); - mdev->dma_mbdev = mbus_register_device(&mdev->pdev->dev, - MBUS_DEV_DMA_HOST, &mic_dma_ops, - &mbus_hw_ops, id, mdev->mmio.va); - if (IS_ERR(mdev->dma_mbdev)) { - rc = PTR_ERR(mdev->dma_mbdev); - goto unlock_ret; - } - if (!mic_request_dma_chans(mdev)) { - rc = -ENODEV; - goto dma_remove; - } - mdev->scdev = scif_register_device(&mdev->pdev->dev, MIC_SCIF_DEV, - &__mic_dma_ops, &scif_hw_ops, - id + 1, 0, &mdev->mmio, - &mdev->aper, mdev->dp, NULL, - mdev->dma_ch, mdev->num_dma_ch, - true); - if (IS_ERR(mdev->scdev)) { - rc = PTR_ERR(mdev->scdev); - goto dma_free; - } - - mdev->vpdev = vop_register_device(&mdev->pdev->dev, - VOP_DEV_TRNSP, &_mic_dma_ops, - &vop_hw_ops, id + 1, &mdev->aper, - mdev->dma_ch[0]); - if (IS_ERR(mdev->vpdev)) { - rc = PTR_ERR(mdev->vpdev); - goto scif_remove; - } - - rc = mdev->ops->load_mic_fw(mdev, NULL); - if (rc) - goto vop_remove; - mic_smpt_restore(mdev); - mic_intr_restore(mdev); - mdev->intr_ops->enable_interrupts(mdev); - mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); - mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); - mdev->ops->send_firmware_intr(mdev); - goto unlock_ret; -vop_remove: - vop_unregister_device(mdev->vpdev); -scif_remove: - scif_unregister_device(mdev->scdev); -dma_free: - mic_free_dma_chans(mdev); -dma_remove: - mbus_unregister_device(mdev->dma_mbdev); -unlock_ret: - return rc; -} - -/** - * _mic_stop - Prepare the MIC for reset and trigger reset. - * @cdev: pointer to cosm_device instance - * @force: force a MIC to reset even if it is already offline. - * - * RETURNS: None. - */ -static void _mic_stop(struct cosm_device *cdev, bool force) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - /* - * Since SCIF handles card shutdown and reset (using COSM), it will - * will be the first to be registered and the last to be - * unregistered. - */ - vop_unregister_device(mdev->vpdev); - scif_unregister_device(mdev->scdev); - mic_free_dma_chans(mdev); - mbus_unregister_device(mdev->dma_mbdev); - mic_bootparam_init(mdev); -} - -static ssize_t _mic_family(struct cosm_device *cdev, char *buf) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - static const char *family[MIC_FAMILY_LAST] = { "x100", "Unknown" }; - - return scnprintf(buf, PAGE_SIZE, "%s\n", family[mdev->family]); -} - -static ssize_t _mic_stepping(struct cosm_device *cdev, char *buf) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - const char *string = "??"; - - switch (mdev->stepping) { - case MIC_A0_STEP: - string = "A0"; - break; - case MIC_B0_STEP: - string = "B0"; - break; - case MIC_B1_STEP: - string = "B1"; - break; - case MIC_C0_STEP: - string = "C0"; - break; - default: - break; - } - return scnprintf(buf, PAGE_SIZE, "%s\n", string); -} - -static struct mic_mw *_mic_aper(struct cosm_device *cdev) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - return &mdev->aper; -} - -struct cosm_hw_ops cosm_hw_ops = { - .reset = _mic_reset, - .force_reset = _mic_reset, - .post_reset = NULL, - .ready = _mic_ready, - .start = _mic_start, - .stop = _mic_stop, - .family = _mic_family, - .stepping = _mic_stepping, - .aper = _mic_aper, -}; diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c deleted file mode 100644 index ffda740e20d5..000000000000 --- a/drivers/misc/mic/host/mic_debugfs.c +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include <linux/debugfs.h> -#include <linux/pci.h> -#include <linux/seq_file.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" - -/* Debugfs parent dir */ -static struct dentry *mic_dbg; - -static int mic_smpt_show(struct seq_file *s, void *pos) -{ - int i; - struct mic_device *mdev = s->private; - unsigned long flags; - - seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", - mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); - seq_puts(s, "====================================================\n"); - - if (mdev->smpt) { - struct mic_smpt_info *smpt_info = mdev->smpt; - spin_lock_irqsave(&smpt_info->smpt_lock, flags); - for (i = 0; i < smpt_info->info.num_reg; i++) { - seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", - " ", i, smpt_info->entry[i].dma_addr, - smpt_info->entry[i].ref_count); - } - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - } - seq_puts(s, "====================================================\n"); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_smpt); - -static int mic_post_code_show(struct seq_file *s, void *pos) -{ - struct mic_device *mdev = s->private; - u32 reg = mdev->ops->get_postcode(mdev); - - seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_post_code); - -static int mic_msi_irq_info_show(struct seq_file *s, void *pos) -{ - struct mic_device *mdev = s->private; - int reg; - int i, j; - u16 entry; - u16 vector; - struct pci_dev *pdev = mdev->pdev; - - if (pci_dev_msi_enabled(pdev)) { - for (i = 0; i < mdev->irq_info.num_vectors; i++) { - if (pdev->msix_enabled) { - entry = mdev->irq_info.msix_entries[i].entry; - vector = mdev->irq_info.msix_entries[i].vector; - } else { - entry = 0; - vector = pdev->irq; - } - - reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); - - seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", - "IRQ:", vector, "Entry:", entry, i, reg); - - seq_printf(s, "%-10s", "offset:"); - for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) - seq_printf(s, "%4d ", j); - seq_puts(s, "\n"); - - - seq_printf(s, "%-10s", "count:"); - for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) - seq_printf(s, "%4d ", - (mdev->irq_info.mic_msi_map[i] & - BIT(j)) ? 1 : 0); - seq_puts(s, "\n\n"); - } - } else { - seq_puts(s, "MSI/MSIx interrupts not enabled\n"); - } - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_msi_irq_info); - -/* - * mic_create_debug_dir - Initialize MIC debugfs entries. - */ -void mic_create_debug_dir(struct mic_device *mdev) -{ - char name[16]; - - if (!mic_dbg) - return; - - scnprintf(name, sizeof(name), "mic%d", mdev->id); - mdev->dbg_dir = debugfs_create_dir(name, mic_dbg); - - debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, - &mic_smpt_fops); - - debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, - &mic_post_code_fops); - - debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, - &mic_msi_irq_info_fops); -} - -/* - * mic_delete_debug_dir - Uninitialize MIC debugfs entries. - */ -void mic_delete_debug_dir(struct mic_device *mdev) -{ - debugfs_remove_recursive(mdev->dbg_dir); -} - -/* - * mic_init_debugfs - Initialize global debugfs entry. - */ -void __init mic_init_debugfs(void) -{ - mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); -} - -/* - * mic_exit_debugfs - Uninitialize global debugfs entry - */ -void mic_exit_debugfs(void) -{ - debugfs_remove(mic_dbg); -} diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h deleted file mode 100644 index 41bcd308ae59..000000000000 --- a/drivers/misc/mic/host/mic_device.h +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef _MIC_DEVICE_H_ -#define _MIC_DEVICE_H_ - -#include <linux/cdev.h> -#include <linux/idr.h> -#include <linux/notifier.h> -#include <linux/irqreturn.h> -#include <linux/dmaengine.h> -#include <linux/miscdevice.h> -#include <linux/mic_bus.h> -#include "../bus/scif_bus.h" -#include "../bus/vop_bus.h" -#include "../bus/cosm_bus.h" -#include "mic_intr.h" - -/** - * enum mic_stepping - MIC stepping ids. - */ -enum mic_stepping { - MIC_A0_STEP = 0x0, - MIC_B0_STEP = 0x10, - MIC_B1_STEP = 0x11, - MIC_C0_STEP = 0x20, -}; - -extern struct cosm_hw_ops cosm_hw_ops; - -/** - * struct mic_device - MIC device information for each card. - * - * @mmio: MMIO bar information. - * @aper: Aperture bar information. - * @family: The MIC family to which this device belongs. - * @ops: MIC HW specific operations. - * @id: The unique device id for this MIC device. - * @stepping: Stepping ID. - * @pdev: Underlying PCI device. - * @mic_mutex: Mutex for synchronizing access to mic_device. - * @intr_ops: HW specific interrupt operations. - * @smpt_ops: Hardware specific SMPT operations. - * @smpt: MIC SMPT information. - * @intr_info: H/W specific interrupt information. - * @irq_info: The OS specific irq information - * @dbg_dir: debugfs directory of this MIC device. - * @bootaddr: MIC boot address. - * @dp: virtio device page - * @dp_dma_addr: virtio device page DMA address. - * @dma_mbdev: MIC BUS DMA device. - * @dma_ch - Array of DMA channels - * @num_dma_ch - Number of DMA channels available - * @scdev: SCIF device on the SCIF virtual bus. - * @vpdev: Virtio over PCIe device on the VOP virtual bus. - * @cosm_dev: COSM device - */ -struct mic_device { - struct mic_mw mmio; - struct mic_mw aper; - enum mic_hw_family family; - struct mic_hw_ops *ops; - int id; - enum mic_stepping stepping; - struct pci_dev *pdev; - struct mutex mic_mutex; - struct mic_hw_intr_ops *intr_ops; - struct mic_smpt_ops *smpt_ops; - struct mic_smpt_info *smpt; - struct mic_intr_info *intr_info; - struct mic_irq_info irq_info; - struct dentry *dbg_dir; - u32 bootaddr; - void *dp; - dma_addr_t dp_dma_addr; - struct mbus_device *dma_mbdev; - struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; - int num_dma_ch; - struct scif_hw_dev *scdev; - struct vop_device *vpdev; - struct cosm_device *cosm_dev; -}; - -/** - * struct mic_hw_ops - MIC HW specific operations. - * @aper_bar: Aperture bar resource number. - * @mmio_bar: MMIO bar resource number. - * @read_spad: Read from scratch pad register. - * @write_spad: Write to scratch pad register. - * @send_intr: Send an interrupt for a particular doorbell on the card. - * @ack_interrupt: Hardware specific operations to ack the h/w on - * receipt of an interrupt. - * @intr_workarounds: Hardware specific workarounds needed after - * handling an interrupt. - * @reset: Reset the remote processor. - * @reset_fw_ready: Reset firmware ready field. - * @is_fw_ready: Check if firmware is ready for OS download. - * @send_firmware_intr: Send an interrupt to the card firmware. - * @load_mic_fw: Load firmware segments required to boot the card - * into card memory. This includes the kernel, command line, ramdisk etc. - * @get_postcode: Get post code status from firmware. - * @dma_filter: DMA filter function to be used. - */ -struct mic_hw_ops { - u8 aper_bar; - u8 mmio_bar; - u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); - void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); - void (*send_intr)(struct mic_device *mdev, int doorbell); - u32 (*ack_interrupt)(struct mic_device *mdev); - void (*intr_workarounds)(struct mic_device *mdev); - void (*reset)(struct mic_device *mdev); - void (*reset_fw_ready)(struct mic_device *mdev); - bool (*is_fw_ready)(struct mic_device *mdev); - void (*send_firmware_intr)(struct mic_device *mdev); - int (*load_mic_fw)(struct mic_device *mdev, const char *buf); - u32 (*get_postcode)(struct mic_device *mdev); - bool (*dma_filter)(struct dma_chan *chan, void *param); -}; - -/** - * mic_mmio_read - read from an MMIO register. - * @mw: MMIO register base virtual address. - * @offset: register offset. - * - * RETURNS: register value. - */ -static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) -{ - return ioread32(mw->va + offset); -} - -/** - * mic_mmio_write - write to an MMIO register. - * @mw: MMIO register base virtual address. - * @val: the data value to put into the register - * @offset: register offset. - * - * RETURNS: none. - */ -static inline void -mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) -{ - iowrite32(val, mw->va + offset); -} - -void mic_bootparam_init(struct mic_device *mdev); -void mic_create_debug_dir(struct mic_device *dev); -void mic_delete_debug_dir(struct mic_device *dev); -void __init mic_init_debugfs(void); -void mic_exit_debugfs(void); -#endif diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c deleted file mode 100644 index 85b3221b5d40..000000000000 --- a/drivers/misc/mic/host/mic_intr.c +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include <linux/pci.h> -#include <linux/interrupt.h> - -#include "../common/mic_dev.h" -#include "mic_device.h" - -static irqreturn_t mic_thread_fn(int irq, void *dev) -{ - struct mic_device *mdev = dev; - struct mic_intr_info *intr_info = mdev->intr_info; - struct mic_irq_info *irq_info = &mdev->irq_info; - struct mic_intr_cb *intr_cb; - struct pci_dev *pdev = mdev->pdev; - int i; - - spin_lock(&irq_info->mic_thread_lock); - for (i = intr_info->intr_start_idx[MIC_INTR_DB]; - i < intr_info->intr_len[MIC_INTR_DB]; i++) - if (test_and_clear_bit(i, &irq_info->mask)) { - list_for_each_entry(intr_cb, &irq_info->cb_list[i], - list) - if (intr_cb->thread_fn) - intr_cb->thread_fn(pdev->irq, - intr_cb->data); - } - spin_unlock(&irq_info->mic_thread_lock); - return IRQ_HANDLED; -} -/** - * mic_interrupt - Generic interrupt handler for - * MSI and INTx based interrupts. - * @irq: interrupt to handle (unused) - * @dev: pointer to the mic_device instance - */ -static irqreturn_t mic_interrupt(int irq, void *dev) -{ - struct mic_device *mdev = dev; - struct mic_intr_info *intr_info = mdev->intr_info; - struct mic_irq_info *irq_info = &mdev->irq_info; - struct mic_intr_cb *intr_cb; - struct pci_dev *pdev = mdev->pdev; - u32 mask; - int i; - - mask = mdev->ops->ack_interrupt(mdev); - if (!mask) - return IRQ_NONE; - - spin_lock(&irq_info->mic_intr_lock); - for (i = intr_info->intr_start_idx[MIC_INTR_DB]; - i < intr_info->intr_len[MIC_INTR_DB]; i++) - if (mask & BIT(i)) { - list_for_each_entry(intr_cb, &irq_info->cb_list[i], - list) - if (intr_cb->handler) - intr_cb->handler(pdev->irq, - intr_cb->data); - set_bit(i, &irq_info->mask); - } - spin_unlock(&irq_info->mic_intr_lock); - return IRQ_WAKE_THREAD; -} - -/* Return the interrupt offset from the index. Index is 0 based. */ -static u16 mic_map_src_to_offset(struct mic_device *mdev, - int intr_src, enum mic_intr_type type) -{ - if (type >= MIC_NUM_INTR_TYPES) - return MIC_NUM_OFFSETS; - if (intr_src >= mdev->intr_info->intr_len[type]) - return MIC_NUM_OFFSETS; - - return mdev->intr_info->intr_start_idx[type] + intr_src; -} - -/* Return next available msix_entry. */ -static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) -{ - int i; - struct mic_irq_info *info = &mdev->irq_info; - - for (i = 0; i < info->num_vectors; i++) - if (!info->mic_msi_map[i]) - return &info->msix_entries[i]; - return NULL; -} - -/** - * mic_register_intr_callback - Register a callback handler for the - * given source id. - * - * @mdev: pointer to the mic_device instance - * @idx: The source id to be registered. - * @handler: The function to be called when the source id receives - * the interrupt. - * @thread_fn: thread fn. corresponding to the handler - * @data: Private data of the requester. - * Return the callback structure that was registered or an - * appropriate error on failure. - */ -static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, - u8 idx, irq_handler_t handler, irq_handler_t thread_fn, - void *data) -{ - struct mic_intr_cb *intr_cb; - unsigned long flags; - int rc; - intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL); - - if (!intr_cb) - return ERR_PTR(-ENOMEM); - - intr_cb->handler = handler; - intr_cb->thread_fn = thread_fn; - intr_cb->data = data; - intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, - 0, 0, GFP_KERNEL); - if (intr_cb->cb_id < 0) { - rc = intr_cb->cb_id; - goto ida_fail; - } - - spin_lock(&mdev->irq_info.mic_thread_lock); - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - - return intr_cb; -ida_fail: - kfree(intr_cb); - return ERR_PTR(rc); -} - -/** - * mic_unregister_intr_callback - Unregister the callback handler - * identified by its callback id. - * - * @mdev: pointer to the mic_device instance - * @idx: The callback structure id to be unregistered. - * Return the source id that was unregistered or MIC_NUM_OFFSETS if no - * such callback handler was found. - */ -static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) -{ - struct list_head *pos, *tmp; - struct mic_intr_cb *intr_cb; - unsigned long flags; - int i; - - spin_lock(&mdev->irq_info.mic_thread_lock); - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - for (i = 0; i < MIC_NUM_OFFSETS; i++) { - list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { - intr_cb = list_entry(pos, struct mic_intr_cb, list); - if (intr_cb->cb_id == idx) { - list_del(pos); - ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); - kfree(intr_cb); - spin_unlock_irqrestore( - &mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - return i; - } - } - } - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - return MIC_NUM_OFFSETS; -} - -/** - * mic_setup_msix - Initializes MSIx interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc, i; - int entry_size = sizeof(*mdev->irq_info.msix_entries); - - mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX, - entry_size, GFP_KERNEL); - if (!mdev->irq_info.msix_entries) { - rc = -ENOMEM; - goto err_nomem1; - } - - for (i = 0; i < MIC_MIN_MSIX; i++) - mdev->irq_info.msix_entries[i].entry = i; - - rc = pci_enable_msix_exact(pdev, mdev->irq_info.msix_entries, - MIC_MIN_MSIX); - if (rc) { - dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); - goto err_enable_msix; - } - - mdev->irq_info.num_vectors = MIC_MIN_MSIX; - mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * - mdev->irq_info.num_vectors), GFP_KERNEL); - - if (!mdev->irq_info.mic_msi_map) { - rc = -ENOMEM; - goto err_nomem2; - } - - dev_dbg(&mdev->pdev->dev, - "%d MSIx irqs setup\n", mdev->irq_info.num_vectors); - return 0; -err_nomem2: - pci_disable_msix(pdev); -err_enable_msix: - kfree(mdev->irq_info.msix_entries); -err_nomem1: - mdev->irq_info.num_vectors = 0; - return rc; -} - -/** - * mic_setup_callbacks - Initialize data structures needed - * to handle callbacks. - * - * @mdev: pointer to mic_device instance - */ -static int mic_setup_callbacks(struct mic_device *mdev) -{ - int i; - - mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS, - sizeof(*mdev->irq_info.cb_list), - GFP_KERNEL); - if (!mdev->irq_info.cb_list) - return -ENOMEM; - - for (i = 0; i < MIC_NUM_OFFSETS; i++) - INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); - ida_init(&mdev->irq_info.cb_ida); - spin_lock_init(&mdev->irq_info.mic_intr_lock); - spin_lock_init(&mdev->irq_info.mic_thread_lock); - return 0; -} - -/** - * mic_release_callbacks - Uninitialize data structures needed - * to handle callbacks. - * - * @mdev: pointer to mic_device instance - */ -static void mic_release_callbacks(struct mic_device *mdev) -{ - unsigned long flags; - struct list_head *pos, *tmp; - struct mic_intr_cb *intr_cb; - int i; - - spin_lock(&mdev->irq_info.mic_thread_lock); - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - for (i = 0; i < MIC_NUM_OFFSETS; i++) { - if (list_empty(&mdev->irq_info.cb_list[i])) - break; - - list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { - intr_cb = list_entry(pos, struct mic_intr_cb, list); - list_del(pos); - ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); - kfree(intr_cb); - } - } - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - ida_destroy(&mdev->irq_info.cb_ida); - kfree(mdev->irq_info.cb_list); -} - -/** - * mic_setup_msi - Initializes MSI interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc; - - rc = pci_enable_msi(pdev); - if (rc) { - dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc); - return rc; - } - - mdev->irq_info.num_vectors = 1; - mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * - mdev->irq_info.num_vectors), GFP_KERNEL); - - if (!mdev->irq_info.mic_msi_map) { - rc = -ENOMEM; - goto err_nomem1; - } - - rc = mic_setup_callbacks(mdev); - if (rc) { - dev_err(&pdev->dev, "Error setting up callbacks\n"); - goto err_nomem2; - } - - rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, - 0, "mic-msi", mdev); - if (rc) { - dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); - goto err_irq_req_fail; - } - - dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors); - return 0; -err_irq_req_fail: - mic_release_callbacks(mdev); -err_nomem2: - kfree(mdev->irq_info.mic_msi_map); -err_nomem1: - pci_disable_msi(pdev); - mdev->irq_info.num_vectors = 0; - return rc; -} - -/** - * mic_setup_intx - Initializes legacy interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc; - - /* Enable intx */ - pci_intx(pdev, 1); - rc = mic_setup_callbacks(mdev); - if (rc) { - dev_err(&pdev->dev, "Error setting up callbacks\n"); - goto err_nomem; - } - - rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, - IRQF_SHARED, "mic-intx", mdev); - if (rc) - goto err; - - dev_dbg(&pdev->dev, "intx irq setup\n"); - return 0; -err: - mic_release_callbacks(mdev); -err_nomem: - return rc; -} - -/** - * mic_next_db - Retrieve the next doorbell interrupt source id. - * The id is picked sequentially from the available pool of - * doorlbell ids. - * - * @mdev: pointer to the mic_device instance. - * - * Returns the next doorbell interrupt source. - */ -int mic_next_db(struct mic_device *mdev) -{ - int next_db; - - next_db = mdev->irq_info.next_avail_src % - mdev->intr_info->intr_len[MIC_INTR_DB]; - mdev->irq_info.next_avail_src++; - return next_db; -} - -#define COOKIE_ID_SHIFT 16 -#define GET_ENTRY(cookie) ((cookie) & 0xFFFF) -#define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT) -#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) - -/** - * mic_request_threaded_irq - request an irq. mic_mutex needs - * to be held before calling this function. - * - * @mdev: pointer to mic_device instance - * @handler: The callback function that handles the interrupt. - * The function needs to call ack_interrupts - * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. - * @thread_fn: thread fn required by request_threaded_irq. - * @name: The ASCII name of the callee requesting the irq. - * @data: private data that is returned back when calling the - * function handler. - * @intr_src: The source id of the requester. Its the doorbell id - * for Doorbell interrupts and DMA channel id for DMA interrupts. - * @type: The type of interrupt. Values defined in mic_intr_type - * - * returns: The cookie that is transparent to the caller. Passed - * back when calling mic_free_irq. An appropriate error code - * is returned on failure. Caller needs to use IS_ERR(return_val) - * to check for failure and PTR_ERR(return_val) to obtained the - * error code. - * - */ -struct mic_irq * -mic_request_threaded_irq(struct mic_device *mdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src, - enum mic_intr_type type) -{ - u16 offset; - int rc = 0; - struct msix_entry *msix = NULL; - unsigned long cookie = 0; - u16 entry; - struct mic_intr_cb *intr_cb; - struct pci_dev *pdev = mdev->pdev; - - offset = mic_map_src_to_offset(mdev, intr_src, type); - if (offset >= MIC_NUM_OFFSETS) { - dev_err(&mdev->pdev->dev, - "Error mapping index %d to a valid source id.\n", - intr_src); - rc = -EINVAL; - goto err; - } - - if (mdev->irq_info.num_vectors > 1) { - msix = mic_get_available_vector(mdev); - if (!msix) { - dev_err(&mdev->pdev->dev, - "No MSIx vectors available for use.\n"); - rc = -ENOSPC; - goto err; - } - - rc = request_threaded_irq(msix->vector, handler, thread_fn, - 0, name, data); - if (rc) { - dev_dbg(&mdev->pdev->dev, - "request irq failed rc = %d\n", rc); - goto err; - } - entry = msix->entry; - mdev->irq_info.mic_msi_map[entry] |= BIT(offset); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, true); - cookie = MK_COOKIE(entry, offset); - dev_dbg(&mdev->pdev->dev, "irq: %d assigned for src: %d\n", - msix->vector, intr_src); - } else { - intr_cb = mic_register_intr_callback(mdev, offset, handler, - thread_fn, data); - if (IS_ERR(intr_cb)) { - dev_err(&mdev->pdev->dev, - "No available callback entries for use\n"); - rc = PTR_ERR(intr_cb); - goto err; - } - - entry = 0; - if (pci_dev_msi_enabled(pdev)) { - mdev->irq_info.mic_msi_map[entry] |= (1 << offset); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, true); - } - cookie = MK_COOKIE(entry, intr_cb->cb_id); - dev_dbg(&mdev->pdev->dev, "callback %d registered for src: %d\n", - intr_cb->cb_id, intr_src); - } - return (struct mic_irq *)cookie; -err: - return ERR_PTR(rc); -} - -/** - * mic_free_irq - free irq. mic_mutex - * needs to be held before calling this function. - * - * @mdev: pointer to mic_device instance - * @cookie: cookie obtained during a successful call to mic_request_threaded_irq - * @data: private data specified by the calling function during the - * mic_request_threaded_irq - * - * returns: none. - */ -void mic_free_irq(struct mic_device *mdev, - struct mic_irq *cookie, void *data) -{ - u32 offset; - u32 entry; - u8 src_id; - unsigned int irq; - struct pci_dev *pdev = mdev->pdev; - - entry = GET_ENTRY((unsigned long)cookie); - offset = GET_OFFSET((unsigned long)cookie); - if (mdev->irq_info.num_vectors > 1) { - if (entry >= mdev->irq_info.num_vectors) { - dev_warn(&mdev->pdev->dev, - "entry %d should be < num_irq %d\n", - entry, mdev->irq_info.num_vectors); - return; - } - irq = mdev->irq_info.msix_entries[entry].vector; - free_irq(irq, data); - mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset)); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, false); - - dev_dbg(&mdev->pdev->dev, "irq: %d freed\n", irq); - } else { - irq = pdev->irq; - src_id = mic_unregister_intr_callback(mdev, offset); - if (src_id >= MIC_NUM_OFFSETS) { - dev_warn(&mdev->pdev->dev, "Error unregistering callback\n"); - return; - } - if (pci_dev_msi_enabled(pdev)) { - mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id)); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, src_id, false); - } - dev_dbg(&mdev->pdev->dev, "callback %d unregistered for src: %d\n", - offset, src_id); - } -} - -/** - * mic_setup_interrupts - Initializes interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc; - - rc = mic_setup_msix(mdev, pdev); - if (!rc) - goto done; - - rc = mic_setup_msi(mdev, pdev); - if (!rc) - goto done; - - rc = mic_setup_intx(mdev, pdev); - if (rc) { - dev_err(&mdev->pdev->dev, "no usable interrupts\n"); - return rc; - } -done: - mdev->intr_ops->enable_interrupts(mdev); - return 0; -} - -/** - * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * returns none. - */ -void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) -{ - int i; - - mdev->intr_ops->disable_interrupts(mdev); - if (mdev->irq_info.num_vectors > 1) { - for (i = 0; i < mdev->irq_info.num_vectors; i++) { - if (mdev->irq_info.mic_msi_map[i]) - dev_warn(&pdev->dev, "irq %d may still be in use.\n", - mdev->irq_info.msix_entries[i].vector); - } - kfree(mdev->irq_info.mic_msi_map); - kfree(mdev->irq_info.msix_entries); - pci_disable_msix(pdev); - } else { - if (pci_dev_msi_enabled(pdev)) { - free_irq(pdev->irq, mdev); - kfree(mdev->irq_info.mic_msi_map); - pci_disable_msi(pdev); - } else { - free_irq(pdev->irq, mdev); - } - mic_release_callbacks(mdev); - } -} - -/** - * mic_intr_restore - Restore MIC interrupt registers. - * - * @mdev: pointer to mic_device instance. - * - * Restore the interrupt registers to values previously - * stored in the SW data structures. mic_mutex needs to - * be held before calling this function. - * - * returns None. - */ -void mic_intr_restore(struct mic_device *mdev) -{ - int entry, offset; - struct pci_dev *pdev = mdev->pdev; - - if (!pci_dev_msi_enabled(pdev)) - return; - - for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) { - for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) { - if (mdev->irq_info.mic_msi_map[entry] & BIT(offset)) - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, true); - } - } -} diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h deleted file mode 100644 index b14ba818006f..000000000000 --- a/drivers/misc/mic/host/mic_intr.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef _MIC_INTR_H_ -#define _MIC_INTR_H_ - -#include <linux/bitops.h> -#include <linux/interrupt.h> -/* - * The minimum number of msix vectors required for normal operation. - * 3 for virtio network, console and block devices. - * 1 for card shutdown notifications. - * 4 for host owned DMA channels. - * 1 for SCIF - */ -#define MIC_MIN_MSIX 9 -#define MIC_NUM_OFFSETS 32 - -/** - * mic_intr_source - The type of source that will generate - * the interrupt.The number of types needs to be in sync with - * MIC_NUM_INTR_TYPES - * - * MIC_INTR_DB: The source is a doorbell - * MIC_INTR_DMA: The source is a DMA channel - * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR - * MIC_NUM_INTR_TYPES: Total number of interrupt sources. - */ -enum mic_intr_type { - MIC_INTR_DB = 0, - MIC_INTR_DMA, - MIC_INTR_ERR, - MIC_NUM_INTR_TYPES -}; - -/** - * struct mic_intr_info - Contains h/w specific interrupt sources - * information. - * - * @intr_start_idx: Contains the starting indexes of the - * interrupt types. - * @intr_len: Contains the length of the interrupt types. - */ -struct mic_intr_info { - u16 intr_start_idx[MIC_NUM_INTR_TYPES]; - u16 intr_len[MIC_NUM_INTR_TYPES]; -}; - -/** - * struct mic_irq_info - OS specific irq information - * - * @next_avail_src: next available doorbell that can be assigned. - * @msix_entries: msix entries allocated while setting up MSI-x - * @mic_msi_map: The MSI/MSI-x mapping information. - * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. - * @cb_ida: callback ID allocator to track the callbacks registered. - * @mic_intr_lock: spinlock to protect the interrupt callback list. - * @mic_thread_lock: spinlock to protect the thread callback list. - * This lock is used to protect against thread_fn while - * mic_intr_lock is used to protect against interrupt handler. - * @cb_list: Array of callback lists one for each source. - * @mask: Mask used by the main thread fn to call the underlying thread fns. - */ -struct mic_irq_info { - int next_avail_src; - struct msix_entry *msix_entries; - u32 *mic_msi_map; - u16 num_vectors; - struct ida cb_ida; - spinlock_t mic_intr_lock; - spinlock_t mic_thread_lock; - struct list_head *cb_list; - unsigned long mask; -}; - -/** - * struct mic_intr_cb - Interrupt callback structure. - * - * @handler: The callback function - * @thread_fn: The thread_fn. - * @data: Private data of the requester. - * @cb_id: The callback id. Identifies this callback. - * @list: list head pointing to the next callback structure. - */ -struct mic_intr_cb { - irq_handler_t handler; - irq_handler_t thread_fn; - void *data; - int cb_id; - struct list_head list; -}; - -/** - * struct mic_irq - opaque pointer used as cookie - */ -struct mic_irq; - -/* Forward declaration */ -struct mic_device; - -/** - * struct mic_hw_intr_ops: MIC HW specific interrupt operations - * @intr_init: Initialize H/W specific interrupt information. - * @enable_interrupts: Enable interrupts from the hardware. - * @disable_interrupts: Disable interrupts from the hardware. - * @program_msi_to_src_map: Update MSI mapping registers with - * irq information. - * @read_msi_to_src_map: Read MSI mapping registers containing - * irq information. - */ -struct mic_hw_intr_ops { - void (*intr_init)(struct mic_device *mdev); - void (*enable_interrupts)(struct mic_device *mdev); - void (*disable_interrupts)(struct mic_device *mdev); - void (*program_msi_to_src_map) (struct mic_device *mdev, - int idx, int intr_src, bool set); - u32 (*read_msi_to_src_map) (struct mic_device *mdev, - int idx); -}; - -int mic_next_db(struct mic_device *mdev); -struct mic_irq * -mic_request_threaded_irq(struct mic_device *mdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src, - enum mic_intr_type type); -void mic_free_irq(struct mic_device *mdev, - struct mic_irq *cookie, void *data); -int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); -void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev); -void mic_intr_restore(struct mic_device *mdev); -#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c deleted file mode 100644 index ea4608527ea0..000000000000 --- a/drivers/misc/mic/host/mic_main.c +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include <linux/fs.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/poll.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_x100.h" -#include "mic_smpt.h" - -static const char mic_driver_name[] = "mic"; - -static const struct pci_device_id mic_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)}, - - /* required last entry */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, mic_pci_tbl); - -/* ID allocator for MIC devices */ -static struct ida g_mic_ida; - -/* Initialize the device page */ -static int mic_dp_init(struct mic_device *mdev) -{ - mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL); - if (!mdev->dp) - return -ENOMEM; - - mdev->dp_dma_addr = mic_map_single(mdev, - mdev->dp, MIC_DP_SIZE); - if (mic_map_error(mdev->dp_dma_addr)) { - kfree(mdev->dp); - dev_err(&mdev->pdev->dev, "%s %d err %d\n", - __func__, __LINE__, -ENOMEM); - return -ENOMEM; - } - mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); - mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); - return 0; -} - -/* Uninitialize the device page */ -static void mic_dp_uninit(struct mic_device *mdev) -{ - mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE); - kfree(mdev->dp); -} - -/** - * mic_ops_init: Initialize HW specific operation tables. - * - * @mdev: pointer to mic_device instance - * - * returns none. - */ -static void mic_ops_init(struct mic_device *mdev) -{ - switch (mdev->family) { - case MIC_FAMILY_X100: - mdev->ops = &mic_x100_ops; - mdev->intr_ops = &mic_x100_intr_ops; - mdev->smpt_ops = &mic_x100_smpt_ops; - break; - default: - break; - } -} - -/** - * mic_get_family - Determine hardware family to which this MIC belongs. - * - * @pdev: The pci device structure - * - * returns family. - */ -static enum mic_hw_family mic_get_family(struct pci_dev *pdev) -{ - enum mic_hw_family family; - - switch (pdev->device) { - case MIC_X100_PCI_DEVICE_2250: - case MIC_X100_PCI_DEVICE_2251: - case MIC_X100_PCI_DEVICE_2252: - case MIC_X100_PCI_DEVICE_2253: - case MIC_X100_PCI_DEVICE_2254: - case MIC_X100_PCI_DEVICE_2255: - case MIC_X100_PCI_DEVICE_2256: - case MIC_X100_PCI_DEVICE_2257: - case MIC_X100_PCI_DEVICE_2258: - case MIC_X100_PCI_DEVICE_2259: - case MIC_X100_PCI_DEVICE_225a: - case MIC_X100_PCI_DEVICE_225b: - case MIC_X100_PCI_DEVICE_225c: - case MIC_X100_PCI_DEVICE_225d: - case MIC_X100_PCI_DEVICE_225e: - family = MIC_FAMILY_X100; - break; - default: - family = MIC_FAMILY_UNKNOWN; - break; - } - return family; -} - -/** - * mic_device_init - Allocates and initializes the MIC device structure - * - * @mdev: pointer to mic_device instance - * @pdev: The pci device structure - * - * returns none. - */ -static void -mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) -{ - mdev->pdev = pdev; - mdev->family = mic_get_family(pdev); - mdev->stepping = pdev->revision; - mic_ops_init(mdev); - mutex_init(&mdev->mic_mutex); - mdev->irq_info.next_avail_src = 0; -} - -/** - * mic_probe - Device Initialization Routine - * - * @pdev: PCI device structure - * @ent: entry in mic_pci_tbl - * - * returns 0 on success, < 0 on failure. - */ -static int mic_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int rc; - struct mic_device *mdev; - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) { - rc = -ENOMEM; - goto mdev_alloc_fail; - } - mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); - if (mdev->id < 0) { - rc = mdev->id; - dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc); - goto ida_fail; - } - - mic_device_init(mdev, pdev); - - rc = pci_enable_device(pdev); - if (rc) { - dev_err(&pdev->dev, "failed to enable pci device.\n"); - goto ida_remove; - } - - pci_set_master(pdev); - - rc = pci_request_regions(pdev, mic_driver_name); - if (rc) { - dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; - } - - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (rc) { - dev_err(&pdev->dev, "Cannot set DMA mask\n"); - goto release_regions; - } - - mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar); - mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar); - mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar); - if (!mdev->mmio.va) { - dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); - rc = -EIO; - goto release_regions; - } - - mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar); - mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar); - mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len); - if (!mdev->aper.va) { - dev_err(&pdev->dev, "Cannot remap Aperture BAR\n"); - rc = -EIO; - goto unmap_mmio; - } - - mdev->intr_ops->intr_init(mdev); - rc = mic_setup_interrupts(mdev, pdev); - if (rc) { - dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc); - goto unmap_aper; - } - rc = mic_smpt_init(mdev); - if (rc) { - dev_err(&pdev->dev, "smpt_init failed %d\n", rc); - goto free_interrupts; - } - - pci_set_drvdata(pdev, mdev); - - rc = mic_dp_init(mdev); - if (rc) { - dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc); - goto smpt_uninit; - } - mic_bootparam_init(mdev); - mic_create_debug_dir(mdev); - - mdev->cosm_dev = cosm_register_device(&mdev->pdev->dev, &cosm_hw_ops); - if (IS_ERR(mdev->cosm_dev)) { - rc = PTR_ERR(mdev->cosm_dev); - dev_err(&pdev->dev, "cosm_add_device failed rc %d\n", rc); - goto cleanup_debug_dir; - } - return 0; -cleanup_debug_dir: - mic_delete_debug_dir(mdev); - mic_dp_uninit(mdev); -smpt_uninit: - mic_smpt_uninit(mdev); -free_interrupts: - mic_free_interrupts(mdev, pdev); -unmap_aper: - iounmap(mdev->aper.va); -unmap_mmio: - iounmap(mdev->mmio.va); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); -ida_remove: - ida_simple_remove(&g_mic_ida, mdev->id); -ida_fail: - kfree(mdev); -mdev_alloc_fail: - dev_err(&pdev->dev, "Probe failed rc %d\n", rc); - return rc; -} - -/** - * mic_remove - Device Removal Routine - * mic_remove is called by the PCI subsystem to alert the driver - * that it should release a PCI device. - * - * @pdev: PCI device structure - */ -static void mic_remove(struct pci_dev *pdev) -{ - struct mic_device *mdev; - - mdev = pci_get_drvdata(pdev); - if (!mdev) - return; - - cosm_unregister_device(mdev->cosm_dev); - mic_delete_debug_dir(mdev); - mic_dp_uninit(mdev); - mic_smpt_uninit(mdev); - mic_free_interrupts(mdev, pdev); - iounmap(mdev->aper.va); - iounmap(mdev->mmio.va); - pci_release_regions(pdev); - pci_disable_device(pdev); - ida_simple_remove(&g_mic_ida, mdev->id); - kfree(mdev); -} - -static struct pci_driver mic_driver = { - .name = mic_driver_name, - .id_table = mic_pci_tbl, - .probe = mic_probe, - .remove = mic_remove -}; - -static int __init mic_init(void) -{ - int ret; - - request_module("mic_x100_dma"); - mic_init_debugfs(); - ida_init(&g_mic_ida); - ret = pci_register_driver(&mic_driver); - if (ret) { - pr_err("pci_register_driver failed ret %d\n", ret); - goto cleanup_debugfs; - } - return 0; -cleanup_debugfs: - ida_destroy(&g_mic_ida); - mic_exit_debugfs(); - return ret; -} - -static void __exit mic_exit(void) -{ - pci_unregister_driver(&mic_driver); - ida_destroy(&g_mic_ida); - mic_exit_debugfs(); -} - -module_init(mic_init); -module_exit(mic_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c deleted file mode 100644 index 50d1bebecd54..000000000000 --- a/drivers/misc/mic/host/mic_smpt.c +++ /dev/null @@ -1,427 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include <linux/pci.h> - -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" - -static inline u64 mic_system_page_mask(struct mic_device *mdev) -{ - return (1ULL << mdev->smpt->info.page_shift) - 1ULL; -} - -static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa) -{ - return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift; -} - -static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index) -{ - return mdev->smpt->info.base + (index * mdev->smpt->info.page_size); -} - -static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa) -{ - return pa & mic_system_page_mask(mdev); -} - -static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa) -{ - return ALIGN(pa - mic_system_page_mask(mdev), - mdev->smpt->info.page_size); -} - -static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa) -{ - return ALIGN(pa, mdev->smpt->info.page_size); -} - -/* Total Cumulative system memory accessible by MIC across all SMPT entries */ -static inline u64 mic_max_system_memory(struct mic_device *mdev) -{ - return mdev->smpt->info.num_reg * mdev->smpt->info.page_size; -} - -/* Maximum system memory address accessible by MIC */ -static inline u64 mic_max_system_addr(struct mic_device *mdev) -{ - return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL; -} - -/* Check if the DMA address is a MIC system memory address */ -static inline bool -mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa) -{ - return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev); -} - -/* Populate an SMPT entry and update the reference counts. */ -static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, - int entries, struct mic_device *mdev) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - int i; - - for (i = spt; i < spt + entries; i++, - addr += smpt_info->info.page_size) { - if (!smpt_info->entry[i].ref_count && - (smpt_info->entry[i].dma_addr != addr)) { - mdev->smpt_ops->set(mdev, addr, i); - smpt_info->entry[i].dma_addr = addr; - } - smpt_info->entry[i].ref_count += ref[i - spt]; - } -} - -/* - * Find an available MIC address in MIC SMPT address space - * for a given DMA address and size. - */ -static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr, - int entries, s64 *ref, size_t size) -{ - int spt; - int ae = 0; - int i; - unsigned long flags; - dma_addr_t mic_addr = 0; - dma_addr_t addr = dma_addr; - struct mic_smpt_info *smpt_info = mdev->smpt; - - spin_lock_irqsave(&smpt_info->smpt_lock, flags); - - /* find existing entries */ - for (i = 0; i < smpt_info->info.num_reg; i++) { - if (smpt_info->entry[i].dma_addr == addr) { - ae++; - addr += smpt_info->info.page_size; - } else if (ae) /* cannot find contiguous entries */ - goto not_found; - - if (ae == entries) - goto found; - } - - /* find free entry */ - for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) { - ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0; - if (ae == entries) - goto found; - } - -not_found: - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - return mic_addr; - -found: - spt = i - entries + 1; - mic_addr = mic_smpt_to_pa(mdev, spt); - mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev); - smpt_info->map_count++; - smpt_info->ref_count += (s64)size; - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - return mic_addr; -} - -/* - * Returns number of smpt entries needed for dma_addr to dma_addr + size - * also returns the reference count array for each of those entries - * and the starting smpt address - */ -static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr, - size_t size, s64 *ref, u64 *smpt_start) -{ - u64 start = dma_addr; - u64 end = dma_addr + size; - int i = 0; - - while (start < end) { - ref[i++] = min(mic_smpt_align_high(mdev, start + 1), - end) - start; - start = mic_smpt_align_high(mdev, start + 1); - } - - if (smpt_start) - *smpt_start = mic_smpt_align_low(mdev, dma_addr); - - return i; -} - -/* - * mic_to_dma_addr - Converts a MIC address to a DMA address. - * - * @mdev: pointer to mic_device instance. - * @mic_addr: MIC address. - * - * returns a DMA address. - */ -dma_addr_t mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - int spt; - dma_addr_t dma_addr; - - if (!mic_is_system_addr(mdev, mic_addr)) { - dev_err(&mdev->pdev->dev, - "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); - return -EINVAL; - } - spt = mic_sys_addr_to_smpt(mdev, mic_addr); - dma_addr = smpt_info->entry[spt].dma_addr + - mic_smpt_offset(mdev, mic_addr); - return dma_addr; -} - -/** - * mic_map - Maps a DMA address to a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @dma_addr: DMA address. - * @size: Size of the region to be mapped. - * - * This API converts the DMA address provided to a DMA address understood - * by MIC. Caller should check for errors by calling mic_map_error(..). - * - * returns DMA address as required by MIC. - */ -dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size) -{ - dma_addr_t mic_addr = 0; - int num_entries; - s64 *ref; - u64 smpt_start; - - if (!size || size > mic_max_system_memory(mdev)) - return mic_addr; - - ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC); - if (!ref) - return mic_addr; - - num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size, - ref, &smpt_start); - - /* Set the smpt table appropriately and get 16G aligned mic address */ - mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size); - - kfree(ref); - - /* - * If mic_addr is zero then its an error case - * since mic_addr can never be zero. - * else generate mic_addr by adding the 16G offset in dma_addr - */ - if (!mic_addr && MIC_FAMILY_X100 == mdev->family) { - dev_err(&mdev->pdev->dev, - "mic_map failed dma_addr 0x%llx size 0x%lx\n", - dma_addr, size); - return mic_addr; - } else { - return mic_addr + mic_smpt_offset(mdev, dma_addr); - } -} - -/** - * mic_unmap - Unmaps a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @mic_addr: MIC physical address. - * @size: Size of the region to be unmapped. - * - * This API unmaps the mappings created by mic_map(..). - * - * returns None. - */ -void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - s64 *ref; - int num_smpt; - int spt; - int i; - unsigned long flags; - - if (!size) - return; - - if (!mic_is_system_addr(mdev, mic_addr)) { - dev_err(&mdev->pdev->dev, - "invalid address: 0x%llx\n", mic_addr); - return; - } - - spt = mic_sys_addr_to_smpt(mdev, mic_addr); - ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC); - if (!ref) - return; - - /* Get number of smpt entries to be mapped, ref count array */ - num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL); - - spin_lock_irqsave(&smpt_info->smpt_lock, flags); - smpt_info->unmap_count++; - smpt_info->ref_count -= (s64)size; - - for (i = spt; i < spt + num_smpt; i++) { - smpt_info->entry[i].ref_count -= ref[i - spt]; - if (smpt_info->entry[i].ref_count < 0) - dev_warn(&mdev->pdev->dev, - "ref count for entry %d is negative\n", i); - } - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - kfree(ref); -} - -/** - * mic_map_single - Maps a virtual address to a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @va: Kernel direct mapped virtual address. - * @size: Size of the region to be mapped. - * - * This API calls pci_map_single(..) for the direct mapped virtual address - * and then converts the DMA address provided to a DMA address understood - * by MIC. Caller should check for errors by calling mic_map_error(..). - * - * returns DMA address as required by MIC. - */ -dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) -{ - dma_addr_t mic_addr = 0; - struct pci_dev *pdev = mdev->pdev; - dma_addr_t dma_addr = - pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL); - - if (!pci_dma_mapping_error(pdev, dma_addr)) { - mic_addr = mic_map(mdev, dma_addr, size); - if (!mic_addr) { - dev_err(&mdev->pdev->dev, - "mic_map failed dma_addr 0x%llx size 0x%lx\n", - dma_addr, size); - pci_unmap_single(pdev, dma_addr, - size, PCI_DMA_BIDIRECTIONAL); - } - } - return mic_addr; -} - -/** - * mic_unmap_single - Unmaps a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @mic_addr: MIC physical address. - * @size: Size of the region to be unmapped. - * - * This API unmaps the mappings created by mic_map_single(..). - * - * returns None. - */ -void -mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) -{ - struct pci_dev *pdev = mdev->pdev; - dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr); - mic_unmap(mdev, mic_addr, size); - pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); -} - -/** - * mic_smpt_init - Initialize MIC System Memory Page Tables. - * - * @mdev: pointer to mic_device instance. - * - * returns 0 for success and -errno for error. - */ -int mic_smpt_init(struct mic_device *mdev) -{ - int i, err = 0; - dma_addr_t dma_addr; - struct mic_smpt_info *smpt_info; - - mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL); - if (!mdev->smpt) - return -ENOMEM; - - smpt_info = mdev->smpt; - mdev->smpt_ops->init(mdev); - smpt_info->entry = kmalloc_array(smpt_info->info.num_reg, - sizeof(*smpt_info->entry), GFP_KERNEL); - if (!smpt_info->entry) { - err = -ENOMEM; - goto free_smpt; - } - spin_lock_init(&smpt_info->smpt_lock); - for (i = 0; i < smpt_info->info.num_reg; i++) { - dma_addr = i * smpt_info->info.page_size; - smpt_info->entry[i].dma_addr = dma_addr; - smpt_info->entry[i].ref_count = 0; - mdev->smpt_ops->set(mdev, dma_addr, i); - } - smpt_info->ref_count = 0; - smpt_info->map_count = 0; - smpt_info->unmap_count = 0; - return 0; -free_smpt: - kfree(smpt_info); - return err; -} - -/** - * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables. - * - * @mdev: pointer to mic_device instance. - * - * returns None. - */ -void mic_smpt_uninit(struct mic_device *mdev) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - int i; - - dev_dbg(&mdev->pdev->dev, - "nodeid %d SMPT ref count %lld map %lld unmap %lld\n", - mdev->id, smpt_info->ref_count, - smpt_info->map_count, smpt_info->unmap_count); - - for (i = 0; i < smpt_info->info.num_reg; i++) { - dev_dbg(&mdev->pdev->dev, - "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n", - i, smpt_info->entry[i].dma_addr, - smpt_info->entry[i].ref_count); - if (smpt_info->entry[i].ref_count) - dev_warn(&mdev->pdev->dev, - "ref count for entry %d is not zero\n", i); - } - kfree(smpt_info->entry); - kfree(smpt_info); -} - -/** - * mic_smpt_restore - Restore MIC System Memory Page Tables. - * - * @mdev: pointer to mic_device instance. - * - * Restore the SMPT registers to values previously stored in the - * SW data structures. Some MIC steppings lose register state - * across resets and this API should be called for performing - * a restore operation if required. - * - * returns None. - */ -void mic_smpt_restore(struct mic_device *mdev) -{ - int i; - dma_addr_t dma_addr; - - for (i = 0; i < mdev->smpt->info.num_reg; i++) { - dma_addr = mdev->smpt->entry[i].dma_addr; - mdev->smpt_ops->set(mdev, dma_addr, i); - } -} diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h deleted file mode 100644 index 3b1ec14a9d81..000000000000 --- a/drivers/misc/mic/host/mic_smpt.h +++ /dev/null @@ -1,87 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef MIC_SMPT_H -#define MIC_SMPT_H -/** - * struct mic_smpt_ops - MIC HW specific SMPT operations. - * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info. - * @set: Set the value for a particular SMPT entry. - */ -struct mic_smpt_ops { - void (*init)(struct mic_device *mdev); - void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index); -}; - -/** - * struct mic_smpt - MIC SMPT entry information. - * @dma_addr: Base DMA address for this SMPT entry. - * @ref_count: Number of active mappings for this SMPT entry in bytes. - */ -struct mic_smpt { - dma_addr_t dma_addr; - s64 ref_count; -}; - -/** - * struct mic_smpt_hw_info - MIC SMPT hardware specific information. - * @num_reg: Number of SMPT registers. - * @page_shift: System memory page shift. - * @page_size: System memory page size. - * @base: System address base. - */ -struct mic_smpt_hw_info { - u8 num_reg; - u8 page_shift; - u64 page_size; - u64 base; -}; - -/** - * struct mic_smpt_info - MIC SMPT information. - * @entry: Array of SMPT entries. - * @smpt_lock: Spin lock protecting access to SMPT data structures. - * @info: Hardware specific SMPT information. - * @ref_count: Number of active SMPT mappings (for debug). - * @map_count: Number of SMPT mappings created (for debug). - * @unmap_count: Number of SMPT mappings destroyed (for debug). - */ -struct mic_smpt_info { - struct mic_smpt *entry; - spinlock_t smpt_lock; - struct mic_smpt_hw_info info; - s64 ref_count; - s64 map_count; - s64 unmap_count; -}; - -dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size); -void mic_unmap_single(struct mic_device *mdev, - dma_addr_t mic_addr, size_t size); -dma_addr_t mic_map(struct mic_device *mdev, - dma_addr_t dma_addr, size_t size); -void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size); -dma_addr_t mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr); - -/** - * mic_map_error - Check a MIC address for errors. - * - * @mdev: pointer to mic_device instance. - * - * returns Whether there was an error during mic_map..(..) APIs. - */ -static inline bool mic_map_error(dma_addr_t mic_addr) -{ - return !mic_addr; -} - -int mic_smpt_init(struct mic_device *mdev); -void mic_smpt_uninit(struct mic_device *mdev); -void mic_smpt_restore(struct mic_device *mdev); - -#endif diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c deleted file mode 100644 index f5536c1ad607..000000000000 --- a/drivers/misc/mic/host/mic_x100.c +++ /dev/null @@ -1,585 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include <linux/fs.h> -#include <linux/pci.h> -#include <linux/sched.h> -#include <linux/firmware.h> -#include <linux/delay.h> - -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_x100.h" -#include "mic_smpt.h" - -static const u16 mic_x100_intr_init[] = { - MIC_X100_DOORBELL_IDX_START, - MIC_X100_DMA_IDX_START, - MIC_X100_ERR_IDX_START, - MIC_X100_NUM_DOORBELL, - MIC_X100_NUM_DMA, - MIC_X100_NUM_ERR, -}; - -/** - * mic_x100_write_spad - write to the scratchpad register - * @mdev: pointer to mic_device instance - * @idx: index to the scratchpad register, 0 based - * @val: the data value to put into the register - * - * This function allows writing of a 32bit value to the indexed scratchpad - * register. - * - * RETURNS: none. - */ -static void -mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) -{ - dev_dbg(&mdev->pdev->dev, "Writing 0x%x to scratch pad index %d\n", - val, idx); - mic_mmio_write(&mdev->mmio, val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); -} - -/** - * mic_x100_read_spad - read from the scratchpad register - * @mdev: pointer to mic_device instance - * @idx: index to scratchpad register, 0 based - * - * This function allows reading of the 32bit scratchpad register. - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static u32 -mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) -{ - u32 val = mic_mmio_read(&mdev->mmio, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); - - dev_dbg(&mdev->pdev->dev, - "Reading 0x%x from scratch pad index %d\n", val, idx); - return val; -} - -/** - * mic_x100_enable_interrupts - Enable interrupts. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_enable_interrupts(struct mic_device *mdev) -{ - u32 reg; - struct mic_mw *mw = &mdev->mmio; - u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; - u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; - - reg = mic_mmio_read(mw, sice0); - reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); - mic_mmio_write(mw, reg, sice0); - - /* - * Enable auto-clear when enabling interrupts. Applicable only for - * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. - */ - if (mdev->irq_info.num_vectors > 1) { - reg = mic_mmio_read(mw, siac0); - reg |= MIC_X100_SBOX_DBR_BITS(0xf) | - MIC_X100_SBOX_DMA_BITS(0xff); - mic_mmio_write(mw, reg, siac0); - } -} - -/** - * mic_x100_disable_interrupts - Disable interrupts. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_disable_interrupts(struct mic_device *mdev) -{ - u32 reg; - struct mic_mw *mw = &mdev->mmio; - u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; - u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; - u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; - - reg = mic_mmio_read(mw, sice0); - mic_mmio_write(mw, reg, sicc0); - - if (mdev->irq_info.num_vectors > 1) { - reg = mic_mmio_read(mw, siac0); - reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | - MIC_X100_SBOX_DMA_BITS(0xff)); - mic_mmio_write(mw, reg, siac0); - } -} - -/** - * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. - * @mdev: pointer to mic_device instance - * @doorbell: doorbell number - */ -static void mic_x100_send_sbox_intr(struct mic_device *mdev, - int doorbell) -{ - struct mic_mw *mw = &mdev->mmio; - u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; - u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + - apic_icr_offset); - - /* for MIC we need to make sure we "hit" the send_icr bit (13) */ - apicicr_low = (apicicr_low | (1 << 13)); - - /* Ensure that the interrupt is ordered w.r.t. previous stores. */ - wmb(); - mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); -} - -/** - * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. - * @mdev: pointer to mic_device instance - * @doorbell: doorbell number - */ -static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, - int doorbell) -{ - int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); - /* Ensure that the interrupt is ordered w.r.t. previous stores. */ - wmb(); - mic_mmio_write(&mdev->mmio, 0, - MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); -} - -/** - * __mic_x100_send_intr - Send interrupt to MIC. - * @mdev: pointer to mic_device instance - * @doorbell: doorbell number. - */ -static void mic_x100_send_intr(struct mic_device *mdev, int doorbell) -{ - int rdmasr_db; - if (doorbell < MIC_X100_NUM_SBOX_IRQ) { - mic_x100_send_sbox_intr(mdev, doorbell); - } else { - rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ; - mic_x100_send_rdmasr_intr(mdev, rdmasr_db); - } -} - -/** - * mic_x100_ack_interrupt - Read the interrupt sources register and - * clear it. This function will be called in the MSI/INTx case. - * @mdev: Pointer to mic_device instance. - * - * Returns: bitmask of interrupt sources triggered. - */ -static u32 mic_x100_ack_interrupt(struct mic_device *mdev) -{ - u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; - u32 reg = mic_mmio_read(&mdev->mmio, sicr0); - mic_mmio_write(&mdev->mmio, reg, sicr0); - return reg; -} - -/** - * mic_x100_intr_workarounds - These hardware specific workarounds are - * to be invoked everytime an interrupt is handled. - * @mdev: Pointer to mic_device instance. - * - * Returns: none - */ -static void mic_x100_intr_workarounds(struct mic_device *mdev) -{ - struct mic_mw *mw = &mdev->mmio; - - /* Clear pending bit array. */ - if (MIC_A0_STEP == mdev->stepping) - mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_MSIXPBACR); - - if (mdev->stepping >= MIC_B0_STEP) - mdev->intr_ops->enable_interrupts(mdev); -} - -/** - * mic_x100_hw_intr_init - Initialize h/w specific interrupt - * information. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_hw_intr_init(struct mic_device *mdev) -{ - mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init; -} - -/** - * mic_x100_read_msi_to_src_map - read from the MSI mapping registers - * @mdev: pointer to mic_device instance - * @idx: index to the mapping register, 0 based - * - * This function allows reading of the 32bit MSI mapping register. - * - * RETURNS: The value in the register. - */ -static u32 -mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) -{ - return mic_mmio_read(&mdev->mmio, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_MXAR0 + idx * 4); -} - -/** - * mic_x100_program_msi_to_src_map - program the MSI mapping registers - * @mdev: pointer to mic_device instance - * @idx: index to the mapping register, 0 based - * @offset: The bit offset in the register that needs to be updated. - * @set: boolean specifying if the bit in the specified offset needs - * to be set or cleared. - * - * RETURNS: None. - */ -static void -mic_x100_program_msi_to_src_map(struct mic_device *mdev, - int idx, int offset, bool set) -{ - unsigned long reg; - struct mic_mw *mw = &mdev->mmio; - u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_MXAR0 + idx * 4; - - reg = mic_mmio_read(mw, mxar); - if (set) - __set_bit(offset, ®); - else - __clear_bit(offset, ®); - mic_mmio_write(mw, reg, mxar); -} - -/* - * mic_x100_reset_fw_ready - Reset Firmware ready status field. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_reset_fw_ready(struct mic_device *mdev) -{ - mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0); -} - -/* - * mic_x100_is_fw_ready - Check if firmware is ready. - * @mdev: pointer to mic_device instance - */ -static bool mic_x100_is_fw_ready(struct mic_device *mdev) -{ - u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); - return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false; -} - -/** - * mic_x100_get_apic_id - Get bootstrap APIC ID. - * @mdev: pointer to mic_device instance - */ -static u32 mic_x100_get_apic_id(struct mic_device *mdev) -{ - u32 scratch2 = 0; - - scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); - return MIC_X100_SPAD2_APIC_ID(scratch2); -} - -/** - * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_send_firmware_intr(struct mic_device *mdev) -{ - u32 apicicr_low; - u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; - int vector = MIC_X100_BSP_INTERRUPT_VECTOR; - struct mic_mw *mw = &mdev->mmio; - - /* - * For MIC we need to make sure we "hit" - * the send_icr bit (13). - */ - apicicr_low = (vector | (1 << 13)); - - mic_mmio_write(mw, mic_x100_get_apic_id(mdev), - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); - - /* Ensure that the interrupt is ordered w.r.t. previous stores. */ - wmb(); - mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); -} - -/** - * mic_x100_hw_reset - Reset the MIC device. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_hw_reset(struct mic_device *mdev) -{ - u32 reset_reg; - u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; - struct mic_mw *mw = &mdev->mmio; - - /* Ensure that the reset is ordered w.r.t. previous loads and stores */ - mb(); - /* Trigger reset */ - reset_reg = mic_mmio_read(mw, rgcr); - reset_reg |= 0x1; - mic_mmio_write(mw, reset_reg, rgcr); - /* - * It seems we really want to delay at least 1 second - * after touching reset to prevent a lot of problems. - */ - msleep(1000); -} - -/** - * mic_x100_load_command_line - Load command line to MIC. - * @mdev: pointer to mic_device instance - * @fw: the firmware image - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) -{ - u32 len = 0; - u32 boot_mem; - char *buf; - void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size; -#define CMDLINE_SIZE 2048 - - boot_mem = mdev->aper.len >> 20; - buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - len += scnprintf(buf, CMDLINE_SIZE - len, - " mem=%dM", boot_mem); - if (mdev->cosm_dev->cmdline) - scnprintf(buf + len, CMDLINE_SIZE - len, " %s", - mdev->cosm_dev->cmdline); - memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); - kfree(buf); - return 0; -} - -/** - * mic_x100_load_ramdisk - Load ramdisk to MIC. - * @mdev: pointer to mic_device instance - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_load_ramdisk(struct mic_device *mdev) -{ - const struct firmware *fw; - int rc; - struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr; - - rc = request_firmware(&fw, mdev->cosm_dev->ramdisk, &mdev->pdev->dev); - if (rc < 0) { - dev_err(&mdev->pdev->dev, - "ramdisk request_firmware failed: %d %s\n", - rc, mdev->cosm_dev->ramdisk); - goto error; - } - /* - * Typically the bootaddr for card OS is 64M - * so copy over the ramdisk @ 128M. - */ - memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size); - iowrite32(mdev->bootaddr << 1, &bp->hdr.ramdisk_image); - iowrite32(fw->size, &bp->hdr.ramdisk_size); - release_firmware(fw); -error: - return rc; -} - -/** - * mic_x100_get_boot_addr - Get MIC boot address. - * @mdev: pointer to mic_device instance - * - * This function is called during firmware load to determine - * the address at which the OS should be downloaded in card - * memory i.e. GDDR. - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_get_boot_addr(struct mic_device *mdev) -{ - u32 scratch2, boot_addr; - int rc = 0; - - scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); - boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2); - dev_dbg(&mdev->pdev->dev, "%s %d boot_addr 0x%x\n", - __func__, __LINE__, boot_addr); - if (boot_addr > (1 << 31)) { - dev_err(&mdev->pdev->dev, - "incorrect bootaddr 0x%x\n", - boot_addr); - rc = -EINVAL; - goto error; - } - mdev->bootaddr = boot_addr; -error: - return rc; -} - -/** - * mic_x100_load_firmware - Load firmware to MIC. - * @mdev: pointer to mic_device instance - * @buf: buffer containing boot string including firmware/ramdisk path. - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_load_firmware(struct mic_device *mdev, const char *buf) -{ - int rc; - const struct firmware *fw; - - rc = mic_x100_get_boot_addr(mdev); - if (rc) - return rc; - /* load OS */ - rc = request_firmware(&fw, mdev->cosm_dev->firmware, &mdev->pdev->dev); - if (rc < 0) { - dev_err(&mdev->pdev->dev, - "ramdisk request_firmware failed: %d %s\n", - rc, mdev->cosm_dev->firmware); - return rc; - } - if (mdev->bootaddr > mdev->aper.len - fw->size) { - rc = -EINVAL; - dev_err(&mdev->pdev->dev, "%s %d rc %d bootaddr 0x%x\n", - __func__, __LINE__, rc, mdev->bootaddr); - goto error; - } - memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); - mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); - if (!strcmp(mdev->cosm_dev->bootmode, "flash")) { - rc = -EINVAL; - dev_err(&mdev->pdev->dev, "%s %d rc %d\n", - __func__, __LINE__, rc); - goto error; - } - /* load command line */ - rc = mic_x100_load_command_line(mdev, fw); - if (rc) { - dev_err(&mdev->pdev->dev, "%s %d rc %d\n", - __func__, __LINE__, rc); - goto error; - } - release_firmware(fw); - /* load ramdisk */ - if (mdev->cosm_dev->ramdisk) - rc = mic_x100_load_ramdisk(mdev); - - return rc; - -error: - release_firmware(fw); - return rc; -} - -/** - * mic_x100_get_postcode - Get postcode status from firmware. - * @mdev: pointer to mic_device instance - * - * RETURNS: postcode. - */ -static u32 mic_x100_get_postcode(struct mic_device *mdev) -{ - return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE); -} - -/** - * mic_x100_smpt_set - Update an SMPT entry with a DMA address. - * @mdev: pointer to mic_device instance - * @dma_addr: DMA address to use - * @index: entry to write to - * - * RETURNS: none. - */ -static void -mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) -{ -#define SNOOP_ON (0 << 0) -#define SNOOP_OFF (1 << 0) -/* - * Sbox Smpt Reg Bits: - * Bits 31:2 Host address - * Bits 1 RSVD - * Bits 0 No snoop - */ -#define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \ - (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01)) - - uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, - dma_addr >> mdev->smpt->info.page_shift); - mic_mmio_write(&mdev->mmio, smpt_reg_val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SMPT00 + (4 * index)); -} - -/** - * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields. - * @mdev: pointer to mic_device instance - * - * RETURNS: none. - */ -static void mic_x100_smpt_hw_init(struct mic_device *mdev) -{ - struct mic_smpt_hw_info *info = &mdev->smpt->info; - - info->num_reg = 32; - info->page_shift = 34; - info->page_size = (1ULL << info->page_shift); - info->base = 0x8000000000ULL; -} - -struct mic_smpt_ops mic_x100_smpt_ops = { - .init = mic_x100_smpt_hw_init, - .set = mic_x100_smpt_set, -}; - -static bool mic_x100_dma_filter(struct dma_chan *chan, void *param) -{ - if (chan->device->dev->parent == (struct device *)param) - return true; - return false; -} - -struct mic_hw_ops mic_x100_ops = { - .aper_bar = MIC_X100_APER_BAR, - .mmio_bar = MIC_X100_MMIO_BAR, - .read_spad = mic_x100_read_spad, - .write_spad = mic_x100_write_spad, - .send_intr = mic_x100_send_intr, - .ack_interrupt = mic_x100_ack_interrupt, - .intr_workarounds = mic_x100_intr_workarounds, - .reset = mic_x100_hw_reset, - .reset_fw_ready = mic_x100_reset_fw_ready, - .is_fw_ready = mic_x100_is_fw_ready, - .send_firmware_intr = mic_x100_send_firmware_intr, - .load_mic_fw = mic_x100_load_firmware, - .get_postcode = mic_x100_get_postcode, - .dma_filter = mic_x100_dma_filter, -}; - -struct mic_hw_intr_ops mic_x100_intr_ops = { - .intr_init = mic_x100_hw_intr_init, - .enable_interrupts = mic_x100_enable_interrupts, - .disable_interrupts = mic_x100_disable_interrupts, - .program_msi_to_src_map = mic_x100_program_msi_to_src_map, - .read_msi_to_src_map = mic_x100_read_msi_to_src_map, -}; diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h deleted file mode 100644 index aebcaed6fa72..000000000000 --- a/drivers/misc/mic/host/mic_x100.h +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef _MIC_X100_HW_H_ -#define _MIC_X100_HW_H_ - -#define MIC_X100_PCI_DEVICE_2250 0x2250 -#define MIC_X100_PCI_DEVICE_2251 0x2251 -#define MIC_X100_PCI_DEVICE_2252 0x2252 -#define MIC_X100_PCI_DEVICE_2253 0x2253 -#define MIC_X100_PCI_DEVICE_2254 0x2254 -#define MIC_X100_PCI_DEVICE_2255 0x2255 -#define MIC_X100_PCI_DEVICE_2256 0x2256 -#define MIC_X100_PCI_DEVICE_2257 0x2257 -#define MIC_X100_PCI_DEVICE_2258 0x2258 -#define MIC_X100_PCI_DEVICE_2259 0x2259 -#define MIC_X100_PCI_DEVICE_225a 0x225a -#define MIC_X100_PCI_DEVICE_225b 0x225b -#define MIC_X100_PCI_DEVICE_225c 0x225c -#define MIC_X100_PCI_DEVICE_225d 0x225d -#define MIC_X100_PCI_DEVICE_225e 0x225e - -#define MIC_X100_APER_BAR 0 -#define MIC_X100_MMIO_BAR 4 - -#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 -#define MIC_X100_SBOX_SPAD0 0x0000AB20 -#define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf) -#define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff) -#define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf) -#define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf) -#define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff) -#define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8) - -#define MIC_X100_SBOX_APICICR0 0x0000A9D0 -#define MIC_X100_SBOX_SICR0 0x00009004 -#define MIC_X100_SBOX_SICE0 0x0000900C -#define MIC_X100_SBOX_SICC0 0x00009010 -#define MIC_X100_SBOX_SIAC0 0x00009014 -#define MIC_X100_SBOX_MSIXPBACR 0x00009084 -#define MIC_X100_SBOX_MXAR0 0x00009044 -#define MIC_X100_SBOX_SMPT00 0x00003100 -#define MIC_X100_SBOX_RDMASR0 0x0000B180 - -#define MIC_X100_DOORBELL_IDX_START 0 -#define MIC_X100_NUM_DOORBELL 4 -#define MIC_X100_DMA_IDX_START 8 -#define MIC_X100_NUM_DMA 8 -#define MIC_X100_ERR_IDX_START 30 -#define MIC_X100_NUM_ERR 1 - -#define MIC_X100_NUM_SBOX_IRQ 8 -#define MIC_X100_NUM_RDMASR_IRQ 8 -#define MIC_X100_RDMASR_IRQ_BASE 17 -#define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1) -#define MIC_X100_SPAD2_APIC_ID(x) (((x) >> 1) & 0x1ff) -#define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000) -#define MIC_X100_SBOX_APICICR7 0x0000AA08 -#define MIC_X100_SBOX_RGCR 0x00004010 -#define MIC_X100_SBOX_SDBIC0 0x0000CC90 -#define MIC_X100_DOWNLOAD_INFO 2 -#define MIC_X100_FW_SIZE 5 -#define MIC_X100_POSTCODE 0x242c - -/* Host->Card(bootstrap) Interrupt Vector */ -#define MIC_X100_BSP_INTERRUPT_VECTOR 229 - -extern struct mic_hw_ops mic_x100_ops; -extern struct mic_smpt_ops mic_x100_smpt_ops; -extern struct mic_hw_intr_ops mic_x100_intr_ops; - -#endif diff --git a/drivers/misc/mic/scif/Makefile b/drivers/misc/mic/scif/Makefile deleted file mode 100644 index ff372555d118..000000000000 --- a/drivers/misc/mic/scif/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - SCIF driver. -# Copyright(c) 2014, Intel Corporation. -# -obj-$(CONFIG_SCIF) += scif.o -scif-objs := scif_main.o -scif-objs += scif_peer_bus.o -scif-objs += scif_ports.o -scif-objs += scif_debugfs.o -scif-objs += scif_fd.o -scif-objs += scif_api.o -scif-objs += scif_epd.o -scif-objs += scif_rb.o -scif-objs += scif_nodeqp.o -scif-objs += scif_nm.o -scif-objs += scif_dma.o -scif-objs += scif_fence.o -scif-objs += scif_mmap.o -scif-objs += scif_rma.o -scif-objs += scif_rma_list.o diff --git a/drivers/misc/mic/scif/scif_api.c b/drivers/misc/mic/scif/scif_api.c deleted file mode 100644 index 304d6c833712..000000000000 --- a/drivers/misc/mic/scif/scif_api.c +++ /dev/null @@ -1,1485 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include <linux/scif.h> -#include "scif_main.h" -#include "scif_map.h" - -static const char * const scif_ep_states[] = { - "Unbound", - "Bound", - "Listening", - "Connected", - "Connecting", - "Mapping", - "Closing", - "Close Listening", - "Disconnected", - "Zombie"}; - -enum conn_async_state { - ASYNC_CONN_IDLE = 1, /* ep setup for async connect */ - ASYNC_CONN_INPROGRESS, /* async connect in progress */ - ASYNC_CONN_FLUSH_WORK /* async work flush in progress */ -}; - -/* - * File operations for anonymous inode file associated with a SCIF endpoint, - * used in kernel mode SCIF poll. Kernel mode SCIF poll calls portions of the - * poll API in the kernel and these take in a struct file *. Since a struct - * file is not available to kernel mode SCIF, it uses an anonymous file for - * this purpose. - */ -const struct file_operations scif_anon_fops = { - .owner = THIS_MODULE, -}; - -scif_epd_t scif_open(void) -{ - struct scif_endpt *ep; - int err; - - might_sleep(); - ep = kzalloc(sizeof(*ep), GFP_KERNEL); - if (!ep) - goto err_ep_alloc; - - ep->qp_info.qp = kzalloc(sizeof(*ep->qp_info.qp), GFP_KERNEL); - if (!ep->qp_info.qp) - goto err_qp_alloc; - - err = scif_anon_inode_getfile(ep); - if (err) - goto err_anon_inode; - - spin_lock_init(&ep->lock); - mutex_init(&ep->sendlock); - mutex_init(&ep->recvlock); - - scif_rma_ep_init(ep); - ep->state = SCIFEP_UNBOUND; - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI open: ep %p success\n", ep); - return ep; - -err_anon_inode: - kfree(ep->qp_info.qp); -err_qp_alloc: - kfree(ep); -err_ep_alloc: - return NULL; -} -EXPORT_SYMBOL_GPL(scif_open); - -/* - * scif_disconnect_ep - Disconnects the endpoint if found - * @epd: The end point returned from scif_open() - */ -static struct scif_endpt *scif_disconnect_ep(struct scif_endpt *ep) -{ - struct scifmsg msg; - struct scif_endpt *fep = NULL; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - int err; - - /* - * Wake up any threads blocked in send()/recv() before closing - * out the connection. Grabbing and releasing the send/recv lock - * will ensure that any blocked senders/receivers have exited for - * Ring 0 endpoints. It is a Ring 0 bug to call send/recv after - * close. Ring 3 endpoints are not affected since close will not - * be called while there are IOCTLs executing. - */ - wake_up_interruptible(&ep->sendwq); - wake_up_interruptible(&ep->recvwq); - mutex_lock(&ep->sendlock); - mutex_unlock(&ep->sendlock); - mutex_lock(&ep->recvlock); - mutex_unlock(&ep->recvlock); - - /* Remove from the connected list */ - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.connected) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) { - list_del(pos); - fep = tmpep; - spin_lock(&ep->lock); - break; - } - } - - if (!fep) { - /* - * The other side has completed the disconnect before - * the end point can be removed from the list. Therefore - * the ep lock is not locked, traverse the disconnected - * list to find the endpoint and release the conn lock. - */ - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.connlock); - return NULL; - } - - init_completion(&ep->discon); - msg.uop = SCIF_DISCNCT; - msg.src = ep->port; - msg.dst = ep->peer; - msg.payload[0] = (u64)ep; - msg.payload[1] = ep->remote_ep; - - err = scif_nodeqp_send(ep->remote_dev, &msg); - spin_unlock(&ep->lock); - mutex_unlock(&scif_info.connlock); - - if (!err) - /* Wait for the remote node to respond with SCIF_DISCNT_ACK */ - wait_for_completion_timeout(&ep->discon, - SCIF_NODE_ALIVE_TIMEOUT); - return ep; -} - -int scif_close(scif_epd_t epd) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - enum scif_epd_state oldstate; - bool flush_conn; - - dev_dbg(scif_info.mdev.this_device, "SCIFAPI close: ep %p %s\n", - ep, scif_ep_states[ep->state]); - might_sleep(); - spin_lock(&ep->lock); - flush_conn = (ep->conn_async_state == ASYNC_CONN_INPROGRESS); - spin_unlock(&ep->lock); - - if (flush_conn) - flush_work(&scif_info.conn_work); - - spin_lock(&ep->lock); - oldstate = ep->state; - - ep->state = SCIFEP_CLOSING; - - switch (oldstate) { - case SCIFEP_ZOMBIE: - dev_err(scif_info.mdev.this_device, - "SCIFAPI close: zombie state unexpected\n"); - fallthrough; - case SCIFEP_DISCONNECTED: - spin_unlock(&ep->lock); - scif_unregister_all_windows(epd); - /* Remove from the disconnected list */ - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.connlock); - break; - case SCIFEP_UNBOUND: - case SCIFEP_BOUND: - case SCIFEP_CONNECTING: - spin_unlock(&ep->lock); - break; - case SCIFEP_MAPPING: - case SCIFEP_CONNECTED: - case SCIFEP_CLOSING: - { - spin_unlock(&ep->lock); - scif_unregister_all_windows(epd); - scif_disconnect_ep(ep); - break; - } - case SCIFEP_LISTENING: - case SCIFEP_CLLISTEN: - { - struct scif_conreq *conreq; - struct scifmsg msg; - struct scif_endpt *aep; - - spin_unlock(&ep->lock); - mutex_lock(&scif_info.eplock); - - /* remove from listen list */ - list_for_each_safe(pos, tmpq, &scif_info.listen) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) - list_del(pos); - } - /* Remove any dangling accepts */ - while (ep->acceptcnt) { - aep = list_first_entry(&ep->li_accept, - struct scif_endpt, liacceptlist); - list_del(&aep->liacceptlist); - scif_put_port(aep->port.port); - list_for_each_safe(pos, tmpq, &scif_info.uaccept) { - tmpep = list_entry(pos, struct scif_endpt, - miacceptlist); - if (tmpep == aep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.eplock); - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.connected) { - tmpep = list_entry(pos, - struct scif_endpt, list); - if (tmpep == aep) { - list_del(pos); - break; - } - } - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - tmpep = list_entry(pos, - struct scif_endpt, list); - if (tmpep == aep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.connlock); - scif_teardown_ep(aep); - mutex_lock(&scif_info.eplock); - scif_add_epd_to_zombie_list(aep, SCIF_EPLOCK_HELD); - ep->acceptcnt--; - } - - spin_lock(&ep->lock); - mutex_unlock(&scif_info.eplock); - - /* Remove and reject any pending connection requests. */ - while (ep->conreqcnt) { - conreq = list_first_entry(&ep->conlist, - struct scif_conreq, list); - list_del(&conreq->list); - - msg.uop = SCIF_CNCT_REJ; - msg.dst.node = conreq->msg.src.node; - msg.dst.port = conreq->msg.src.port; - msg.payload[0] = conreq->msg.payload[0]; - msg.payload[1] = conreq->msg.payload[1]; - /* - * No Error Handling on purpose for scif_nodeqp_send(). - * If the remote node is lost we still want free the - * connection requests on the self node. - */ - scif_nodeqp_send(&scif_dev[conreq->msg.src.node], - &msg); - ep->conreqcnt--; - kfree(conreq); - } - - spin_unlock(&ep->lock); - /* If a kSCIF accept is waiting wake it up */ - wake_up_interruptible(&ep->conwq); - break; - } - } - scif_put_port(ep->port.port); - scif_anon_inode_fput(ep); - scif_teardown_ep(ep); - scif_add_epd_to_zombie_list(ep, !SCIF_EPLOCK_HELD); - return 0; -} -EXPORT_SYMBOL_GPL(scif_close); - -/** - * scif_flush() - Wakes up any blocking accepts. The endpoint will no longer - * accept new connections. - * @epd: The end point returned from scif_open() - */ -int __scif_flush(scif_epd_t epd) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - - switch (ep->state) { - case SCIFEP_LISTENING: - { - ep->state = SCIFEP_CLLISTEN; - - /* If an accept is waiting wake it up */ - wake_up_interruptible(&ep->conwq); - break; - } - default: - break; - } - return 0; -} - -int scif_bind(scif_epd_t epd, u16 pn) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int ret = 0; - int tmp; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI bind: ep %p %s requested port number %d\n", - ep, scif_ep_states[ep->state], pn); - if (pn) { - /* - * Similar to IETF RFC 1700, SCIF ports below - * SCIF_ADMIN_PORT_END can only be bound by system (or root) - * processes or by processes executed by privileged users. - */ - if (pn < SCIF_ADMIN_PORT_END && !capable(CAP_SYS_ADMIN)) { - ret = -EACCES; - goto scif_bind_admin_exit; - } - } - - spin_lock(&ep->lock); - if (ep->state == SCIFEP_BOUND) { - ret = -EINVAL; - goto scif_bind_exit; - } else if (ep->state != SCIFEP_UNBOUND) { - ret = -EISCONN; - goto scif_bind_exit; - } - - if (pn) { - tmp = scif_rsrv_port(pn); - if (tmp != pn) { - ret = -EINVAL; - goto scif_bind_exit; - } - } else { - ret = scif_get_new_port(); - if (ret < 0) - goto scif_bind_exit; - pn = ret; - } - - ep->state = SCIFEP_BOUND; - ep->port.node = scif_info.nodeid; - ep->port.port = pn; - ep->conn_async_state = ASYNC_CONN_IDLE; - ret = pn; - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI bind: bound to port number %d\n", pn); -scif_bind_exit: - spin_unlock(&ep->lock); -scif_bind_admin_exit: - return ret; -} -EXPORT_SYMBOL_GPL(scif_bind); - -int scif_listen(scif_epd_t epd, int backlog) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI listen: ep %p %s\n", ep, scif_ep_states[ep->state]); - spin_lock(&ep->lock); - switch (ep->state) { - case SCIFEP_ZOMBIE: - case SCIFEP_CLOSING: - case SCIFEP_CLLISTEN: - case SCIFEP_UNBOUND: - case SCIFEP_DISCONNECTED: - spin_unlock(&ep->lock); - return -EINVAL; - case SCIFEP_LISTENING: - case SCIFEP_CONNECTED: - case SCIFEP_CONNECTING: - case SCIFEP_MAPPING: - spin_unlock(&ep->lock); - return -EISCONN; - case SCIFEP_BOUND: - break; - } - - ep->state = SCIFEP_LISTENING; - ep->backlog = backlog; - - ep->conreqcnt = 0; - ep->acceptcnt = 0; - INIT_LIST_HEAD(&ep->conlist); - init_waitqueue_head(&ep->conwq); - INIT_LIST_HEAD(&ep->li_accept); - spin_unlock(&ep->lock); - - /* - * Listen status is complete so delete the qp information not needed - * on a listen before placing on the list of listening ep's - */ - scif_teardown_ep(ep); - ep->qp_info.qp = NULL; - - mutex_lock(&scif_info.eplock); - list_add_tail(&ep->list, &scif_info.listen); - mutex_unlock(&scif_info.eplock); - return 0; -} -EXPORT_SYMBOL_GPL(scif_listen); - -/* - ************************************************************************ - * SCIF connection flow: - * - * 1) A SCIF listening endpoint can call scif_accept(..) to wait for SCIF - * connections via a SCIF_CNCT_REQ message - * 2) A SCIF endpoint can initiate a SCIF connection by calling - * scif_connect(..) which calls scif_setup_qp_connect(..) which - * allocates the local qp for the endpoint ring buffer and then sends - * a SCIF_CNCT_REQ to the remote node and waits for a SCIF_CNCT_GNT or - * a SCIF_CNCT_REJ message - * 3) The peer node handles a SCIF_CNCT_REQ via scif_cnctreq_resp(..) which - * wakes up any threads blocked in step 1 or sends a SCIF_CNCT_REJ - * message otherwise - * 4) A thread blocked waiting for incoming connections allocates its local - * endpoint QP and ring buffer following which it sends a SCIF_CNCT_GNT - * and waits for a SCIF_CNCT_GNT(N)ACK. If the allocation fails then - * the node sends a SCIF_CNCT_REJ message - * 5) Upon receipt of a SCIF_CNCT_GNT or a SCIF_CNCT_REJ message the - * connecting endpoint is woken up as part of handling - * scif_cnctgnt_resp(..) following which it maps the remote endpoints' - * QP, updates its outbound QP and sends a SCIF_CNCT_GNTACK message on - * success or a SCIF_CNCT_GNTNACK message on failure and completes - * the scif_connect(..) API - * 6) Upon receipt of a SCIF_CNCT_GNT(N)ACK the accepting endpoint blocked - * in step 4 is woken up and completes the scif_accept(..) API - * 7) The SCIF connection is now established between the two SCIF endpoints. - */ -static int scif_conn_func(struct scif_endpt *ep) -{ - int err = 0; - struct scifmsg msg; - struct device *spdev; - - err = scif_reserve_dma_chan(ep); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - ep->state = SCIFEP_BOUND; - goto connect_error_simple; - } - /* Initiate the first part of the endpoint QP setup */ - err = scif_setup_qp_connect(ep->qp_info.qp, &ep->qp_info.qp_offset, - SCIF_ENDPT_QP_SIZE, ep->remote_dev); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s err %d qp_offset 0x%llx\n", - __func__, err, ep->qp_info.qp_offset); - ep->state = SCIFEP_BOUND; - goto connect_error_simple; - } - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto cleanup_qp; - } - /* Format connect message and send it */ - msg.src = ep->port; - msg.dst = ep->conn_port; - msg.uop = SCIF_CNCT_REQ; - msg.payload[0] = (u64)ep; - msg.payload[1] = ep->qp_info.qp_offset; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - if (err) - goto connect_error_dec; - scif_put_peer_dev(spdev); - /* - * Wait for the remote node to respond with SCIF_CNCT_GNT or - * SCIF_CNCT_REJ message. - */ - err = wait_event_timeout(ep->conwq, ep->state != SCIFEP_CONNECTING, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d timeout\n", __func__, __LINE__); - ep->state = SCIFEP_BOUND; - } - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto cleanup_qp; - } - if (ep->state == SCIFEP_MAPPING) { - err = scif_setup_qp_connect_response(ep->remote_dev, - ep->qp_info.qp, - ep->qp_info.gnt_pld); - /* - * If the resource to map the queue are not available then - * we need to tell the other side to terminate the accept - */ - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - msg.uop = SCIF_CNCT_GNTNACK; - msg.payload[0] = ep->remote_ep; - _scif_nodeqp_send(ep->remote_dev, &msg); - ep->state = SCIFEP_BOUND; - goto connect_error_dec; - } - - msg.uop = SCIF_CNCT_GNTACK; - msg.payload[0] = ep->remote_ep; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - if (err) { - ep->state = SCIFEP_BOUND; - goto connect_error_dec; - } - ep->state = SCIFEP_CONNECTED; - mutex_lock(&scif_info.connlock); - list_add_tail(&ep->list, &scif_info.connected); - mutex_unlock(&scif_info.connlock); - dev_dbg(&ep->remote_dev->sdev->dev, - "SCIFAPI connect: ep %p connected\n", ep); - } else if (ep->state == SCIFEP_BOUND) { - dev_dbg(&ep->remote_dev->sdev->dev, - "SCIFAPI connect: ep %p connection refused\n", ep); - err = -ECONNREFUSED; - goto connect_error_dec; - } - scif_put_peer_dev(spdev); - return err; -connect_error_dec: - scif_put_peer_dev(spdev); -cleanup_qp: - scif_cleanup_ep_qp(ep); -connect_error_simple: - return err; -} - -/* - * scif_conn_handler: - * - * Workqueue handler for servicing non-blocking SCIF connect - * - */ -void scif_conn_handler(struct work_struct *work) -{ - struct scif_endpt *ep; - - do { - ep = NULL; - spin_lock(&scif_info.nb_connect_lock); - if (!list_empty(&scif_info.nb_connect_list)) { - ep = list_first_entry(&scif_info.nb_connect_list, - struct scif_endpt, conn_list); - list_del(&ep->conn_list); - } - spin_unlock(&scif_info.nb_connect_lock); - if (ep) { - ep->conn_err = scif_conn_func(ep); - wake_up_interruptible(&ep->conn_pend_wq); - } - } while (ep); -} - -int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - struct scif_dev *remote_dev; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, "SCIFAPI connect: ep %p %s\n", ep, - scif_ep_states[ep->state]); - - if (!scif_dev || dst->node > scif_info.maxid) - return -ENODEV; - - might_sleep(); - - remote_dev = &scif_dev[dst->node]; - spdev = scif_get_peer_dev(remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - - spin_lock(&ep->lock); - switch (ep->state) { - case SCIFEP_ZOMBIE: - case SCIFEP_CLOSING: - err = -EINVAL; - break; - case SCIFEP_DISCONNECTED: - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) - ep->conn_async_state = ASYNC_CONN_FLUSH_WORK; - else - err = -EINVAL; - break; - case SCIFEP_LISTENING: - case SCIFEP_CLLISTEN: - err = -EOPNOTSUPP; - break; - case SCIFEP_CONNECTING: - case SCIFEP_MAPPING: - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) - err = -EINPROGRESS; - else - err = -EISCONN; - break; - case SCIFEP_CONNECTED: - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) - ep->conn_async_state = ASYNC_CONN_FLUSH_WORK; - else - err = -EISCONN; - break; - case SCIFEP_UNBOUND: - err = scif_get_new_port(); - if (err < 0) - break; - ep->port.port = err; - ep->port.node = scif_info.nodeid; - ep->conn_async_state = ASYNC_CONN_IDLE; - fallthrough; - case SCIFEP_BOUND: - /* - * If a non-blocking connect has been already initiated - * (conn_async_state is either ASYNC_CONN_INPROGRESS or - * ASYNC_CONN_FLUSH_WORK), the end point could end up in - * SCIF_BOUND due an error in the connection process - * (e.g., connection refused) If conn_async_state is - * ASYNC_CONN_INPROGRESS - transition to ASYNC_CONN_FLUSH_WORK - * so that the error status can be collected. If the state is - * already ASYNC_CONN_FLUSH_WORK - then set the error to - * EINPROGRESS since some other thread is waiting to collect - * error status. - */ - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - ep->conn_async_state = ASYNC_CONN_FLUSH_WORK; - } else if (ep->conn_async_state == ASYNC_CONN_FLUSH_WORK) { - err = -EINPROGRESS; - } else { - ep->conn_port = *dst; - init_waitqueue_head(&ep->sendwq); - init_waitqueue_head(&ep->recvwq); - init_waitqueue_head(&ep->conwq); - ep->conn_async_state = 0; - - if (unlikely(non_block)) - ep->conn_async_state = ASYNC_CONN_INPROGRESS; - } - break; - } - - if (err || ep->conn_async_state == ASYNC_CONN_FLUSH_WORK) - goto connect_simple_unlock1; - - ep->state = SCIFEP_CONNECTING; - ep->remote_dev = &scif_dev[dst->node]; - ep->qp_info.qp->magic = SCIFEP_MAGIC; - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - init_waitqueue_head(&ep->conn_pend_wq); - spin_lock(&scif_info.nb_connect_lock); - list_add_tail(&ep->conn_list, &scif_info.nb_connect_list); - spin_unlock(&scif_info.nb_connect_lock); - err = -EINPROGRESS; - schedule_work(&scif_info.conn_work); - } -connect_simple_unlock1: - spin_unlock(&ep->lock); - scif_put_peer_dev(spdev); - if (err) { - return err; - } else if (ep->conn_async_state == ASYNC_CONN_FLUSH_WORK) { - flush_work(&scif_info.conn_work); - err = ep->conn_err; - spin_lock(&ep->lock); - ep->conn_async_state = ASYNC_CONN_IDLE; - spin_unlock(&ep->lock); - } else { - err = scif_conn_func(ep); - } - return err; -} - -int scif_connect(scif_epd_t epd, struct scif_port_id *dst) -{ - return __scif_connect(epd, dst, false); -} -EXPORT_SYMBOL_GPL(scif_connect); - -/* - * scif_accept() - Accept a connection request from the remote node - * - * The function accepts a connection request from the remote node. Successful - * complete is indicate by a new end point being created and passed back - * to the caller for future reference. - * - * Upon successful complete a zero will be returned and the peer information - * will be filled in. - * - * If the end point is not in the listening state -EINVAL will be returned. - * - * If during the connection sequence resource allocation fails the -ENOMEM - * will be returned. - * - * If the function is called with the ASYNC flag set and no connection requests - * are pending it will return -EAGAIN. - * - * If the remote side is not sending any connection requests the caller may - * terminate this function with a signal. If so a -EINTR will be returned. - */ -int scif_accept(scif_epd_t epd, struct scif_port_id *peer, - scif_epd_t *newepd, int flags) -{ - struct scif_endpt *lep = (struct scif_endpt *)epd; - struct scif_endpt *cep; - struct scif_conreq *conreq; - struct scifmsg msg; - int err; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI accept: ep %p %s\n", lep, scif_ep_states[lep->state]); - - if (flags & ~SCIF_ACCEPT_SYNC) - return -EINVAL; - - if (!peer || !newepd) - return -EINVAL; - - might_sleep(); - spin_lock(&lep->lock); - if (lep->state != SCIFEP_LISTENING) { - spin_unlock(&lep->lock); - return -EINVAL; - } - - if (!lep->conreqcnt && !(flags & SCIF_ACCEPT_SYNC)) { - /* No connection request present and we do not want to wait */ - spin_unlock(&lep->lock); - return -EAGAIN; - } - - lep->files = current->files; -retry_connection: - spin_unlock(&lep->lock); - /* Wait for the remote node to send us a SCIF_CNCT_REQ */ - err = wait_event_interruptible(lep->conwq, - (lep->conreqcnt || - (lep->state != SCIFEP_LISTENING))); - if (err) - return err; - - if (lep->state != SCIFEP_LISTENING) - return -EINTR; - - spin_lock(&lep->lock); - - if (!lep->conreqcnt) - goto retry_connection; - - /* Get the first connect request off the list */ - conreq = list_first_entry(&lep->conlist, struct scif_conreq, list); - list_del(&conreq->list); - lep->conreqcnt--; - spin_unlock(&lep->lock); - - /* Fill in the peer information */ - peer->node = conreq->msg.src.node; - peer->port = conreq->msg.src.port; - - cep = kzalloc(sizeof(*cep), GFP_KERNEL); - if (!cep) { - err = -ENOMEM; - goto scif_accept_error_epalloc; - } - spin_lock_init(&cep->lock); - mutex_init(&cep->sendlock); - mutex_init(&cep->recvlock); - cep->state = SCIFEP_CONNECTING; - cep->remote_dev = &scif_dev[peer->node]; - cep->remote_ep = conreq->msg.payload[0]; - - scif_rma_ep_init(cep); - - err = scif_reserve_dma_chan(cep); - if (err) { - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - goto scif_accept_error_qpalloc; - } - - cep->qp_info.qp = kzalloc(sizeof(*cep->qp_info.qp), GFP_KERNEL); - if (!cep->qp_info.qp) { - err = -ENOMEM; - goto scif_accept_error_qpalloc; - } - - err = scif_anon_inode_getfile(cep); - if (err) - goto scif_accept_error_anon_inode; - - cep->qp_info.qp->magic = SCIFEP_MAGIC; - spdev = scif_get_peer_dev(cep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto scif_accept_error_map; - } - err = scif_setup_qp_accept(cep->qp_info.qp, &cep->qp_info.qp_offset, - conreq->msg.payload[1], SCIF_ENDPT_QP_SIZE, - cep->remote_dev); - if (err) { - dev_dbg(&cep->remote_dev->sdev->dev, - "SCIFAPI accept: ep %p new %p scif_setup_qp_accept %d qp_offset 0x%llx\n", - lep, cep, err, cep->qp_info.qp_offset); - scif_put_peer_dev(spdev); - goto scif_accept_error_map; - } - - cep->port.node = lep->port.node; - cep->port.port = lep->port.port; - cep->peer.node = peer->node; - cep->peer.port = peer->port; - init_waitqueue_head(&cep->sendwq); - init_waitqueue_head(&cep->recvwq); - init_waitqueue_head(&cep->conwq); - - msg.uop = SCIF_CNCT_GNT; - msg.src = cep->port; - msg.payload[0] = cep->remote_ep; - msg.payload[1] = cep->qp_info.qp_offset; - msg.payload[2] = (u64)cep; - - err = _scif_nodeqp_send(cep->remote_dev, &msg); - scif_put_peer_dev(spdev); - if (err) - goto scif_accept_error_map; -retry: - /* Wait for the remote node to respond with SCIF_CNCT_GNT(N)ACK */ - err = wait_event_timeout(cep->conwq, cep->state != SCIFEP_CONNECTING, - SCIF_NODE_ACCEPT_TIMEOUT); - if (!err && scifdev_alive(cep)) - goto retry; - err = !err ? -ENODEV : 0; - if (err) - goto scif_accept_error_map; - kfree(conreq); - - spin_lock(&cep->lock); - - if (cep->state == SCIFEP_CLOSING) { - /* - * Remote failed to allocate resources and NAKed the grant. - * There is at this point nothing referencing the new end point. - */ - spin_unlock(&cep->lock); - scif_teardown_ep(cep); - kfree(cep); - - /* If call with sync flag then go back and wait. */ - if (flags & SCIF_ACCEPT_SYNC) { - spin_lock(&lep->lock); - goto retry_connection; - } - return -EAGAIN; - } - - scif_get_port(cep->port.port); - *newepd = (scif_epd_t)cep; - spin_unlock(&cep->lock); - return 0; -scif_accept_error_map: - scif_anon_inode_fput(cep); -scif_accept_error_anon_inode: - scif_teardown_ep(cep); -scif_accept_error_qpalloc: - kfree(cep); -scif_accept_error_epalloc: - msg.uop = SCIF_CNCT_REJ; - msg.dst.node = conreq->msg.src.node; - msg.dst.port = conreq->msg.src.port; - msg.payload[0] = conreq->msg.payload[0]; - msg.payload[1] = conreq->msg.payload[1]; - scif_nodeqp_send(&scif_dev[conreq->msg.src.node], &msg); - kfree(conreq); - return err; -} -EXPORT_SYMBOL_GPL(scif_accept); - -/* - * scif_msg_param_check: - * @epd: The end point returned from scif_open() - * @len: Length to receive - * @flags: blocking or non blocking - * - * Validate parameters for messaging APIs scif_send(..)/scif_recv(..). - */ -static inline int scif_msg_param_check(scif_epd_t epd, int len, int flags) -{ - int ret = -EINVAL; - - if (len < 0) - goto err_ret; - if (flags && (!(flags & SCIF_RECV_BLOCK))) - goto err_ret; - ret = 0; -err_ret: - return ret; -} - -static int _scif_send(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scifmsg notif_msg; - int curr_xfer_len = 0, sent_len = 0, write_count; - int ret = 0; - struct scif_qp *qp = ep->qp_info.qp; - - if (flags & SCIF_SEND_BLOCK) - might_sleep(); - - spin_lock(&ep->lock); - while (sent_len != len && SCIFEP_CONNECTED == ep->state) { - write_count = scif_rb_space(&qp->outbound_q); - if (write_count) { - /* Best effort to send as much data as possible */ - curr_xfer_len = min(len - sent_len, write_count); - ret = scif_rb_write(&qp->outbound_q, msg, - curr_xfer_len); - if (ret < 0) - break; - /* Success. Update write pointer */ - scif_rb_commit(&qp->outbound_q); - /* - * Send a notification to the peer about the - * produced data message. - */ - notif_msg.src = ep->port; - notif_msg.uop = SCIF_CLIENT_SENT; - notif_msg.payload[0] = ep->remote_ep; - ret = _scif_nodeqp_send(ep->remote_dev, ¬if_msg); - if (ret) - break; - sent_len += curr_xfer_len; - msg = msg + curr_xfer_len; - continue; - } - curr_xfer_len = min(len - sent_len, SCIF_ENDPT_QP_SIZE - 1); - /* Not enough RB space. return for the Non Blocking case */ - if (!(flags & SCIF_SEND_BLOCK)) - break; - - spin_unlock(&ep->lock); - /* Wait for a SCIF_CLIENT_RCVD message in the Blocking case */ - ret = - wait_event_interruptible(ep->sendwq, - (SCIFEP_CONNECTED != ep->state) || - (scif_rb_space(&qp->outbound_q) >= - curr_xfer_len)); - spin_lock(&ep->lock); - if (ret) - break; - } - if (sent_len) - ret = sent_len; - else if (!ret && SCIFEP_CONNECTED != ep->state) - ret = SCIFEP_DISCONNECTED == ep->state ? - -ECONNRESET : -ENOTCONN; - spin_unlock(&ep->lock); - return ret; -} - -static int _scif_recv(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scifmsg notif_msg; - int curr_recv_len = 0, remaining_len = len, read_count; - int ret = 0; - struct scif_qp *qp = ep->qp_info.qp; - - if (flags & SCIF_RECV_BLOCK) - might_sleep(); - spin_lock(&ep->lock); - while (remaining_len && (SCIFEP_CONNECTED == ep->state || - SCIFEP_DISCONNECTED == ep->state)) { - read_count = scif_rb_count(&qp->inbound_q, remaining_len); - if (read_count) { - /* - * Best effort to recv as much data as there - * are bytes to read in the RB particularly - * important for the Non Blocking case. - */ - curr_recv_len = min(remaining_len, read_count); - scif_rb_get_next(&qp->inbound_q, msg, curr_recv_len); - if (ep->state == SCIFEP_CONNECTED) { - /* - * Update the read pointer only if the endpoint - * is still connected else the read pointer - * might no longer exist since the peer has - * freed resources! - */ - scif_rb_update_read_ptr(&qp->inbound_q); - /* - * Send a notification to the peer about the - * consumed data message only if the EP is in - * SCIFEP_CONNECTED state. - */ - notif_msg.src = ep->port; - notif_msg.uop = SCIF_CLIENT_RCVD; - notif_msg.payload[0] = ep->remote_ep; - ret = _scif_nodeqp_send(ep->remote_dev, - ¬if_msg); - if (ret) - break; - } - remaining_len -= curr_recv_len; - msg = msg + curr_recv_len; - continue; - } - /* - * Bail out now if the EP is in SCIFEP_DISCONNECTED state else - * we will keep looping forever. - */ - if (ep->state == SCIFEP_DISCONNECTED) - break; - /* - * Return in the Non Blocking case if there is no data - * to read in this iteration. - */ - if (!(flags & SCIF_RECV_BLOCK)) - break; - curr_recv_len = min(remaining_len, SCIF_ENDPT_QP_SIZE - 1); - spin_unlock(&ep->lock); - /* - * Wait for a SCIF_CLIENT_SEND message in the blocking case - * or until other side disconnects. - */ - ret = - wait_event_interruptible(ep->recvwq, - SCIFEP_CONNECTED != ep->state || - scif_rb_count(&qp->inbound_q, - curr_recv_len) - >= curr_recv_len); - spin_lock(&ep->lock); - if (ret) - break; - } - if (len - remaining_len) - ret = len - remaining_len; - else if (!ret && ep->state != SCIFEP_CONNECTED) - ret = ep->state == SCIFEP_DISCONNECTED ? - -ECONNRESET : -ENOTCONN; - spin_unlock(&ep->lock); - return ret; -} - -/** - * scif_user_send() - Send data to connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the driver IOCTL entry point - * only and is a wrapper for _scif_send(). - */ -int scif_user_send(scif_epd_t epd, void __user *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - int sent_len = 0; - char *tmp; - int loop_len; - int chunk_len = min(len, (1 << (MAX_ORDER + PAGE_SHIFT - 1))); - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI send (U): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - err = scif_msg_param_check(epd, len, flags); - if (err) - goto send_err; - - tmp = kmalloc(chunk_len, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto send_err; - } - /* - * Grabbing the lock before breaking up the transfer in - * multiple chunks is required to ensure that messages do - * not get fragmented and reordered. - */ - mutex_lock(&ep->sendlock); - while (sent_len != len) { - loop_len = len - sent_len; - loop_len = min(chunk_len, loop_len); - if (copy_from_user(tmp, msg, loop_len)) { - err = -EFAULT; - goto send_free_err; - } - err = _scif_send(epd, tmp, loop_len, flags); - if (err < 0) - goto send_free_err; - sent_len += err; - msg += err; - if (err != loop_len) - goto send_free_err; - } -send_free_err: - mutex_unlock(&ep->sendlock); - kfree(tmp); -send_err: - return err < 0 ? err : sent_len; -} - -/** - * scif_user_recv() - Receive data from connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the driver IOCTL entry point - * only and is a wrapper for _scif_recv(). - */ -int scif_user_recv(scif_epd_t epd, void __user *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - int recv_len = 0; - char *tmp; - int loop_len; - int chunk_len = min(len, (1 << (MAX_ORDER + PAGE_SHIFT - 1))); - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI recv (U): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - err = scif_msg_param_check(epd, len, flags); - if (err) - goto recv_err; - - tmp = kmalloc(chunk_len, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto recv_err; - } - /* - * Grabbing the lock before breaking up the transfer in - * multiple chunks is required to ensure that messages do - * not get fragmented and reordered. - */ - mutex_lock(&ep->recvlock); - while (recv_len != len) { - loop_len = len - recv_len; - loop_len = min(chunk_len, loop_len); - err = _scif_recv(epd, tmp, loop_len, flags); - if (err < 0) - goto recv_free_err; - if (copy_to_user(msg, tmp, err)) { - err = -EFAULT; - goto recv_free_err; - } - recv_len += err; - msg += err; - if (err != loop_len) - goto recv_free_err; - } -recv_free_err: - mutex_unlock(&ep->recvlock); - kfree(tmp); -recv_err: - return err < 0 ? err : recv_len; -} - -/** - * scif_send() - Send data to connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the kernel mode only and is - * a wrapper for _scif_send(). - */ -int scif_send(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int ret; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI send (K): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - ret = scif_msg_param_check(epd, len, flags); - if (ret) - return ret; - if (!ep->remote_dev) - return -ENOTCONN; - /* - * Grab the mutex lock in the blocking case only - * to ensure messages do not get fragmented/reordered. - * The non blocking mode is protected using spin locks - * in _scif_send(). - */ - if (flags & SCIF_SEND_BLOCK) - mutex_lock(&ep->sendlock); - - ret = _scif_send(epd, msg, len, flags); - - if (flags & SCIF_SEND_BLOCK) - mutex_unlock(&ep->sendlock); - return ret; -} -EXPORT_SYMBOL_GPL(scif_send); - -/** - * scif_recv() - Receive data from connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the kernel mode only and is - * a wrapper for _scif_recv(). - */ -int scif_recv(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int ret; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI recv (K): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - ret = scif_msg_param_check(epd, len, flags); - if (ret) - return ret; - /* - * Grab the mutex lock in the blocking case only - * to ensure messages do not get fragmented/reordered. - * The non blocking mode is protected using spin locks - * in _scif_send(). - */ - if (flags & SCIF_RECV_BLOCK) - mutex_lock(&ep->recvlock); - - ret = _scif_recv(epd, msg, len, flags); - - if (flags & SCIF_RECV_BLOCK) - mutex_unlock(&ep->recvlock); - - return ret; -} -EXPORT_SYMBOL_GPL(scif_recv); - -static inline void _scif_poll_wait(struct file *f, wait_queue_head_t *wq, - poll_table *p, struct scif_endpt *ep) -{ - /* - * Because poll_wait makes a GFP_KERNEL allocation, give up the lock - * and regrab it afterwards. Because the endpoint state might have - * changed while the lock was given up, the state must be checked - * again after re-acquiring the lock. The code in __scif_pollfd(..) - * does this. - */ - spin_unlock(&ep->lock); - poll_wait(f, wq, p); - spin_lock(&ep->lock); -} - -__poll_t -__scif_pollfd(struct file *f, poll_table *wait, struct scif_endpt *ep) -{ - __poll_t mask = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI pollfd: ep %p %s\n", ep, scif_ep_states[ep->state]); - - spin_lock(&ep->lock); - - /* Endpoint is waiting for a non-blocking connect to complete */ - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - _scif_poll_wait(f, &ep->conn_pend_wq, wait, ep); - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - if (ep->state == SCIFEP_CONNECTED || - ep->state == SCIFEP_DISCONNECTED || - ep->conn_err) - mask |= EPOLLOUT; - goto exit; - } - } - - /* Endpoint is listening for incoming connection requests */ - if (ep->state == SCIFEP_LISTENING) { - _scif_poll_wait(f, &ep->conwq, wait, ep); - if (ep->state == SCIFEP_LISTENING) { - if (ep->conreqcnt) - mask |= EPOLLIN; - goto exit; - } - } - - /* Endpoint is connected or disconnected */ - if (ep->state == SCIFEP_CONNECTED || ep->state == SCIFEP_DISCONNECTED) { - if (poll_requested_events(wait) & EPOLLIN) - _scif_poll_wait(f, &ep->recvwq, wait, ep); - if (poll_requested_events(wait) & EPOLLOUT) - _scif_poll_wait(f, &ep->sendwq, wait, ep); - if (ep->state == SCIFEP_CONNECTED || - ep->state == SCIFEP_DISCONNECTED) { - /* Data can be read without blocking */ - if (scif_rb_count(&ep->qp_info.qp->inbound_q, 1)) - mask |= EPOLLIN; - /* Data can be written without blocking */ - if (scif_rb_space(&ep->qp_info.qp->outbound_q)) - mask |= EPOLLOUT; - /* Return EPOLLHUP if endpoint is disconnected */ - if (ep->state == SCIFEP_DISCONNECTED) - mask |= EPOLLHUP; - goto exit; - } - } - - /* Return EPOLLERR if the endpoint is in none of the above states */ - mask |= EPOLLERR; -exit: - spin_unlock(&ep->lock); - return mask; -} - -/** - * scif_poll() - Kernel mode SCIF poll - * @ufds: Array of scif_pollepd structures containing the end points - * and events to poll on - * @nfds: Size of the ufds array - * @timeout_msecs: Timeout in msecs, -ve implies infinite timeout - * - * The code flow in this function is based on do_poll(..) in select.c - * - * Returns the number of endpoints which have pending events or 0 in - * the event of a timeout. If a signal is used for wake up, -EINTR is - * returned. - */ -int -scif_poll(struct scif_pollepd *ufds, unsigned int nfds, long timeout_msecs) -{ - struct poll_wqueues table; - poll_table *pt; - int i, count = 0, timed_out = timeout_msecs == 0; - __poll_t mask; - u64 timeout = timeout_msecs < 0 ? MAX_SCHEDULE_TIMEOUT - : msecs_to_jiffies(timeout_msecs); - - poll_initwait(&table); - pt = &table.pt; - while (1) { - for (i = 0; i < nfds; i++) { - pt->_key = ufds[i].events | EPOLLERR | EPOLLHUP; - mask = __scif_pollfd(ufds[i].epd->anon, - pt, ufds[i].epd); - mask &= ufds[i].events | EPOLLERR | EPOLLHUP; - if (mask) { - count++; - pt->_qproc = NULL; - } - ufds[i].revents = mask; - } - pt->_qproc = NULL; - if (!count) { - count = table.error; - if (signal_pending(current)) - count = -EINTR; - } - if (count || timed_out) - break; - - if (!schedule_timeout_interruptible(timeout)) - timed_out = 1; - } - poll_freewait(&table); - return count; -} -EXPORT_SYMBOL_GPL(scif_poll); - -int scif_get_node_ids(u16 *nodes, int len, u16 *self) -{ - int online = 0; - int offset = 0; - int node; - - if (!scif_is_mgmt_node()) - scif_get_node_info(); - - *self = scif_info.nodeid; - mutex_lock(&scif_info.conflock); - len = min_t(int, len, scif_info.total); - for (node = 0; node <= scif_info.maxid; node++) { - if (_scifdev_alive(&scif_dev[node])) { - online++; - if (offset < len) - nodes[offset++] = node; - } - } - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI get_node_ids total %d online %d filled in %d nodes\n", - scif_info.total, online, offset); - mutex_unlock(&scif_info.conflock); - - return online; -} -EXPORT_SYMBOL_GPL(scif_get_node_ids); - -static int scif_add_client_dev(struct device *dev, struct subsys_interface *si) -{ - struct scif_client *client = - container_of(si, struct scif_client, si); - struct scif_peer_dev *spdev = - container_of(dev, struct scif_peer_dev, dev); - - if (client->probe) - client->probe(spdev); - return 0; -} - -static void scif_remove_client_dev(struct device *dev, - struct subsys_interface *si) -{ - struct scif_client *client = - container_of(si, struct scif_client, si); - struct scif_peer_dev *spdev = - container_of(dev, struct scif_peer_dev, dev); - - if (client->remove) - client->remove(spdev); -} - -void scif_client_unregister(struct scif_client *client) -{ - subsys_interface_unregister(&client->si); -} -EXPORT_SYMBOL_GPL(scif_client_unregister); - -int scif_client_register(struct scif_client *client) -{ - struct subsys_interface *si = &client->si; - - si->name = client->name; - si->subsys = &scif_peer_bus; - si->add_dev = scif_add_client_dev; - si->remove_dev = scif_remove_client_dev; - - return subsys_interface_register(&client->si); -} -EXPORT_SYMBOL_GPL(scif_client_register); diff --git a/drivers/misc/mic/scif/scif_debugfs.c b/drivers/misc/mic/scif/scif_debugfs.c deleted file mode 100644 index 8fe38e7ca6e6..000000000000 --- a/drivers/misc/mic/scif/scif_debugfs.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include <linux/debugfs.h> -#include <linux/seq_file.h> - -#include "../common/mic_dev.h" -#include "scif_main.h" - -/* Debugfs parent dir */ -static struct dentry *scif_dbg; - -static int scif_dev_show(struct seq_file *s, void *unused) -{ - int node; - - seq_printf(s, "Total Nodes %d Self Node Id %d Maxid %d\n", - scif_info.total, scif_info.nodeid, - scif_info.maxid); - - if (!scif_dev) - return 0; - - seq_printf(s, "%-16s\t%-16s\n", "node_id", "state"); - - for (node = 0; node <= scif_info.maxid; node++) - seq_printf(s, "%-16d\t%-16s\n", scif_dev[node].node, - _scifdev_alive(&scif_dev[node]) ? - "Running" : "Offline"); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(scif_dev); - -static void scif_display_window(struct scif_window *window, struct seq_file *s) -{ - int j; - struct scatterlist *sg; - scif_pinned_pages_t pin = window->pinned_pages; - - seq_printf(s, "window %p type %d temp %d offset 0x%llx ", - window, window->type, window->temp, window->offset); - seq_printf(s, "nr_pages 0x%llx nr_contig_chunks 0x%x prot %d ", - window->nr_pages, window->nr_contig_chunks, window->prot); - seq_printf(s, "ref_count %d magic 0x%llx peer_window 0x%llx ", - window->ref_count, window->magic, window->peer_window); - seq_printf(s, "unreg_state 0x%x va_for_temp 0x%lx\n", - window->unreg_state, window->va_for_temp); - - for (j = 0; j < window->nr_contig_chunks; j++) - seq_printf(s, "page[%d] dma_addr 0x%llx num_pages 0x%llx\n", j, - window->dma_addr[j], window->num_pages[j]); - - if (window->type == SCIF_WINDOW_SELF && pin) - for (j = 0; j < window->nr_pages; j++) - seq_printf(s, "page[%d] = pinned_pages %p address %p\n", - j, pin->pages[j], - page_address(pin->pages[j])); - - if (window->st) - for_each_sg(window->st->sgl, sg, window->st->nents, j) - seq_printf(s, "sg[%d] dma addr 0x%llx length 0x%x\n", - j, sg_dma_address(sg), sg_dma_len(sg)); -} - -static void scif_display_all_windows(struct list_head *head, struct seq_file *s) -{ - struct list_head *item; - struct scif_window *window; - - list_for_each(item, head) { - window = list_entry(item, struct scif_window, list); - scif_display_window(window, s); - } -} - -static int scif_rma_show(struct seq_file *s, void *unused) -{ - struct scif_endpt *ep; - struct list_head *pos; - - mutex_lock(&scif_info.connlock); - list_for_each(pos, &scif_info.connected) { - ep = list_entry(pos, struct scif_endpt, list); - seq_printf(s, "ep %p self windows\n", ep); - mutex_lock(&ep->rma_info.rma_lock); - scif_display_all_windows(&ep->rma_info.reg_list, s); - seq_printf(s, "ep %p remote windows\n", ep); - scif_display_all_windows(&ep->rma_info.remote_reg_list, s); - mutex_unlock(&ep->rma_info.rma_lock); - } - mutex_unlock(&scif_info.connlock); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(scif_rma); - -void __init scif_init_debugfs(void) -{ - scif_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); - - debugfs_create_file("scif_dev", 0444, scif_dbg, NULL, &scif_dev_fops); - debugfs_create_file("scif_rma", 0444, scif_dbg, NULL, &scif_rma_fops); - debugfs_create_u8("en_msg_log", 0666, scif_dbg, &scif_info.en_msg_log); - debugfs_create_u8("p2p_enable", 0666, scif_dbg, &scif_info.p2p_enable); -} - -void scif_exit_debugfs(void) -{ - debugfs_remove_recursive(scif_dbg); -} diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c deleted file mode 100644 index 401b98e5ad79..000000000000 --- a/drivers/misc/mic/scif/scif_dma.c +++ /dev/null @@ -1,1940 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include "scif_map.h" - -/* - * struct scif_dma_comp_cb - SCIF DMA completion callback - * - * @dma_completion_func: DMA completion callback - * @cb_cookie: DMA completion callback cookie - * @temp_buf: Temporary buffer - * @temp_buf_to_free: Temporary buffer to be freed - * @is_cache: Is a kmem_cache allocated buffer - * @dst_offset: Destination registration offset - * @dst_window: Destination registration window - * @len: Length of the temp buffer - * @temp_phys: DMA address of the temp buffer - * @sdev: The SCIF device - * @header_padding: padding for cache line alignment - */ -struct scif_dma_comp_cb { - void (*dma_completion_func)(void *cookie); - void *cb_cookie; - u8 *temp_buf; - u8 *temp_buf_to_free; - bool is_cache; - s64 dst_offset; - struct scif_window *dst_window; - size_t len; - dma_addr_t temp_phys; - struct scif_dev *sdev; - int header_padding; -}; - -/** - * struct scif_copy_work - Work for DMA copy - * - * @src_offset: Starting source offset - * @dst_offset: Starting destination offset - * @src_window: Starting src registered window - * @dst_window: Starting dst registered window - * @loopback: true if this is a loopback DMA transfer - * @len: Length of the transfer - * @comp_cb: DMA copy completion callback - * @remote_dev: The remote SCIF peer device - * @fence_type: polling or interrupt based - * @ordered: is this a tail byte ordered DMA transfer - */ -struct scif_copy_work { - s64 src_offset; - s64 dst_offset; - struct scif_window *src_window; - struct scif_window *dst_window; - int loopback; - size_t len; - struct scif_dma_comp_cb *comp_cb; - struct scif_dev *remote_dev; - int fence_type; - bool ordered; -}; - -/** - * scif_reserve_dma_chan: - * @ep: Endpoint Descriptor. - * - * This routine reserves a DMA channel for a particular - * endpoint. All DMA transfers for an endpoint are always - * programmed on the same DMA channel. - */ -int scif_reserve_dma_chan(struct scif_endpt *ep) -{ - int err = 0; - struct scif_dev *scifdev; - struct scif_hw_dev *sdev; - struct dma_chan *chan; - - /* Loopback DMAs are not supported on the management node */ - if (!scif_info.nodeid && scifdev_self(ep->remote_dev)) - return 0; - if (scif_info.nodeid) - scifdev = &scif_dev[0]; - else - scifdev = ep->remote_dev; - sdev = scifdev->sdev; - if (!sdev->num_dma_ch) - return -ENODEV; - chan = sdev->dma_ch[scifdev->dma_ch_idx]; - scifdev->dma_ch_idx = (scifdev->dma_ch_idx + 1) % sdev->num_dma_ch; - mutex_lock(&ep->rma_info.rma_lock); - ep->rma_info.dma_chan = chan; - mutex_unlock(&ep->rma_info.rma_lock); - return err; -} - -#ifdef CONFIG_MMU_NOTIFIER -/* - * scif_rma_destroy_tcw: - * - * This routine destroys temporary cached windows - */ -static -void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, - u64 start, u64 len) -{ - struct list_head *item, *tmp; - struct scif_window *window; - u64 start_va, end_va; - u64 end = start + len; - - if (end <= start) - return; - - list_for_each_safe(item, tmp, &mmn->tc_reg_list) { - window = list_entry(item, struct scif_window, list); - if (!len) - break; - start_va = window->va_for_temp; - end_va = start_va + (window->nr_pages << PAGE_SHIFT); - if (start < start_va && end <= start_va) - break; - if (start >= end_va) - continue; - __scif_rma_destroy_tcw_helper(window); - } -} - -static void scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, u64 start, u64 len) -{ - struct scif_endpt *ep = mmn->ep; - - spin_lock(&ep->rma_info.tc_lock); - __scif_rma_destroy_tcw(mmn, start, len); - spin_unlock(&ep->rma_info.tc_lock); -} - -static void scif_rma_destroy_tcw_ep(struct scif_endpt *ep) -{ - struct list_head *item, *tmp; - struct scif_mmu_notif *mmn; - - list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); - scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); - } -} - -static void __scif_rma_destroy_tcw_ep(struct scif_endpt *ep) -{ - struct list_head *item, *tmp; - struct scif_mmu_notif *mmn; - - spin_lock(&ep->rma_info.tc_lock); - list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); - __scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); - } - spin_unlock(&ep->rma_info.tc_lock); -} - -static bool scif_rma_tc_can_cache(struct scif_endpt *ep, size_t cur_bytes) -{ - if ((cur_bytes >> PAGE_SHIFT) > scif_info.rma_tc_limit) - return false; - if ((atomic_read(&ep->rma_info.tcw_total_pages) - + (cur_bytes >> PAGE_SHIFT)) > - scif_info.rma_tc_limit) { - dev_info(scif_info.mdev.this_device, - "%s %d total=%d, current=%zu reached max\n", - __func__, __LINE__, - atomic_read(&ep->rma_info.tcw_total_pages), - (1 + (cur_bytes >> PAGE_SHIFT))); - scif_rma_destroy_tcw_invalid(); - __scif_rma_destroy_tcw_ep(ep); - } - return true; -} - -static void scif_mmu_notifier_release(struct mmu_notifier *mn, - struct mm_struct *mm) -{ - struct scif_mmu_notif *mmn; - - mmn = container_of(mn, struct scif_mmu_notif, ep_mmu_notifier); - scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); - schedule_work(&scif_info.misc_work); -} - -static int scif_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct scif_mmu_notif *mmn; - - mmn = container_of(mn, struct scif_mmu_notif, ep_mmu_notifier); - scif_rma_destroy_tcw(mmn, range->start, range->end - range->start); - - return 0; -} - -static void scif_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - /* - * Nothing to do here, everything needed was done in - * invalidate_range_start. - */ -} - -static const struct mmu_notifier_ops scif_mmu_notifier_ops = { - .release = scif_mmu_notifier_release, - .clear_flush_young = NULL, - .invalidate_range_start = scif_mmu_notifier_invalidate_range_start, - .invalidate_range_end = scif_mmu_notifier_invalidate_range_end}; - -static void scif_ep_unregister_mmu_notifier(struct scif_endpt *ep) -{ - struct scif_endpt_rma_info *rma = &ep->rma_info; - struct scif_mmu_notif *mmn = NULL; - struct list_head *item, *tmp; - - mutex_lock(&ep->rma_info.mmn_lock); - list_for_each_safe(item, tmp, &rma->mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); - mmu_notifier_unregister(&mmn->ep_mmu_notifier, mmn->mm); - list_del(item); - kfree(mmn); - } - mutex_unlock(&ep->rma_info.mmn_lock); -} - -static void scif_init_mmu_notifier(struct scif_mmu_notif *mmn, - struct mm_struct *mm, struct scif_endpt *ep) -{ - mmn->ep = ep; - mmn->mm = mm; - mmn->ep_mmu_notifier.ops = &scif_mmu_notifier_ops; - INIT_LIST_HEAD(&mmn->list); - INIT_LIST_HEAD(&mmn->tc_reg_list); -} - -static struct scif_mmu_notif * -scif_find_mmu_notifier(struct mm_struct *mm, struct scif_endpt_rma_info *rma) -{ - struct scif_mmu_notif *mmn; - - list_for_each_entry(mmn, &rma->mmn_list, list) - if (mmn->mm == mm) - return mmn; - return NULL; -} - -static struct scif_mmu_notif * -scif_add_mmu_notifier(struct mm_struct *mm, struct scif_endpt *ep) -{ - struct scif_mmu_notif *mmn - = kzalloc(sizeof(*mmn), GFP_KERNEL); - - if (!mmn) - return ERR_PTR(-ENOMEM); - - scif_init_mmu_notifier(mmn, current->mm, ep); - if (mmu_notifier_register(&mmn->ep_mmu_notifier, current->mm)) { - kfree(mmn); - return ERR_PTR(-EBUSY); - } - list_add(&mmn->list, &ep->rma_info.mmn_list); - return mmn; -} - -/* - * Called from the misc thread to destroy temporary cached windows and - * unregister the MMU notifier for the SCIF endpoint. - */ -void scif_mmu_notif_handler(struct work_struct *work) -{ - struct list_head *pos, *tmpq; - struct scif_endpt *ep; -restart: - scif_rma_destroy_tcw_invalid(); - spin_lock(&scif_info.rmalock); - list_for_each_safe(pos, tmpq, &scif_info.mmu_notif_cleanup) { - ep = list_entry(pos, struct scif_endpt, mmu_list); - list_del(&ep->mmu_list); - spin_unlock(&scif_info.rmalock); - scif_rma_destroy_tcw_ep(ep); - scif_ep_unregister_mmu_notifier(ep); - goto restart; - } - spin_unlock(&scif_info.rmalock); -} - -static bool scif_is_set_reg_cache(int flags) -{ - return !!(flags & SCIF_RMA_USECACHE); -} -#else -static struct scif_mmu_notif * -scif_find_mmu_notifier(struct mm_struct *mm, - struct scif_endpt_rma_info *rma) -{ - return NULL; -} - -static struct scif_mmu_notif * -scif_add_mmu_notifier(struct mm_struct *mm, struct scif_endpt *ep) -{ - return NULL; -} - -void scif_mmu_notif_handler(struct work_struct *work) -{ -} - -static bool scif_is_set_reg_cache(int flags) -{ - return false; -} - -static bool scif_rma_tc_can_cache(struct scif_endpt *ep, size_t cur_bytes) -{ - return false; -} -#endif - -/** - * scif_register_temp: - * @epd: End Point Descriptor. - * @addr: virtual address to/from which to copy - * @len: length of range to copy - * @prot: read/write protection - * @out_offset: computed offset returned by reference. - * @out_window: allocated registered window returned by reference. - * - * Create a temporary registered window. The peer will not know about this - * window. This API is used for scif_vreadfrom()/scif_vwriteto() API's. - */ -static int -scif_register_temp(scif_epd_t epd, unsigned long addr, size_t len, int prot, - off_t *out_offset, struct scif_window **out_window) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err; - scif_pinned_pages_t pinned_pages; - size_t aligned_len; - - aligned_len = ALIGN(len, PAGE_SIZE); - - err = __scif_pin_pages((void *)(addr & PAGE_MASK), - aligned_len, &prot, 0, &pinned_pages); - if (err) - return err; - - pinned_pages->prot = prot; - - /* Compute the offset for this registration */ - err = scif_get_window_offset(ep, 0, 0, - aligned_len >> PAGE_SHIFT, - (s64 *)out_offset); - if (err) - goto error_unpin; - - /* Allocate and prepare self registration window */ - *out_window = scif_create_window(ep, aligned_len >> PAGE_SHIFT, - *out_offset, true); - if (!*out_window) { - scif_free_window_offset(ep, NULL, *out_offset); - err = -ENOMEM; - goto error_unpin; - } - - (*out_window)->pinned_pages = pinned_pages; - (*out_window)->nr_pages = pinned_pages->nr_pages; - (*out_window)->prot = pinned_pages->prot; - - (*out_window)->va_for_temp = addr & PAGE_MASK; - err = scif_map_window(ep->remote_dev, *out_window); - if (err) { - /* Something went wrong! Rollback */ - scif_destroy_window(ep, *out_window); - *out_window = NULL; - } else { - *out_offset |= (addr - (*out_window)->va_for_temp); - } - return err; -error_unpin: - if (err) - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - scif_unpin_pages(pinned_pages); - return err; -} - -#define SCIF_DMA_TO (3 * HZ) - -/* - * scif_sync_dma - Program a DMA without an interrupt descriptor - * - * @dev - The address of the pointer to the device instance used - * for DMA registration. - * @chan - DMA channel to be used. - * @sync_wait: Wait for DMA to complete? - * - * Return 0 on success and -errno on error. - */ -static int scif_sync_dma(struct scif_hw_dev *sdev, struct dma_chan *chan, - bool sync_wait) -{ - int err = 0; - struct dma_async_tx_descriptor *tx = NULL; - enum dma_ctrl_flags flags = DMA_PREP_FENCE; - dma_cookie_t cookie; - struct dma_device *ddev; - - if (!chan) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - ddev = chan->device; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, flags); - if (!tx) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - cookie = tx->tx_submit(tx); - - if (dma_submit_error(cookie)) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - if (!sync_wait) { - dma_async_issue_pending(chan); - } else { - if (dma_sync_wait(chan, cookie) == DMA_COMPLETE) { - err = 0; - } else { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - } - } -release: - return err; -} - -static void scif_dma_callback(void *arg) -{ - struct completion *done = (struct completion *)arg; - - complete(done); -} - -#define SCIF_DMA_SYNC_WAIT true -#define SCIF_DMA_POLL BIT(0) -#define SCIF_DMA_INTR BIT(1) - -/* - * scif_async_dma - Program a DMA with an interrupt descriptor - * - * @dev - The address of the pointer to the device instance used - * for DMA registration. - * @chan - DMA channel to be used. - * Return 0 on success and -errno on error. - */ -static int scif_async_dma(struct scif_hw_dev *sdev, struct dma_chan *chan) -{ - int err = 0; - struct dma_device *ddev; - struct dma_async_tx_descriptor *tx = NULL; - enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; - DECLARE_COMPLETION_ONSTACK(done_wait); - dma_cookie_t cookie; - enum dma_status status; - - if (!chan) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - ddev = chan->device; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, flags); - if (!tx) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - reinit_completion(&done_wait); - tx->callback = scif_dma_callback; - tx->callback_param = &done_wait; - cookie = tx->tx_submit(tx); - - if (dma_submit_error(cookie)) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - dma_async_issue_pending(chan); - - err = wait_for_completion_timeout(&done_wait, SCIF_DMA_TO); - if (!err) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - err = 0; - status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); - if (status != DMA_COMPLETE) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } -release: - return err; -} - -/* - * scif_drain_dma_poll - Drain all outstanding DMA operations for a particular - * DMA channel via polling. - * - * @sdev - The SCIF device - * @chan - DMA channel - * Return 0 on success and -errno on error. - */ -static int scif_drain_dma_poll(struct scif_hw_dev *sdev, struct dma_chan *chan) -{ - if (!chan) - return -EINVAL; - return scif_sync_dma(sdev, chan, SCIF_DMA_SYNC_WAIT); -} - -/* - * scif_drain_dma_intr - Drain all outstanding DMA operations for a particular - * DMA channel via interrupt based blocking wait. - * - * @sdev - The SCIF device - * @chan - DMA channel - * Return 0 on success and -errno on error. - */ -int scif_drain_dma_intr(struct scif_hw_dev *sdev, struct dma_chan *chan) -{ - if (!chan) - return -EINVAL; - return scif_async_dma(sdev, chan); -} - -/** - * scif_rma_destroy_windows: - * - * This routine destroys all windows queued for cleanup - */ -void scif_rma_destroy_windows(void) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep; - struct dma_chan *chan; - - might_sleep(); -restart: - spin_lock(&scif_info.rmalock); - list_for_each_safe(item, tmp, &scif_info.rma) { - window = list_entry(item, struct scif_window, - list); - ep = (struct scif_endpt *)window->ep; - chan = ep->rma_info.dma_chan; - - list_del_init(&window->list); - spin_unlock(&scif_info.rmalock); - if (!chan || !scifdev_alive(ep) || - !scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan)) - /* Remove window from global list */ - window->unreg_state = OP_COMPLETED; - else - dev_warn(&ep->remote_dev->sdev->dev, - "DMA engine hung?\n"); - if (window->unreg_state == OP_COMPLETED) { - if (window->type == SCIF_WINDOW_SELF) - scif_destroy_window(ep, window); - else - scif_destroy_remote_window(window); - atomic_dec(&ep->rma_info.tw_refcount); - } - goto restart; - } - spin_unlock(&scif_info.rmalock); -} - -/** - * scif_rma_destroy_tcw: - * - * This routine destroys temporary cached registered windows - * which have been queued for cleanup. - */ -void scif_rma_destroy_tcw_invalid(void) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep; - struct dma_chan *chan; - - might_sleep(); -restart: - spin_lock(&scif_info.rmalock); - list_for_each_safe(item, tmp, &scif_info.rma_tc) { - window = list_entry(item, struct scif_window, list); - ep = (struct scif_endpt *)window->ep; - chan = ep->rma_info.dma_chan; - list_del_init(&window->list); - spin_unlock(&scif_info.rmalock); - mutex_lock(&ep->rma_info.rma_lock); - if (!chan || !scifdev_alive(ep) || - !scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan)) { - atomic_sub(window->nr_pages, - &ep->rma_info.tcw_total_pages); - scif_destroy_window(ep, window); - atomic_dec(&ep->rma_info.tcw_refcount); - } else { - dev_warn(&ep->remote_dev->sdev->dev, - "DMA engine hung?\n"); - } - mutex_unlock(&ep->rma_info.rma_lock); - goto restart; - } - spin_unlock(&scif_info.rmalock); -} - -static inline -void *_get_local_va(off_t off, struct scif_window *window, size_t len) -{ - int page_nr = (off - window->offset) >> PAGE_SHIFT; - off_t page_off = off & ~PAGE_MASK; - void *va = NULL; - - if (window->type == SCIF_WINDOW_SELF) { - struct page **pages = window->pinned_pages->pages; - - va = page_address(pages[page_nr]) + page_off; - } - return va; -} - -static inline -void *ioremap_remote(off_t off, struct scif_window *window, - size_t len, struct scif_dev *dev, - struct scif_window_iter *iter) -{ - dma_addr_t phys = scif_off_to_dma_addr(window, off, NULL, iter); - - /* - * If the DMA address is not card relative then we need the DMA - * addresses to be an offset into the bar. The aperture base was already - * added so subtract it here since scif_ioremap is going to add it again - */ - if (!scifdev_self(dev) && window->type == SCIF_WINDOW_PEER && - dev->sdev->aper && !dev->sdev->card_rel_da) - phys = phys - dev->sdev->aper->pa; - return scif_ioremap(phys, len, dev); -} - -static inline void -iounmap_remote(void *virt, size_t size, struct scif_copy_work *work) -{ - scif_iounmap(virt, size, work->remote_dev); -} - -/* - * Takes care of ordering issue caused by - * 1. Hardware: Only in the case of cpu copy from mgmt node to card - * because of WC memory. - * 2. Software: If memcpy reorders copy instructions for optimization. - * This could happen at both mgmt node and card. - */ -static inline void -scif_ordered_memcpy_toio(char *dst, const char *src, size_t count) -{ - if (!count) - return; - - memcpy_toio((void __iomem __force *)dst, src, --count); - /* Order the last byte with the previous stores */ - wmb(); - *(dst + count) = *(src + count); -} - -static inline void scif_unaligned_cpy_toio(char *dst, const char *src, - size_t count, bool ordered) -{ - if (ordered) - scif_ordered_memcpy_toio(dst, src, count); - else - memcpy_toio((void __iomem __force *)dst, src, count); -} - -static inline -void scif_ordered_memcpy_fromio(char *dst, const char *src, size_t count) -{ - if (!count) - return; - - memcpy_fromio(dst, (void __iomem __force *)src, --count); - /* Order the last byte with the previous loads */ - rmb(); - *(dst + count) = *(src + count); -} - -static inline void scif_unaligned_cpy_fromio(char *dst, const char *src, - size_t count, bool ordered) -{ - if (ordered) - scif_ordered_memcpy_fromio(dst, src, count); - else - memcpy_fromio(dst, (void __iomem __force *)src, count); -} - -#define SCIF_RMA_ERROR_CODE (~(dma_addr_t)0x0) - -/* - * scif_off_to_dma_addr: - * Obtain the dma_addr given the window and the offset. - * @window: Registered window. - * @off: Window offset. - * @nr_bytes: Return the number of contiguous bytes till next DMA addr index. - * @index: Return the index of the dma_addr array found. - * @start_off: start offset of index of the dma addr array found. - * The nr_bytes provides the callee an estimate of the maximum possible - * DMA xfer possible while the index/start_off provide faster lookups - * for the next iteration. - */ -dma_addr_t scif_off_to_dma_addr(struct scif_window *window, s64 off, - size_t *nr_bytes, struct scif_window_iter *iter) -{ - int i, page_nr; - s64 start, end; - off_t page_off; - - if (window->nr_pages == window->nr_contig_chunks) { - page_nr = (off - window->offset) >> PAGE_SHIFT; - page_off = off & ~PAGE_MASK; - - if (nr_bytes) - *nr_bytes = PAGE_SIZE - page_off; - return window->dma_addr[page_nr] | page_off; - } - if (iter) { - i = iter->index; - start = iter->offset; - } else { - i = 0; - start = window->offset; - } - for (; i < window->nr_contig_chunks; i++) { - end = start + (window->num_pages[i] << PAGE_SHIFT); - if (off >= start && off < end) { - if (iter) { - iter->index = i; - iter->offset = start; - } - if (nr_bytes) - *nr_bytes = end - off; - return (window->dma_addr[i] + (off - start)); - } - start += (window->num_pages[i] << PAGE_SHIFT); - } - dev_err(scif_info.mdev.this_device, - "%s %d BUG. Addr not found? window %p off 0x%llx\n", - __func__, __LINE__, window, off); - return SCIF_RMA_ERROR_CODE; -} - -/* - * Copy between rma window and temporary buffer - */ -static void scif_rma_local_cpu_copy(s64 offset, struct scif_window *window, - u8 *temp, size_t rem_len, bool to_temp) -{ - void *window_virt; - size_t loop_len; - int offset_in_page; - s64 end_offset; - - offset_in_page = offset & ~PAGE_MASK; - loop_len = PAGE_SIZE - offset_in_page; - - if (rem_len < loop_len) - loop_len = rem_len; - - window_virt = _get_local_va(offset, window, loop_len); - if (!window_virt) - return; - if (to_temp) - memcpy(temp, window_virt, loop_len); - else - memcpy(window_virt, temp, loop_len); - - offset += loop_len; - temp += loop_len; - rem_len -= loop_len; - - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - while (rem_len) { - if (offset == end_offset) { - window = list_next_entry(window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - } - loop_len = min(PAGE_SIZE, rem_len); - window_virt = _get_local_va(offset, window, loop_len); - if (!window_virt) - return; - if (to_temp) - memcpy(temp, window_virt, loop_len); - else - memcpy(window_virt, temp, loop_len); - offset += loop_len; - temp += loop_len; - rem_len -= loop_len; - } -} - -/** - * scif_rma_completion_cb: - * @data: RMA cookie - * - * RMA interrupt completion callback. - */ -static void scif_rma_completion_cb(void *data) -{ - struct scif_dma_comp_cb *comp_cb = data; - - /* Free DMA Completion CB. */ - if (comp_cb->dst_window) - scif_rma_local_cpu_copy(comp_cb->dst_offset, - comp_cb->dst_window, - comp_cb->temp_buf + - comp_cb->header_padding, - comp_cb->len, false); - scif_unmap_single(comp_cb->temp_phys, comp_cb->sdev, - SCIF_KMEM_UNALIGNED_BUF_SIZE); - if (comp_cb->is_cache) - kmem_cache_free(unaligned_cache, - comp_cb->temp_buf_to_free); - else - kfree(comp_cb->temp_buf_to_free); -} - -/* Copies between temporary buffer and offsets provided in work */ -static int -scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, - u8 *temp, struct dma_chan *chan, - bool src_local) -{ - struct scif_dma_comp_cb *comp_cb = work->comp_cb; - dma_addr_t window_dma_addr, temp_dma_addr; - dma_addr_t temp_phys = comp_cb->temp_phys; - size_t loop_len, nr_contig_bytes = 0, remaining_len = work->len; - int offset_in_ca, ret = 0; - s64 end_offset, offset; - struct scif_window *window; - void *window_virt_addr; - size_t tail_len; - struct dma_async_tx_descriptor *tx; - struct dma_device *dev = chan->device; - dma_cookie_t cookie; - - if (src_local) { - offset = work->dst_offset; - window = work->dst_window; - } else { - offset = work->src_offset; - window = work->src_window; - } - - offset_in_ca = offset & (L1_CACHE_BYTES - 1); - if (offset_in_ca) { - loop_len = L1_CACHE_BYTES - offset_in_ca; - loop_len = min(loop_len, remaining_len); - window_virt_addr = ioremap_remote(offset, window, - loop_len, - work->remote_dev, - NULL); - if (!window_virt_addr) - return -ENOMEM; - if (src_local) - scif_unaligned_cpy_toio(window_virt_addr, temp, - loop_len, - work->ordered && - !(remaining_len - loop_len)); - else - scif_unaligned_cpy_fromio(temp, window_virt_addr, - loop_len, work->ordered && - !(remaining_len - loop_len)); - iounmap_remote(window_virt_addr, loop_len, work); - - offset += loop_len; - temp += loop_len; - temp_phys += loop_len; - remaining_len -= loop_len; - } - - offset_in_ca = offset & ~PAGE_MASK; - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - - tail_len = remaining_len & (L1_CACHE_BYTES - 1); - remaining_len -= tail_len; - while (remaining_len) { - if (offset == end_offset) { - window = list_next_entry(window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - } - if (scif_is_mgmt_node()) - temp_dma_addr = temp_phys; - else - /* Fix if we ever enable IOMMU on the card */ - temp_dma_addr = (dma_addr_t)virt_to_phys(temp); - window_dma_addr = scif_off_to_dma_addr(window, offset, - &nr_contig_bytes, - NULL); - loop_len = min(nr_contig_bytes, remaining_len); - if (src_local) { - if (work->ordered && !tail_len && - !(remaining_len - loop_len) && - loop_len != L1_CACHE_BYTES) { - /* - * Break up the last chunk of the transfer into - * two steps. if there is no tail to guarantee - * DMA ordering. SCIF_DMA_POLLING inserts - * a status update descriptor in step 1 which - * acts as a double sided synchronization fence - * for the DMA engine to ensure that the last - * cache line in step 2 is updated last. - */ - /* Step 1) DMA: Body Length - L1_CACHE_BYTES. */ - tx = - dev->device_prep_dma_memcpy(chan, - window_dma_addr, - temp_dma_addr, - loop_len - - L1_CACHE_BYTES, - DMA_PREP_FENCE); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - offset += (loop_len - L1_CACHE_BYTES); - temp_dma_addr += (loop_len - L1_CACHE_BYTES); - window_dma_addr += (loop_len - L1_CACHE_BYTES); - remaining_len -= (loop_len - L1_CACHE_BYTES); - loop_len = remaining_len; - - /* Step 2) DMA: L1_CACHE_BYTES */ - tx = - dev->device_prep_dma_memcpy(chan, - window_dma_addr, - temp_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } else { - tx = - dev->device_prep_dma_memcpy(chan, - window_dma_addr, - temp_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } - } else { - tx = dev->device_prep_dma_memcpy(chan, temp_dma_addr, - window_dma_addr, loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } - offset += loop_len; - temp += loop_len; - temp_phys += loop_len; - remaining_len -= loop_len; - offset_in_ca = 0; - } - if (tail_len) { - if (offset == end_offset) { - window = list_next_entry(window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - } - window_virt_addr = ioremap_remote(offset, window, tail_len, - work->remote_dev, - NULL); - if (!window_virt_addr) - return -ENOMEM; - /* - * The CPU copy for the tail bytes must be initiated only once - * previous DMA transfers for this endpoint have completed - * to guarantee ordering. - */ - if (work->ordered) { - struct scif_dev *rdev = work->remote_dev; - - ret = scif_drain_dma_intr(rdev->sdev, chan); - if (ret) - return ret; - } - if (src_local) - scif_unaligned_cpy_toio(window_virt_addr, temp, - tail_len, work->ordered); - else - scif_unaligned_cpy_fromio(temp, window_virt_addr, - tail_len, work->ordered); - iounmap_remote(window_virt_addr, tail_len, work); - } - tx = dev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_INTERRUPT); - if (!tx) { - ret = -ENOMEM; - return ret; - } - tx->callback = &scif_rma_completion_cb; - tx->callback_param = comp_cb; - cookie = tx->tx_submit(tx); - - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - return ret; - } - dma_async_issue_pending(chan); - return 0; -err: - dev_err(scif_info.mdev.this_device, - "%s %d Desc Prog Failed ret %d\n", - __func__, __LINE__, ret); - return ret; -} - -/* - * _scif_rma_list_dma_copy_aligned: - * - * Traverse all the windows and perform DMA copy. - */ -static int _scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, - struct dma_chan *chan) -{ - dma_addr_t src_dma_addr, dst_dma_addr; - size_t loop_len, remaining_len, src_contig_bytes = 0; - size_t dst_contig_bytes = 0; - struct scif_window_iter src_win_iter; - struct scif_window_iter dst_win_iter; - s64 end_src_offset, end_dst_offset; - struct scif_window *src_window = work->src_window; - struct scif_window *dst_window = work->dst_window; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - int ret = 0; - struct dma_async_tx_descriptor *tx; - struct dma_device *dev = chan->device; - dma_cookie_t cookie; - - remaining_len = work->len; - - scif_init_window_iter(src_window, &src_win_iter); - scif_init_window_iter(dst_window, &dst_win_iter); - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - while (remaining_len) { - if (src_offset == end_src_offset) { - src_window = list_next_entry(src_window, list); - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(src_window, &src_win_iter); - } - if (dst_offset == end_dst_offset) { - dst_window = list_next_entry(dst_window, list); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(dst_window, &dst_win_iter); - } - - /* compute dma addresses for transfer */ - src_dma_addr = scif_off_to_dma_addr(src_window, src_offset, - &src_contig_bytes, - &src_win_iter); - dst_dma_addr = scif_off_to_dma_addr(dst_window, dst_offset, - &dst_contig_bytes, - &dst_win_iter); - loop_len = min(src_contig_bytes, dst_contig_bytes); - loop_len = min(loop_len, remaining_len); - if (work->ordered && !(remaining_len - loop_len)) { - /* - * Break up the last chunk of the transfer into two - * steps to ensure that the last byte in step 2 is - * updated last. - */ - /* Step 1) DMA: Body Length - 1 */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len - 1, - DMA_PREP_FENCE); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - src_offset += (loop_len - 1); - dst_offset += (loop_len - 1); - src_dma_addr += (loop_len - 1); - dst_dma_addr += (loop_len - 1); - remaining_len -= (loop_len - 1); - loop_len = remaining_len; - - /* Step 2) DMA: 1 BYTES */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } else { - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - } - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - } - return ret; -err: - dev_err(scif_info.mdev.this_device, - "%s %d Desc Prog Failed ret %d\n", - __func__, __LINE__, ret); - return ret; -} - -/* - * scif_rma_list_dma_copy_aligned: - * - * Traverse all the windows and perform DMA copy. - */ -static int scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, - struct dma_chan *chan) -{ - dma_addr_t src_dma_addr, dst_dma_addr; - size_t loop_len, remaining_len, tail_len, src_contig_bytes = 0; - size_t dst_contig_bytes = 0; - int src_cache_off; - s64 end_src_offset, end_dst_offset; - struct scif_window_iter src_win_iter; - struct scif_window_iter dst_win_iter; - void *src_virt, *dst_virt; - struct scif_window *src_window = work->src_window; - struct scif_window *dst_window = work->dst_window; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - int ret = 0; - struct dma_async_tx_descriptor *tx; - struct dma_device *dev = chan->device; - dma_cookie_t cookie; - - remaining_len = work->len; - scif_init_window_iter(src_window, &src_win_iter); - scif_init_window_iter(dst_window, &dst_win_iter); - - src_cache_off = src_offset & (L1_CACHE_BYTES - 1); - if (src_cache_off != 0) { - /* Head */ - loop_len = L1_CACHE_BYTES - src_cache_off; - loop_len = min(loop_len, remaining_len); - src_dma_addr = __scif_off_to_dma_addr(src_window, src_offset); - dst_dma_addr = __scif_off_to_dma_addr(dst_window, dst_offset); - if (src_window->type == SCIF_WINDOW_SELF) - src_virt = _get_local_va(src_offset, src_window, - loop_len); - else - src_virt = ioremap_remote(src_offset, src_window, - loop_len, - work->remote_dev, NULL); - if (!src_virt) - return -ENOMEM; - if (dst_window->type == SCIF_WINDOW_SELF) - dst_virt = _get_local_va(dst_offset, dst_window, - loop_len); - else - dst_virt = ioremap_remote(dst_offset, dst_window, - loop_len, - work->remote_dev, NULL); - if (!dst_virt) { - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - return -ENOMEM; - } - if (src_window->type == SCIF_WINDOW_SELF) - scif_unaligned_cpy_toio(dst_virt, src_virt, loop_len, - remaining_len == loop_len ? - work->ordered : false); - else - scif_unaligned_cpy_fromio(dst_virt, src_virt, loop_len, - remaining_len == loop_len ? - work->ordered : false); - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - if (dst_window->type != SCIF_WINDOW_SELF) - iounmap_remote(dst_virt, loop_len, work); - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - } - - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - tail_len = remaining_len & (L1_CACHE_BYTES - 1); - remaining_len -= tail_len; - while (remaining_len) { - if (src_offset == end_src_offset) { - src_window = list_next_entry(src_window, list); - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(src_window, &src_win_iter); - } - if (dst_offset == end_dst_offset) { - dst_window = list_next_entry(dst_window, list); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(dst_window, &dst_win_iter); - } - - /* compute dma addresses for transfer */ - src_dma_addr = scif_off_to_dma_addr(src_window, src_offset, - &src_contig_bytes, - &src_win_iter); - dst_dma_addr = scif_off_to_dma_addr(dst_window, dst_offset, - &dst_contig_bytes, - &dst_win_iter); - loop_len = min(src_contig_bytes, dst_contig_bytes); - loop_len = min(loop_len, remaining_len); - if (work->ordered && !tail_len && - !(remaining_len - loop_len)) { - /* - * Break up the last chunk of the transfer into two - * steps. if there is no tail to gurantee DMA ordering. - * Passing SCIF_DMA_POLLING inserts a status update - * descriptor in step 1 which acts as a double sided - * synchronization fence for the DMA engine to ensure - * that the last cache line in step 2 is updated last. - */ - /* Step 1) DMA: Body Length - L1_CACHE_BYTES. */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len - - L1_CACHE_BYTES, - DMA_PREP_FENCE); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - src_offset += (loop_len - L1_CACHE_BYTES); - dst_offset += (loop_len - L1_CACHE_BYTES); - src_dma_addr += (loop_len - L1_CACHE_BYTES); - dst_dma_addr += (loop_len - L1_CACHE_BYTES); - remaining_len -= (loop_len - L1_CACHE_BYTES); - loop_len = remaining_len; - - /* Step 2) DMA: L1_CACHE_BYTES */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } else { - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - } - remaining_len = tail_len; - if (remaining_len) { - loop_len = remaining_len; - if (src_offset == end_src_offset) - src_window = list_next_entry(src_window, list); - if (dst_offset == end_dst_offset) - dst_window = list_next_entry(dst_window, list); - - src_dma_addr = __scif_off_to_dma_addr(src_window, src_offset); - dst_dma_addr = __scif_off_to_dma_addr(dst_window, dst_offset); - /* - * The CPU copy for the tail bytes must be initiated only once - * previous DMA transfers for this endpoint have completed to - * guarantee ordering. - */ - if (work->ordered) { - struct scif_dev *rdev = work->remote_dev; - - ret = scif_drain_dma_poll(rdev->sdev, chan); - if (ret) - return ret; - } - if (src_window->type == SCIF_WINDOW_SELF) - src_virt = _get_local_va(src_offset, src_window, - loop_len); - else - src_virt = ioremap_remote(src_offset, src_window, - loop_len, - work->remote_dev, NULL); - if (!src_virt) - return -ENOMEM; - - if (dst_window->type == SCIF_WINDOW_SELF) - dst_virt = _get_local_va(dst_offset, dst_window, - loop_len); - else - dst_virt = ioremap_remote(dst_offset, dst_window, - loop_len, - work->remote_dev, NULL); - if (!dst_virt) { - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - return -ENOMEM; - } - - if (src_window->type == SCIF_WINDOW_SELF) - scif_unaligned_cpy_toio(dst_virt, src_virt, loop_len, - work->ordered); - else - scif_unaligned_cpy_fromio(dst_virt, src_virt, - loop_len, work->ordered); - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - - if (dst_window->type != SCIF_WINDOW_SELF) - iounmap_remote(dst_virt, loop_len, work); - remaining_len -= loop_len; - } - return ret; -err: - dev_err(scif_info.mdev.this_device, - "%s %d Desc Prog Failed ret %d\n", - __func__, __LINE__, ret); - return ret; -} - -/* - * scif_rma_list_cpu_copy: - * - * Traverse all the windows and perform CPU copy. - */ -static int scif_rma_list_cpu_copy(struct scif_copy_work *work) -{ - void *src_virt, *dst_virt; - size_t loop_len, remaining_len; - int src_page_off, dst_page_off; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - struct scif_window *src_window = work->src_window; - struct scif_window *dst_window = work->dst_window; - s64 end_src_offset, end_dst_offset; - int ret = 0; - struct scif_window_iter src_win_iter; - struct scif_window_iter dst_win_iter; - - remaining_len = work->len; - - scif_init_window_iter(src_window, &src_win_iter); - scif_init_window_iter(dst_window, &dst_win_iter); - while (remaining_len) { - src_page_off = src_offset & ~PAGE_MASK; - dst_page_off = dst_offset & ~PAGE_MASK; - loop_len = min(PAGE_SIZE - - max(src_page_off, dst_page_off), - remaining_len); - - if (src_window->type == SCIF_WINDOW_SELF) - src_virt = _get_local_va(src_offset, src_window, - loop_len); - else - src_virt = ioremap_remote(src_offset, src_window, - loop_len, - work->remote_dev, - &src_win_iter); - if (!src_virt) { - ret = -ENOMEM; - goto error; - } - - if (dst_window->type == SCIF_WINDOW_SELF) - dst_virt = _get_local_va(dst_offset, dst_window, - loop_len); - else - dst_virt = ioremap_remote(dst_offset, dst_window, - loop_len, - work->remote_dev, - &dst_win_iter); - if (!dst_virt) { - if (src_window->type == SCIF_WINDOW_PEER) - iounmap_remote(src_virt, loop_len, work); - ret = -ENOMEM; - goto error; - } - - if (work->loopback) { - memcpy(dst_virt, src_virt, loop_len); - } else { - if (src_window->type == SCIF_WINDOW_SELF) - memcpy_toio((void __iomem __force *)dst_virt, - src_virt, loop_len); - else - memcpy_fromio(dst_virt, - (void __iomem __force *)src_virt, - loop_len); - } - if (src_window->type == SCIF_WINDOW_PEER) - iounmap_remote(src_virt, loop_len, work); - - if (dst_window->type == SCIF_WINDOW_PEER) - iounmap_remote(dst_virt, loop_len, work); - - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - if (remaining_len) { - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - if (src_offset == end_src_offset) { - src_window = list_next_entry(src_window, list); - scif_init_window_iter(src_window, - &src_win_iter); - } - if (dst_offset == end_dst_offset) { - dst_window = list_next_entry(dst_window, list); - scif_init_window_iter(dst_window, - &dst_win_iter); - } - } - } -error: - return ret; -} - -static int scif_rma_list_dma_copy_wrapper(struct scif_endpt *epd, - struct scif_copy_work *work, - struct dma_chan *chan, off_t loffset) -{ - int src_cache_off, dst_cache_off; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - u8 *temp = NULL; - bool src_local = true; - struct scif_dma_comp_cb *comp_cb; - int err; - - if (is_dma_copy_aligned(chan->device, 1, 1, 1)) - return _scif_rma_list_dma_copy_aligned(work, chan); - - src_cache_off = src_offset & (L1_CACHE_BYTES - 1); - dst_cache_off = dst_offset & (L1_CACHE_BYTES - 1); - - if (dst_cache_off == src_cache_off) - return scif_rma_list_dma_copy_aligned(work, chan); - - if (work->loopback) - return scif_rma_list_cpu_copy(work); - src_local = work->src_window->type == SCIF_WINDOW_SELF; - - /* Allocate dma_completion cb */ - comp_cb = kzalloc(sizeof(*comp_cb), GFP_KERNEL); - if (!comp_cb) - goto error; - - work->comp_cb = comp_cb; - comp_cb->cb_cookie = comp_cb; - comp_cb->dma_completion_func = &scif_rma_completion_cb; - - if (work->len + (L1_CACHE_BYTES << 1) < SCIF_KMEM_UNALIGNED_BUF_SIZE) { - comp_cb->is_cache = false; - /* Allocate padding bytes to align to a cache line */ - temp = kmalloc(work->len + (L1_CACHE_BYTES << 1), - GFP_KERNEL); - if (!temp) - goto free_comp_cb; - comp_cb->temp_buf_to_free = temp; - /* kmalloc(..) does not guarantee cache line alignment */ - if (!IS_ALIGNED((u64)temp, L1_CACHE_BYTES)) - temp = PTR_ALIGN(temp, L1_CACHE_BYTES); - } else { - comp_cb->is_cache = true; - temp = kmem_cache_alloc(unaligned_cache, GFP_KERNEL); - if (!temp) - goto free_comp_cb; - comp_cb->temp_buf_to_free = temp; - } - - if (src_local) { - temp += dst_cache_off; - scif_rma_local_cpu_copy(work->src_offset, work->src_window, - temp, work->len, true); - } else { - comp_cb->dst_window = work->dst_window; - comp_cb->dst_offset = work->dst_offset; - work->src_offset = work->src_offset - src_cache_off; - comp_cb->len = work->len; - work->len = ALIGN(work->len + src_cache_off, L1_CACHE_BYTES); - comp_cb->header_padding = src_cache_off; - } - comp_cb->temp_buf = temp; - - err = scif_map_single(&comp_cb->temp_phys, temp, - work->remote_dev, SCIF_KMEM_UNALIGNED_BUF_SIZE); - if (err) - goto free_temp_buf; - comp_cb->sdev = work->remote_dev; - if (scif_rma_list_dma_copy_unaligned(work, temp, chan, src_local) < 0) - goto free_temp_buf; - if (!src_local) - work->fence_type = SCIF_DMA_INTR; - return 0; -free_temp_buf: - if (comp_cb->is_cache) - kmem_cache_free(unaligned_cache, comp_cb->temp_buf_to_free); - else - kfree(comp_cb->temp_buf_to_free); -free_comp_cb: - kfree(comp_cb); -error: - return -ENOMEM; -} - -/** - * scif_rma_copy: - * @epd: end point descriptor. - * @loffset: offset in local registered address space to/from which to copy - * @addr: user virtual address to/from which to copy - * @len: length of range to copy - * @roffset: offset in remote registered address space to/from which to copy - * @flags: flags - * @dir: LOCAL->REMOTE or vice versa. - * @last_chunk: true if this is the last chunk of a larger transfer - * - * Validate parameters, check if src/dst registered ranges requested for copy - * are valid and initiate either CPU or DMA copy. - */ -static int scif_rma_copy(scif_epd_t epd, off_t loffset, unsigned long addr, - size_t len, off_t roffset, int flags, - enum scif_rma_dir dir, bool last_chunk) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_rma_req remote_req; - struct scif_rma_req req; - struct scif_window *local_window = NULL; - struct scif_window *remote_window = NULL; - struct scif_copy_work copy_work; - bool loopback; - int err = 0; - struct dma_chan *chan; - struct scif_mmu_notif *mmn = NULL; - bool cache = false; - struct device *spdev; - - err = scif_verify_epd(ep); - if (err) - return err; - - if (flags && !(flags & (SCIF_RMA_USECPU | SCIF_RMA_USECACHE | - SCIF_RMA_SYNC | SCIF_RMA_ORDERED))) - return -EINVAL; - - loopback = scifdev_self(ep->remote_dev) ? true : false; - copy_work.fence_type = ((flags & SCIF_RMA_SYNC) && last_chunk) ? - SCIF_DMA_POLL : 0; - copy_work.ordered = !!((flags & SCIF_RMA_ORDERED) && last_chunk); - - /* Use CPU for Mgmt node <-> Mgmt node copies */ - if (loopback && scif_is_mgmt_node()) { - flags |= SCIF_RMA_USECPU; - copy_work.fence_type = 0x0; - } - - cache = scif_is_set_reg_cache(flags); - - remote_req.out_window = &remote_window; - remote_req.offset = roffset; - remote_req.nr_bytes = len; - /* - * If transfer is from local to remote then the remote window - * must be writeable and vice versa. - */ - remote_req.prot = dir == SCIF_LOCAL_TO_REMOTE ? VM_WRITE : VM_READ; - remote_req.type = SCIF_WINDOW_PARTIAL; - remote_req.head = &ep->rma_info.remote_reg_list; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - - if (addr && cache) { - mutex_lock(&ep->rma_info.mmn_lock); - mmn = scif_find_mmu_notifier(current->mm, &ep->rma_info); - if (!mmn) - mmn = scif_add_mmu_notifier(current->mm, ep); - mutex_unlock(&ep->rma_info.mmn_lock); - if (IS_ERR(mmn)) { - scif_put_peer_dev(spdev); - return PTR_ERR(mmn); - } - cache = cache && !scif_rma_tc_can_cache(ep, len); - } - mutex_lock(&ep->rma_info.rma_lock); - if (addr) { - req.out_window = &local_window; - req.nr_bytes = ALIGN(len + (addr & ~PAGE_MASK), - PAGE_SIZE); - req.va_for_temp = addr & PAGE_MASK; - req.prot = (dir == SCIF_LOCAL_TO_REMOTE ? - VM_READ : VM_WRITE | VM_READ); - /* Does a valid local window exist? */ - if (mmn) { - spin_lock(&ep->rma_info.tc_lock); - req.head = &mmn->tc_reg_list; - err = scif_query_tcw(ep, &req); - spin_unlock(&ep->rma_info.tc_lock); - } - if (!mmn || err) { - err = scif_register_temp(epd, req.va_for_temp, - req.nr_bytes, req.prot, - &loffset, &local_window); - if (err) { - mutex_unlock(&ep->rma_info.rma_lock); - goto error; - } - if (!cache) - goto skip_cache; - atomic_inc(&ep->rma_info.tcw_refcount); - atomic_add_return(local_window->nr_pages, - &ep->rma_info.tcw_total_pages); - if (mmn) { - spin_lock(&ep->rma_info.tc_lock); - scif_insert_tcw(local_window, - &mmn->tc_reg_list); - spin_unlock(&ep->rma_info.tc_lock); - } - } -skip_cache: - loffset = local_window->offset + - (addr - local_window->va_for_temp); - } else { - req.out_window = &local_window; - req.offset = loffset; - /* - * If transfer is from local to remote then the self window - * must be readable and vice versa. - */ - req.prot = dir == SCIF_LOCAL_TO_REMOTE ? VM_READ : VM_WRITE; - req.nr_bytes = len; - req.type = SCIF_WINDOW_PARTIAL; - req.head = &ep->rma_info.reg_list; - /* Does a valid local window exist? */ - err = scif_query_window(&req); - if (err) { - mutex_unlock(&ep->rma_info.rma_lock); - goto error; - } - } - - /* Does a valid remote window exist? */ - err = scif_query_window(&remote_req); - if (err) { - mutex_unlock(&ep->rma_info.rma_lock); - goto error; - } - - /* - * Prepare copy_work for submitting work to the DMA kernel thread - * or CPU copy routine. - */ - copy_work.len = len; - copy_work.loopback = loopback; - copy_work.remote_dev = ep->remote_dev; - if (dir == SCIF_LOCAL_TO_REMOTE) { - copy_work.src_offset = loffset; - copy_work.src_window = local_window; - copy_work.dst_offset = roffset; - copy_work.dst_window = remote_window; - } else { - copy_work.src_offset = roffset; - copy_work.src_window = remote_window; - copy_work.dst_offset = loffset; - copy_work.dst_window = local_window; - } - - if (flags & SCIF_RMA_USECPU) { - scif_rma_list_cpu_copy(©_work); - } else { - chan = ep->rma_info.dma_chan; - err = scif_rma_list_dma_copy_wrapper(epd, ©_work, - chan, loffset); - } - if (addr && !cache) - atomic_inc(&ep->rma_info.tw_refcount); - - mutex_unlock(&ep->rma_info.rma_lock); - - if (last_chunk) { - struct scif_dev *rdev = ep->remote_dev; - - if (copy_work.fence_type == SCIF_DMA_POLL) - err = scif_drain_dma_poll(rdev->sdev, - ep->rma_info.dma_chan); - else if (copy_work.fence_type == SCIF_DMA_INTR) - err = scif_drain_dma_intr(rdev->sdev, - ep->rma_info.dma_chan); - } - - if (addr && !cache) - scif_queue_for_cleanup(local_window, &scif_info.rma); - scif_put_peer_dev(spdev); - return err; -error: - if (err) { - if (addr && local_window && !cache) - scif_destroy_window(ep, local_window); - dev_err(scif_info.mdev.this_device, - "%s %d err %d len 0x%lx\n", - __func__, __LINE__, err, len); - } - scif_put_peer_dev(spdev); - return err; -} - -int scif_readfrom(scif_epd_t epd, off_t loffset, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI readfrom: ep %p loffset 0x%lx len 0x%lx offset 0x%lx flags 0x%x\n", - epd, loffset, len, roffset, flags); - if (scif_unaligned(loffset, roffset)) { - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, loffset, 0x0, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_REMOTE_TO_LOCAL, false); - if (err) - goto readfrom_err; - loffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, loffset, 0x0, len, - roffset, flags, SCIF_REMOTE_TO_LOCAL, true); -readfrom_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_readfrom); - -int scif_writeto(scif_epd_t epd, off_t loffset, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI writeto: ep %p loffset 0x%lx len 0x%lx roffset 0x%lx flags 0x%x\n", - epd, loffset, len, roffset, flags); - if (scif_unaligned(loffset, roffset)) { - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, loffset, 0x0, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_LOCAL_TO_REMOTE, false); - if (err) - goto writeto_err; - loffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, loffset, 0x0, len, - roffset, flags, SCIF_LOCAL_TO_REMOTE, true); -writeto_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_writeto); - -int scif_vreadfrom(scif_epd_t epd, void *addr, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI vreadfrom: ep %p addr %p len 0x%lx roffset 0x%lx flags 0x%x\n", - epd, addr, len, roffset, flags); - if (scif_unaligned((off_t __force)addr, roffset)) { - if (len > SCIF_MAX_UNALIGNED_BUF_SIZE) - flags &= ~SCIF_RMA_USECACHE; - - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, 0, (u64)addr, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_REMOTE_TO_LOCAL, false); - if (err) - goto vreadfrom_err; - addr += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, 0, (u64)addr, len, - roffset, flags, SCIF_REMOTE_TO_LOCAL, true); -vreadfrom_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_vreadfrom); - -int scif_vwriteto(scif_epd_t epd, void *addr, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI vwriteto: ep %p addr %p len 0x%lx roffset 0x%lx flags 0x%x\n", - epd, addr, len, roffset, flags); - if (scif_unaligned((off_t __force)addr, roffset)) { - if (len > SCIF_MAX_UNALIGNED_BUF_SIZE) - flags &= ~SCIF_RMA_USECACHE; - - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, 0, (u64)addr, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_LOCAL_TO_REMOTE, false); - if (err) - goto vwriteto_err; - addr += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, 0, (u64)addr, len, - roffset, flags, SCIF_LOCAL_TO_REMOTE, true); -vwriteto_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_vwriteto); diff --git a/drivers/misc/mic/scif/scif_epd.c b/drivers/misc/mic/scif/scif_epd.c deleted file mode 100644 index 426687f6696b..000000000000 --- a/drivers/misc/mic/scif/scif_epd.c +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include "scif_map.h" - -void scif_cleanup_ep_qp(struct scif_endpt *ep) -{ - struct scif_qp *qp = ep->qp_info.qp; - - if (qp->outbound_q.rb_base) { - scif_iounmap((void *)qp->outbound_q.rb_base, - qp->outbound_q.size, ep->remote_dev); - qp->outbound_q.rb_base = NULL; - } - if (qp->remote_qp) { - scif_iounmap((void *)qp->remote_qp, - sizeof(struct scif_qp), ep->remote_dev); - qp->remote_qp = NULL; - } - if (qp->local_qp) { - scif_unmap_single(qp->local_qp, ep->remote_dev, - sizeof(struct scif_qp)); - qp->local_qp = 0x0; - } - if (qp->local_buf) { - scif_unmap_single(qp->local_buf, ep->remote_dev, - SCIF_ENDPT_QP_SIZE); - qp->local_buf = 0; - } -} - -void scif_teardown_ep(void *endpt) -{ - struct scif_endpt *ep = endpt; - struct scif_qp *qp = ep->qp_info.qp; - - if (qp) { - spin_lock(&ep->lock); - scif_cleanup_ep_qp(ep); - spin_unlock(&ep->lock); - kfree(qp->inbound_q.rb_base); - kfree(qp); - } -} - -/* - * Enqueue the endpoint to the zombie list for cleanup. - * The endpoint should not be accessed once this API returns. - */ -void scif_add_epd_to_zombie_list(struct scif_endpt *ep, bool eplock_held) -{ - if (!eplock_held) - mutex_lock(&scif_info.eplock); - spin_lock(&ep->lock); - ep->state = SCIFEP_ZOMBIE; - spin_unlock(&ep->lock); - list_add_tail(&ep->list, &scif_info.zombie); - scif_info.nr_zombies++; - if (!eplock_held) - mutex_unlock(&scif_info.eplock); - schedule_work(&scif_info.misc_work); -} - -static struct scif_endpt *scif_find_listen_ep(u16 port) -{ - struct scif_endpt *ep = NULL; - struct list_head *pos, *tmpq; - - mutex_lock(&scif_info.eplock); - list_for_each_safe(pos, tmpq, &scif_info.listen) { - ep = list_entry(pos, struct scif_endpt, list); - if (ep->port.port == port) { - mutex_unlock(&scif_info.eplock); - return ep; - } - } - mutex_unlock(&scif_info.eplock); - return NULL; -} - -void scif_cleanup_zombie_epd(void) -{ - struct list_head *pos, *tmpq; - struct scif_endpt *ep; - - mutex_lock(&scif_info.eplock); - list_for_each_safe(pos, tmpq, &scif_info.zombie) { - ep = list_entry(pos, struct scif_endpt, list); - if (scif_rma_ep_can_uninit(ep)) { - list_del(pos); - scif_info.nr_zombies--; - put_iova_domain(&ep->rma_info.iovad); - kfree(ep); - } - } - mutex_unlock(&scif_info.eplock); -} - -/** - * scif_cnctreq() - Respond to SCIF_CNCT_REQ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * This message is initiated by the remote node to request a connection - * to the local node. This function looks for an end point in the - * listen state on the requested port id. - * - * If it finds a listening port it places the connect request on the - * listening end points queue and wakes up any pending accept calls. - * - * If it does not find a listening end point it sends a connection - * reject message to the remote node. - */ -void scif_cnctreq(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = NULL; - struct scif_conreq *conreq; - - conreq = kmalloc(sizeof(*conreq), GFP_KERNEL); - if (!conreq) - /* Lack of resources so reject the request. */ - goto conreq_sendrej; - - ep = scif_find_listen_ep(msg->dst.port); - if (!ep) - /* Send reject due to no listening ports */ - goto conreq_sendrej_free; - else - spin_lock(&ep->lock); - - if (ep->backlog <= ep->conreqcnt) { - /* Send reject due to too many pending requests */ - spin_unlock(&ep->lock); - goto conreq_sendrej_free; - } - - conreq->msg = *msg; - list_add_tail(&conreq->list, &ep->conlist); - ep->conreqcnt++; - wake_up_interruptible(&ep->conwq); - spin_unlock(&ep->lock); - return; - -conreq_sendrej_free: - kfree(conreq); -conreq_sendrej: - msg->uop = SCIF_CNCT_REJ; - scif_nodeqp_send(&scif_dev[msg->src.node], msg); -} - -/** - * scif_cnctgnt() - Respond to SCIF_CNCT_GNT interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * An accept() on the remote node has occurred and sent this message - * to indicate success. Place the end point in the MAPPING state and - * save the remote nodes memory information. Then wake up the connect - * request so it can finish. - */ -void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTING == ep->state) { - ep->peer.node = msg->src.node; - ep->peer.port = msg->src.port; - ep->qp_info.gnt_pld = msg->payload[1]; - ep->remote_ep = msg->payload[2]; - ep->state = SCIFEP_MAPPING; - - wake_up(&ep->conwq); - } - spin_unlock(&ep->lock); -} - -/** - * scif_cnctgnt_ack() - Respond to SCIF_CNCT_GNTACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote connection request has finished mapping the local memory. - * Place the connection in the connected state and wake up the pending - * accept() call. - */ -void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - mutex_lock(&scif_info.connlock); - spin_lock(&ep->lock); - /* New ep is now connected with all resources set. */ - ep->state = SCIFEP_CONNECTED; - list_add_tail(&ep->list, &scif_info.connected); - wake_up(&ep->conwq); - spin_unlock(&ep->lock); - mutex_unlock(&scif_info.connlock); -} - -/** - * scif_cnctgnt_nack() - Respond to SCIF_CNCT_GNTNACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote connection request failed to map the local memory it was sent. - * Place the end point in the CLOSING state to indicate it and wake up - * the pending accept(); - */ -void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - ep->state = SCIFEP_CLOSING; - wake_up(&ep->conwq); - spin_unlock(&ep->lock); -} - -/** - * scif_cnctrej() - Respond to SCIF_CNCT_REJ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote end has rejected the connection request. Set the end - * point back to the bound state and wake up the pending connect(). - */ -void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTING == ep->state) { - ep->state = SCIFEP_BOUND; - wake_up(&ep->conwq); - } - spin_unlock(&ep->lock); -} - -/** - * scif_discnct() - Respond to SCIF_DISCNCT interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote node has indicated close() has been called on its end - * point. Remove the local end point from the connected list, set its - * state to disconnected and ensure accesses to the remote node are - * shutdown. - * - * When all accesses to the remote end have completed then send a - * DISCNT_ACK to indicate it can remove its resources and complete - * the close routine. - */ -void scif_discnct(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = NULL; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.connected) { - tmpep = list_entry(pos, struct scif_endpt, list); - /* - * The local ep may have sent a disconnect and and been closed - * due to a message response time out. It may have been - * allocated again and formed a new connection so we want to - * check if the remote ep matches - */ - if (((u64)tmpep == msg->payload[1]) && - ((u64)tmpep->remote_ep == msg->payload[0])) { - list_del(pos); - ep = tmpep; - spin_lock(&ep->lock); - break; - } - } - - /* - * If the terminated end is not found then this side started closing - * before the other side sent the disconnect. If so the ep will no - * longer be on the connected list. Regardless the other side - * needs to be acked to let it know close is complete. - */ - if (!ep) { - mutex_unlock(&scif_info.connlock); - goto discnct_ack; - } - - ep->state = SCIFEP_DISCONNECTED; - list_add_tail(&ep->list, &scif_info.disconnected); - - wake_up_interruptible(&ep->sendwq); - wake_up_interruptible(&ep->recvwq); - spin_unlock(&ep->lock); - mutex_unlock(&scif_info.connlock); - -discnct_ack: - msg->uop = SCIF_DISCNT_ACK; - scif_nodeqp_send(&scif_dev[msg->src.node], msg); -} - -/** - * scif_discnct_ack() - Respond to SCIF_DISCNT_ACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side has indicated it has not more references to local resources - */ -void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - ep->state = SCIFEP_DISCONNECTED; - spin_unlock(&ep->lock); - complete(&ep->discon); -} - -/** - * scif_clientsend() - Respond to SCIF_CLIENT_SEND interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side is confirming send or receive interrupt handling is complete. - */ -void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTED == ep->state) - wake_up_interruptible(&ep->recvwq); - spin_unlock(&ep->lock); -} - -/** - * scif_clientrcvd() - Respond to SCIF_CLIENT_RCVD interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side is confirming send or receive interrupt handling is complete. - */ -void scif_clientrcvd(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTED == ep->state) - wake_up_interruptible(&ep->sendwq); - spin_unlock(&ep->lock); -} diff --git a/drivers/misc/mic/scif/scif_epd.h b/drivers/misc/mic/scif/scif_epd.h deleted file mode 100644 index 0b9dfe1cc06c..000000000000 --- a/drivers/misc/mic/scif/scif_epd.h +++ /dev/null @@ -1,200 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_EPD_H -#define SCIF_EPD_H - -#include <linux/delay.h> -#include <linux/scif.h> -#include <linux/scif_ioctl.h> - -#define SCIF_EPLOCK_HELD true - -enum scif_epd_state { - SCIFEP_UNBOUND, - SCIFEP_BOUND, - SCIFEP_LISTENING, - SCIFEP_CONNECTED, - SCIFEP_CONNECTING, - SCIFEP_MAPPING, - SCIFEP_CLOSING, - SCIFEP_CLLISTEN, - SCIFEP_DISCONNECTED, - SCIFEP_ZOMBIE -}; - -/* - * struct scif_conreq - Data structure added to the connection list. - * - * @msg: connection request message received - * @list: link to list of connection requests - */ -struct scif_conreq { - struct scifmsg msg; - struct list_head list; -}; - -/* Size of the RB for the Endpoint QP */ -#define SCIF_ENDPT_QP_SIZE 0x1000 - -/* - * scif_endpt_qp_info - SCIF endpoint queue pair - * - * @qp - Qpair for this endpoint - * @qp_offset - DMA address of the QP - * @gnt_pld - Payload in a SCIF_CNCT_GNT message containing the - * physical address of the remote_qp. - */ -struct scif_endpt_qp_info { - struct scif_qp *qp; - dma_addr_t qp_offset; - dma_addr_t gnt_pld; -}; - -/* - * struct scif_endpt - The SCIF endpoint data structure - * - * @state: end point state - * @lock: lock synchronizing access to endpoint fields like state etc - * @port: self port information - * @peer: peer port information - * @backlog: maximum pending connection requests - * @qp_info: Endpoint QP information for SCIF messaging - * @remote_dev: scifdev used by this endpt to communicate with remote node. - * @remote_ep: remote endpoint - * @conreqcnt: Keep track of number of connection requests. - * @files: Open file information used to match the id passed in with - * the flush routine. - * @conlist: list of connection requests - * @conwq: waitqueue for connection processing - * @discon: completion used during disconnection - * @sendwq: waitqueue used during sending messages - * @recvwq: waitqueue used during message receipt - * @sendlock: Synchronize ordering of messages sent - * @recvlock: Synchronize ordering of messages received - * @list: link to list of various endpoints like connected, listening etc - * @li_accept: pending ACCEPTREG - * @acceptcnt: pending ACCEPTREG cnt - * @liacceptlist: link to listen accept - * @miacceptlist: link to uaccept - * @listenep: associated listen ep - * @conn_work: Non blocking connect work - * @conn_port: Connection port - * @conn_err: Errors during connection - * @conn_async_state: Async connection - * @conn_pend_wq: Used by poll while waiting for incoming connections - * @conn_list: List of async connection requests - * @rma_info: Information for triggering SCIF RMA and DMA operations - * @mmu_list: link to list of MMU notifier cleanup work - * @anon: anonymous file for use in kernel mode scif poll - */ -struct scif_endpt { - enum scif_epd_state state; - spinlock_t lock; - struct scif_port_id port; - struct scif_port_id peer; - int backlog; - struct scif_endpt_qp_info qp_info; - struct scif_dev *remote_dev; - u64 remote_ep; - int conreqcnt; - struct files_struct *files; - struct list_head conlist; - wait_queue_head_t conwq; - struct completion discon; - wait_queue_head_t sendwq; - wait_queue_head_t recvwq; - struct mutex sendlock; - struct mutex recvlock; - struct list_head list; - struct list_head li_accept; - int acceptcnt; - struct list_head liacceptlist; - struct list_head miacceptlist; - struct scif_endpt *listenep; - struct scif_port_id conn_port; - int conn_err; - int conn_async_state; - wait_queue_head_t conn_pend_wq; - struct list_head conn_list; - struct scif_endpt_rma_info rma_info; - struct list_head mmu_list; - struct file *anon; -}; - -static inline int scifdev_alive(struct scif_endpt *ep) -{ - return _scifdev_alive(ep->remote_dev); -} - -/* - * scif_verify_epd: - * ep: SCIF endpoint - * - * Checks several generic error conditions and returns the - * appropriate error. - */ -static inline int scif_verify_epd(struct scif_endpt *ep) -{ - if (ep->state == SCIFEP_DISCONNECTED) - return -ECONNRESET; - - if (ep->state != SCIFEP_CONNECTED) - return -ENOTCONN; - - if (!scifdev_alive(ep)) - return -ENODEV; - - return 0; -} - -static inline int scif_anon_inode_getfile(scif_epd_t epd) -{ - epd->anon = anon_inode_getfile("scif", &scif_anon_fops, NULL, 0); - - return PTR_ERR_OR_ZERO(epd->anon); -} - -static inline void scif_anon_inode_fput(scif_epd_t epd) -{ - if (epd->anon) { - fput(epd->anon); - epd->anon = NULL; - } -} - -void scif_cleanup_zombie_epd(void); -void scif_teardown_ep(void *endpt); -void scif_cleanup_ep_qp(struct scif_endpt *ep); -void scif_add_epd_to_zombie_list(struct scif_endpt *ep, bool eplock_held); -void scif_get_node_info(void); -void scif_send_acks(struct scif_dev *dev); -void scif_conn_handler(struct work_struct *work); -int scif_rsrv_port(u16 port); -void scif_get_port(u16 port); -int scif_get_new_port(void); -void scif_put_port(u16 port); -int scif_user_send(scif_epd_t epd, void __user *msg, int len, int flags); -int scif_user_recv(scif_epd_t epd, void __user *msg, int len, int flags); -void scif_cnctreq(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_discnct(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_clientrcvd(struct scif_dev *scifdev, struct scifmsg *msg); -int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block); -int __scif_flush(scif_epd_t epd); -int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd); -__poll_t __scif_pollfd(struct file *f, poll_table *wait, - struct scif_endpt *ep); -int __scif_pin_pages(void *addr, size_t len, int *out_prot, - int map_flags, scif_pinned_pages_t *pages); -#endif /* SCIF_EPD_H */ diff --git a/drivers/misc/mic/scif/scif_fd.c b/drivers/misc/mic/scif/scif_fd.c deleted file mode 100644 index 3f08646cd78a..000000000000 --- a/drivers/misc/mic/scif/scif_fd.c +++ /dev/null @@ -1,462 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" - -static int scif_fdopen(struct inode *inode, struct file *f) -{ - struct scif_endpt *priv = scif_open(); - - if (!priv) - return -ENOMEM; - f->private_data = priv; - return 0; -} - -static int scif_fdclose(struct inode *inode, struct file *f) -{ - struct scif_endpt *priv = f->private_data; - - return scif_close(priv); -} - -static int scif_fdmmap(struct file *f, struct vm_area_struct *vma) -{ - struct scif_endpt *priv = f->private_data; - - return scif_mmap(vma, priv); -} - -static __poll_t scif_fdpoll(struct file *f, poll_table *wait) -{ - struct scif_endpt *priv = f->private_data; - - return __scif_pollfd(f, wait, priv); -} - -static int scif_fdflush(struct file *f, fl_owner_t id) -{ - struct scif_endpt *ep = f->private_data; - - spin_lock(&ep->lock); - /* - * The listening endpoint stashes the open file information before - * waiting for incoming connections. The release callback would never be - * called if the application closed the endpoint, while waiting for - * incoming connections from a separate thread since the file descriptor - * reference count is bumped up in the accept IOCTL. Call the flush - * routine if the id matches the endpoint open file information so that - * the listening endpoint can be woken up and the fd released. - */ - if (ep->files == id) - __scif_flush(ep); - spin_unlock(&ep->lock); - return 0; -} - -static __always_inline void scif_err_debug(int err, const char *str) -{ - /* - * ENOTCONN is a common uninteresting error which is - * flooding debug messages to the console unnecessarily. - */ - if (err < 0 && err != -ENOTCONN) - dev_dbg(scif_info.mdev.this_device, "%s err %d\n", str, err); -} - -static long scif_fdioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - struct scif_endpt *priv = f->private_data; - void __user *argp = (void __user *)arg; - int err = 0; - struct scifioctl_msg request; - bool non_block = false; - - non_block = !!(f->f_flags & O_NONBLOCK); - - switch (cmd) { - case SCIF_BIND: - { - int pn; - - if (copy_from_user(&pn, argp, sizeof(pn))) - return -EFAULT; - - pn = scif_bind(priv, pn); - if (pn < 0) - return pn; - - if (copy_to_user(argp, &pn, sizeof(pn))) - return -EFAULT; - - return 0; - } - case SCIF_LISTEN: - return scif_listen(priv, arg); - case SCIF_CONNECT: - { - struct scifioctl_connect req; - struct scif_endpt *ep = (struct scif_endpt *)priv; - - if (copy_from_user(&req, argp, sizeof(req))) - return -EFAULT; - - err = __scif_connect(priv, &req.peer, non_block); - if (err < 0) - return err; - - req.self.node = ep->port.node; - req.self.port = ep->port.port; - - if (copy_to_user(argp, &req, sizeof(req))) - return -EFAULT; - - return 0; - } - /* - * Accept is done in two halves. The request ioctl does the basic - * functionality of accepting the request and returning the information - * about it including the internal ID of the end point. The register - * is done with the internal ID on a new file descriptor opened by the - * requesting process. - */ - case SCIF_ACCEPTREQ: - { - struct scifioctl_accept request; - scif_epd_t *ep = (scif_epd_t *)&request.endpt; - - if (copy_from_user(&request, argp, sizeof(request))) - return -EFAULT; - - err = scif_accept(priv, &request.peer, ep, request.flags); - if (err < 0) - return err; - - if (copy_to_user(argp, &request, sizeof(request))) { - scif_close(*ep); - return -EFAULT; - } - /* - * Add to the list of user mode eps where the second half - * of the accept is not yet completed. - */ - mutex_lock(&scif_info.eplock); - list_add_tail(&((*ep)->miacceptlist), &scif_info.uaccept); - list_add_tail(&((*ep)->liacceptlist), &priv->li_accept); - (*ep)->listenep = priv; - priv->acceptcnt++; - mutex_unlock(&scif_info.eplock); - - return 0; - } - case SCIF_ACCEPTREG: - { - struct scif_endpt *priv = f->private_data; - struct scif_endpt *newep; - struct scif_endpt *lisep; - struct scif_endpt *fep = NULL; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - - /* Finally replace the pointer to the accepted endpoint */ - if (copy_from_user(&newep, argp, sizeof(void *))) - return -EFAULT; - - /* Remove form the user accept queue */ - mutex_lock(&scif_info.eplock); - list_for_each_safe(pos, tmpq, &scif_info.uaccept) { - tmpep = list_entry(pos, - struct scif_endpt, miacceptlist); - if (tmpep == newep) { - list_del(pos); - fep = tmpep; - break; - } - } - - if (!fep) { - mutex_unlock(&scif_info.eplock); - return -ENOENT; - } - - lisep = newep->listenep; - list_for_each_safe(pos, tmpq, &lisep->li_accept) { - tmpep = list_entry(pos, - struct scif_endpt, liacceptlist); - if (tmpep == newep) { - list_del(pos); - lisep->acceptcnt--; - break; - } - } - - mutex_unlock(&scif_info.eplock); - - /* Free the resources automatically created from the open. */ - scif_anon_inode_fput(priv); - scif_teardown_ep(priv); - scif_add_epd_to_zombie_list(priv, !SCIF_EPLOCK_HELD); - f->private_data = newep; - return 0; - } - case SCIF_SEND: - { - struct scif_endpt *priv = f->private_data; - - if (copy_from_user(&request, argp, - sizeof(struct scifioctl_msg))) { - err = -EFAULT; - goto send_err; - } - err = scif_user_send(priv, (void __user *)request.msg, - request.len, request.flags); - if (err < 0) - goto send_err; - if (copy_to_user(& - ((struct scifioctl_msg __user *)argp)->out_len, - &err, sizeof(err))) { - err = -EFAULT; - goto send_err; - } - err = 0; -send_err: - scif_err_debug(err, "scif_send"); - return err; - } - case SCIF_RECV: - { - struct scif_endpt *priv = f->private_data; - - if (copy_from_user(&request, argp, - sizeof(struct scifioctl_msg))) { - err = -EFAULT; - goto recv_err; - } - - err = scif_user_recv(priv, (void __user *)request.msg, - request.len, request.flags); - if (err < 0) - goto recv_err; - - if (copy_to_user(& - ((struct scifioctl_msg __user *)argp)->out_len, - &err, sizeof(err))) { - err = -EFAULT; - goto recv_err; - } - err = 0; -recv_err: - scif_err_debug(err, "scif_recv"); - return err; - } - case SCIF_GET_NODEIDS: - { - struct scifioctl_node_ids node_ids; - int entries; - u16 *nodes; - void __user *unodes, *uself; - u16 self; - - if (copy_from_user(&node_ids, argp, sizeof(node_ids))) { - err = -EFAULT; - goto getnodes_err2; - } - - entries = min_t(int, scif_info.maxid, node_ids.len); - nodes = kmalloc_array(entries, sizeof(u16), GFP_KERNEL); - if (entries && !nodes) { - err = -ENOMEM; - goto getnodes_err2; - } - node_ids.len = scif_get_node_ids(nodes, entries, &self); - - unodes = (void __user *)node_ids.nodes; - if (copy_to_user(unodes, nodes, sizeof(u16) * entries)) { - err = -EFAULT; - goto getnodes_err1; - } - - uself = (void __user *)node_ids.self; - if (copy_to_user(uself, &self, sizeof(u16))) { - err = -EFAULT; - goto getnodes_err1; - } - - if (copy_to_user(argp, &node_ids, sizeof(node_ids))) { - err = -EFAULT; - goto getnodes_err1; - } -getnodes_err1: - kfree(nodes); -getnodes_err2: - return err; - } - case SCIF_REG: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_reg reg; - off_t ret; - - if (copy_from_user(®, argp, sizeof(reg))) { - err = -EFAULT; - goto reg_err; - } - if (reg.flags & SCIF_MAP_KERNEL) { - err = -EINVAL; - goto reg_err; - } - ret = scif_register(priv, (void *)reg.addr, reg.len, - reg.offset, reg.prot, reg.flags); - if (ret < 0) { - err = (int)ret; - goto reg_err; - } - - if (copy_to_user(&((struct scifioctl_reg __user *)argp) - ->out_offset, &ret, sizeof(reg.out_offset))) { - err = -EFAULT; - goto reg_err; - } - err = 0; -reg_err: - scif_err_debug(err, "scif_register"); - return err; - } - case SCIF_UNREG: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_unreg unreg; - - if (copy_from_user(&unreg, argp, sizeof(unreg))) { - err = -EFAULT; - goto unreg_err; - } - err = scif_unregister(priv, unreg.offset, unreg.len); -unreg_err: - scif_err_debug(err, "scif_unregister"); - return err; - } - case SCIF_READFROM: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto readfrom_err; - } - err = scif_readfrom(priv, copy.loffset, copy.len, copy.roffset, - copy.flags); -readfrom_err: - scif_err_debug(err, "scif_readfrom"); - return err; - } - case SCIF_WRITETO: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto writeto_err; - } - err = scif_writeto(priv, copy.loffset, copy.len, copy.roffset, - copy.flags); -writeto_err: - scif_err_debug(err, "scif_writeto"); - return err; - } - case SCIF_VREADFROM: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto vreadfrom_err; - } - err = scif_vreadfrom(priv, (void __force *)copy.addr, copy.len, - copy.roffset, copy.flags); -vreadfrom_err: - scif_err_debug(err, "scif_vreadfrom"); - return err; - } - case SCIF_VWRITETO: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto vwriteto_err; - } - err = scif_vwriteto(priv, (void __force *)copy.addr, copy.len, - copy.roffset, copy.flags); -vwriteto_err: - scif_err_debug(err, "scif_vwriteto"); - return err; - } - case SCIF_FENCE_MARK: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_fence_mark mark; - int tmp_mark = 0; - - if (copy_from_user(&mark, argp, sizeof(mark))) { - err = -EFAULT; - goto fence_mark_err; - } - err = scif_fence_mark(priv, mark.flags, &tmp_mark); - if (err) - goto fence_mark_err; - if (copy_to_user((void __user *)mark.mark, &tmp_mark, - sizeof(tmp_mark))) { - err = -EFAULT; - goto fence_mark_err; - } -fence_mark_err: - scif_err_debug(err, "scif_fence_mark"); - return err; - } - case SCIF_FENCE_WAIT: - { - struct scif_endpt *priv = f->private_data; - - err = scif_fence_wait(priv, arg); - scif_err_debug(err, "scif_fence_wait"); - return err; - } - case SCIF_FENCE_SIGNAL: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_fence_signal signal; - - if (copy_from_user(&signal, argp, sizeof(signal))) { - err = -EFAULT; - goto fence_signal_err; - } - - err = scif_fence_signal(priv, signal.loff, signal.lval, - signal.roff, signal.rval, signal.flags); -fence_signal_err: - scif_err_debug(err, "scif_fence_signal"); - return err; - } - } - return -EINVAL; -} - -const struct file_operations scif_fops = { - .open = scif_fdopen, - .release = scif_fdclose, - .unlocked_ioctl = scif_fdioctl, - .mmap = scif_fdmmap, - .poll = scif_fdpoll, - .flush = scif_fdflush, - .owner = THIS_MODULE, -}; diff --git a/drivers/misc/mic/scif/scif_fence.c b/drivers/misc/mic/scif/scif_fence.c deleted file mode 100644 index 4fedf6183951..000000000000 --- a/drivers/misc/mic/scif/scif_fence.c +++ /dev/null @@ -1,783 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ - -#include "scif_main.h" - -/** - * scif_recv_mark: Handle SCIF_MARK request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested a mark. - */ -void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - int mark = 0; - int err; - - err = _scif_fence_mark(ep, &mark); - if (err) - msg->uop = SCIF_MARK_NACK; - else - msg->uop = SCIF_MARK_ACK; - msg->payload[0] = ep->remote_ep; - msg->payload[2] = mark; - scif_nodeqp_send(ep->remote_dev, msg); -} - -/** - * scif_recv_mark_resp: Handle SCIF_MARK_(N)ACK messages. - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has responded to a SCIF_MARK message. - */ -void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_fence_info *fence_req = - (struct scif_fence_info *)msg->payload[1]; - - mutex_lock(&ep->rma_info.rma_lock); - if (msg->uop == SCIF_MARK_ACK) { - fence_req->state = OP_COMPLETED; - fence_req->dma_mark = (int)msg->payload[2]; - } else { - fence_req->state = OP_FAILED; - } - mutex_unlock(&ep->rma_info.rma_lock); - complete(&fence_req->comp); -} - -/** - * scif_recv_wait: Handle SCIF_WAIT request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested waiting on a fence. - */ -void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_remote_fence_info *fence; - - /* - * Allocate structure for remote fence information and - * send a NACK if the allocation failed. The peer will - * return ENOMEM upon receiving a NACK. - */ - fence = kmalloc(sizeof(*fence), GFP_KERNEL); - if (!fence) { - msg->payload[0] = ep->remote_ep; - msg->uop = SCIF_WAIT_NACK; - scif_nodeqp_send(ep->remote_dev, msg); - return; - } - - /* Prepare the fence request */ - memcpy(&fence->msg, msg, sizeof(struct scifmsg)); - INIT_LIST_HEAD(&fence->list); - - /* Insert to the global remote fence request list */ - mutex_lock(&scif_info.fencelock); - atomic_inc(&ep->rma_info.fence_refcount); - list_add_tail(&fence->list, &scif_info.fence); - mutex_unlock(&scif_info.fencelock); - - schedule_work(&scif_info.misc_work); -} - -/** - * scif_recv_wait_resp: Handle SCIF_WAIT_(N)ACK messages. - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has responded to a SCIF_WAIT message. - */ -void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_fence_info *fence_req = - (struct scif_fence_info *)msg->payload[1]; - - mutex_lock(&ep->rma_info.rma_lock); - if (msg->uop == SCIF_WAIT_ACK) - fence_req->state = OP_COMPLETED; - else - fence_req->state = OP_FAILED; - mutex_unlock(&ep->rma_info.rma_lock); - complete(&fence_req->comp); -} - -/** - * scif_recv_sig_local: Handle SCIF_SIG_LOCAL request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested a signal on a local offset. - */ -void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - int err; - - err = scif_prog_signal(ep, msg->payload[1], msg->payload[2], - SCIF_WINDOW_SELF); - if (err) - msg->uop = SCIF_SIG_NACK; - else - msg->uop = SCIF_SIG_ACK; - msg->payload[0] = ep->remote_ep; - scif_nodeqp_send(ep->remote_dev, msg); -} - -/** - * scif_recv_sig_remote: Handle SCIF_SIGNAL_REMOTE request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested a signal on a remote offset. - */ -void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - int err; - - err = scif_prog_signal(ep, msg->payload[1], msg->payload[2], - SCIF_WINDOW_PEER); - if (err) - msg->uop = SCIF_SIG_NACK; - else - msg->uop = SCIF_SIG_ACK; - msg->payload[0] = ep->remote_ep; - scif_nodeqp_send(ep->remote_dev, msg); -} - -/** - * scif_recv_sig_resp: Handle SCIF_SIG_(N)ACK messages. - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has responded to a signal request. - */ -void scif_recv_sig_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_fence_info *fence_req = - (struct scif_fence_info *)msg->payload[3]; - - mutex_lock(&ep->rma_info.rma_lock); - if (msg->uop == SCIF_SIG_ACK) - fence_req->state = OP_COMPLETED; - else - fence_req->state = OP_FAILED; - mutex_unlock(&ep->rma_info.rma_lock); - complete(&fence_req->comp); -} - -static inline void *scif_get_local_va(off_t off, struct scif_window *window) -{ - struct page **pages = window->pinned_pages->pages; - int page_nr = (off - window->offset) >> PAGE_SHIFT; - off_t page_off = off & ~PAGE_MASK; - - return page_address(pages[page_nr]) + page_off; -} - -static void scif_prog_signal_cb(void *arg) -{ - struct scif_cb_arg *cb_arg = arg; - - dma_pool_free(cb_arg->ep->remote_dev->signal_pool, cb_arg->status, - cb_arg->src_dma_addr); - kfree(cb_arg); -} - -static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct dma_chan *chan = ep->rma_info.dma_chan; - struct dma_device *ddev = chan->device; - bool x100 = !is_dma_copy_aligned(chan->device, 1, 1, 1); - struct dma_async_tx_descriptor *tx; - struct scif_status *status = NULL; - struct scif_cb_arg *cb_arg = NULL; - dma_addr_t src; - dma_cookie_t cookie; - int err; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto alloc_fail; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = (int)cookie; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto alloc_fail; - } - dma_async_issue_pending(chan); - if (x100) { - /* - * For X100 use the status descriptor to write the value to - * the destination. - */ - tx = ddev->device_prep_dma_imm_data(chan, dst, val, 0); - } else { - status = dma_pool_alloc(ep->remote_dev->signal_pool, GFP_KERNEL, - &src); - if (!status) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto alloc_fail; - } - status->val = val; - status->src_dma_addr = src; - status->ep = ep; - src += offsetof(struct scif_status, val); - tx = ddev->device_prep_dma_memcpy(chan, dst, src, sizeof(val), - DMA_PREP_INTERRUPT); - } - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto dma_fail; - } - if (!x100) { - cb_arg = kmalloc(sizeof(*cb_arg), GFP_KERNEL); - if (!cb_arg) { - err = -ENOMEM; - goto dma_fail; - } - cb_arg->src_dma_addr = src; - cb_arg->status = status; - cb_arg->ep = ep; - tx->callback = scif_prog_signal_cb; - tx->callback_param = cb_arg; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = -EIO; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto dma_fail; - } - dma_async_issue_pending(chan); - return 0; -dma_fail: - if (!x100) { - dma_pool_free(ep->remote_dev->signal_pool, status, - src - offsetof(struct scif_status, val)); - kfree(cb_arg); - } -alloc_fail: - return err; -} - -/** - * scif_prog_signal: - * @epd: Endpoint Descriptor - * @offset: registered address to write @val to - * @val: Value to be written at @offset - * @type: Type of the window. - * - * Arrange to write a value to the registered offset after ensuring that the - * offset provided is indeed valid. - */ -int scif_prog_signal(scif_epd_t epd, off_t offset, u64 val, - enum scif_window_type type) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_window *window = NULL; - struct scif_rma_req req; - dma_addr_t dst_dma_addr; - int err; - - mutex_lock(&ep->rma_info.rma_lock); - req.out_window = &window; - req.offset = offset; - req.nr_bytes = sizeof(u64); - req.prot = SCIF_PROT_WRITE; - req.type = SCIF_WINDOW_SINGLE; - if (type == SCIF_WINDOW_SELF) - req.head = &ep->rma_info.reg_list; - else - req.head = &ep->rma_info.remote_reg_list; - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - goto unlock_ret; - } - - if (scif_is_mgmt_node() && scifdev_self(ep->remote_dev)) { - u64 *dst_virt; - - if (type == SCIF_WINDOW_SELF) - dst_virt = scif_get_local_va(offset, window); - else - dst_virt = - scif_get_local_va(offset, (struct scif_window *) - window->peer_window); - *dst_virt = val; - } else { - dst_dma_addr = __scif_off_to_dma_addr(window, offset); - err = _scif_prog_signal(epd, dst_dma_addr, val); - } -unlock_ret: - mutex_unlock(&ep->rma_info.rma_lock); - return err; -} - -static int _scif_fence_wait(scif_epd_t epd, int mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - dma_cookie_t cookie = mark & ~SCIF_REMOTE_FENCE; - int err; - - /* Wait for DMA callback in scif_fence_mark_cb(..) */ - err = wait_event_interruptible_timeout(ep->rma_info.markwq, - dma_async_is_tx_complete( - ep->rma_info.dma_chan, - cookie, NULL, NULL) == - DMA_COMPLETE, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err) - err = -ETIMEDOUT; - else if (err > 0) - err = 0; - return err; -} - -/** - * scif_rma_handle_remote_fences: - * - * This routine services remote fence requests. - */ -void scif_rma_handle_remote_fences(void) -{ - struct list_head *item, *tmp; - struct scif_remote_fence_info *fence; - struct scif_endpt *ep; - int mark, err; - - might_sleep(); - mutex_lock(&scif_info.fencelock); - list_for_each_safe(item, tmp, &scif_info.fence) { - fence = list_entry(item, struct scif_remote_fence_info, - list); - /* Remove fence from global list */ - list_del(&fence->list); - - /* Initiate the fence operation */ - ep = (struct scif_endpt *)fence->msg.payload[0]; - mark = fence->msg.payload[2]; - err = _scif_fence_wait(ep, mark); - if (err) - fence->msg.uop = SCIF_WAIT_NACK; - else - fence->msg.uop = SCIF_WAIT_ACK; - fence->msg.payload[0] = ep->remote_ep; - scif_nodeqp_send(ep->remote_dev, &fence->msg); - kfree(fence); - if (!atomic_sub_return(1, &ep->rma_info.fence_refcount)) - schedule_work(&scif_info.misc_work); - } - mutex_unlock(&scif_info.fencelock); -} - -static int _scif_send_fence(scif_epd_t epd, int uop, int mark, int *out_mark) -{ - int err; - struct scifmsg msg; - struct scif_fence_info *fence_req; - struct scif_endpt *ep = (struct scif_endpt *)epd; - - fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL); - if (!fence_req) { - err = -ENOMEM; - goto error; - } - - fence_req->state = OP_IN_PROGRESS; - init_completion(&fence_req->comp); - - msg.src = ep->port; - msg.uop = uop; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = (u64)fence_req; - if (uop == SCIF_WAIT) - msg.payload[2] = mark; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - if (err) - goto error_free; -retry: - /* Wait for a SCIF_WAIT_(N)ACK message */ - err = wait_for_completion_timeout(&fence_req->comp, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - if (!err) - err = -ENODEV; - if (err > 0) - err = 0; - mutex_lock(&ep->rma_info.rma_lock); - if (err < 0) { - if (fence_req->state == OP_IN_PROGRESS) - fence_req->state = OP_FAILED; - } - if (fence_req->state == OP_FAILED && !err) - err = -ENOMEM; - if (uop == SCIF_MARK && fence_req->state == OP_COMPLETED) - *out_mark = SCIF_REMOTE_FENCE | fence_req->dma_mark; - mutex_unlock(&ep->rma_info.rma_lock); -error_free: - kfree(fence_req); -error: - return err; -} - -/** - * scif_send_fence_mark: - * @epd: end point descriptor. - * @out_mark: Output DMA mark reported by peer. - * - * Send a remote fence mark request. - */ -static int scif_send_fence_mark(scif_epd_t epd, int *out_mark) -{ - return _scif_send_fence(epd, SCIF_MARK, 0, out_mark); -} - -/** - * scif_send_fence_wait: - * @epd: end point descriptor. - * @mark: DMA mark to wait for. - * - * Send a remote fence wait request. - */ -static int scif_send_fence_wait(scif_epd_t epd, int mark) -{ - return _scif_send_fence(epd, SCIF_WAIT, mark, NULL); -} - -static int _scif_send_fence_signal_wait(struct scif_endpt *ep, - struct scif_fence_info *fence_req) -{ - int err; - -retry: - /* Wait for a SCIF_SIG_(N)ACK message */ - err = wait_for_completion_timeout(&fence_req->comp, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - if (!err) - err = -ENODEV; - if (err > 0) - err = 0; - if (err < 0) { - mutex_lock(&ep->rma_info.rma_lock); - if (fence_req->state == OP_IN_PROGRESS) - fence_req->state = OP_FAILED; - mutex_unlock(&ep->rma_info.rma_lock); - } - if (fence_req->state == OP_FAILED && !err) - err = -ENXIO; - return err; -} - -/** - * scif_send_fence_signal: - * @epd: endpoint descriptor - * @loff: local offset - * @lval: local value to write to loffset - * @roff: remote offset - * @rval: remote value to write to roffset - * @flags: flags - * - * Sends a remote fence signal request - */ -static int scif_send_fence_signal(scif_epd_t epd, off_t roff, u64 rval, - off_t loff, u64 lval, int flags) -{ - int err = 0; - struct scifmsg msg; - struct scif_fence_info *fence_req; - struct scif_endpt *ep = (struct scif_endpt *)epd; - - fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL); - if (!fence_req) { - err = -ENOMEM; - goto error; - } - - fence_req->state = OP_IN_PROGRESS; - init_completion(&fence_req->comp); - msg.src = ep->port; - if (flags & SCIF_SIGNAL_LOCAL) { - msg.uop = SCIF_SIG_LOCAL; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = roff; - msg.payload[2] = rval; - msg.payload[3] = (u64)fence_req; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - if (err) - goto error_free; - err = _scif_send_fence_signal_wait(ep, fence_req); - if (err) - goto error_free; - } - fence_req->state = OP_IN_PROGRESS; - - if (flags & SCIF_SIGNAL_REMOTE) { - msg.uop = SCIF_SIG_REMOTE; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = loff; - msg.payload[2] = lval; - msg.payload[3] = (u64)fence_req; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - if (err) - goto error_free; - err = _scif_send_fence_signal_wait(ep, fence_req); - } -error_free: - kfree(fence_req); -error: - return err; -} - -static void scif_fence_mark_cb(void *arg) -{ - struct scif_endpt *ep = (struct scif_endpt *)arg; - - wake_up_interruptible(&ep->rma_info.markwq); - atomic_dec(&ep->rma_info.fence_refcount); -} - -/** - * _scif_fence_mark: - * @epd: endpoint descriptor - * @mark: DMA mark to set-up - * - * Set up a mark for this endpoint and return the value of the mark. - */ -int _scif_fence_mark(scif_epd_t epd, int *mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct dma_chan *chan = ep->rma_info.dma_chan; - struct dma_device *ddev = chan->device; - struct dma_async_tx_descriptor *tx; - dma_cookie_t cookie; - int err; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = (int)cookie; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - dma_async_issue_pending(chan); - tx = ddev->device_prep_dma_interrupt(chan, DMA_PREP_INTERRUPT); - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - tx->callback = scif_fence_mark_cb; - tx->callback_param = ep; - *mark = cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = (int)cookie; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - atomic_inc(&ep->rma_info.fence_refcount); - dma_async_issue_pending(chan); - return 0; -} - -#define SCIF_LOOPB_MAGIC_MARK 0xdead - -int scif_fence_mark(scif_epd_t epd, int flags, int *mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x\n", - ep, flags, *mark); - err = scif_verify_epd(ep); - if (err) - return err; - - /* Invalid flags? */ - if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER)) - return -EINVAL; - - /* At least one of init self or peer RMA should be set */ - if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))) - return -EINVAL; - - /* Exactly one of init self or peer RMA should be set but not both */ - if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER)) - return -EINVAL; - - /* - * Management node loopback does not need to use DMA. - * Return a valid mark to be symmetric. - */ - if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) { - *mark = SCIF_LOOPB_MAGIC_MARK; - return 0; - } - - if (flags & SCIF_FENCE_INIT_SELF) - err = _scif_fence_mark(epd, mark); - else - err = scif_send_fence_mark(ep, mark); - - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x err %d\n", - ep, flags, *mark, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_fence_mark); - -int scif_fence_wait(scif_epd_t epd, int mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_wait: ep %p mark 0x%x\n", - ep, mark); - err = scif_verify_epd(ep); - if (err) - return err; - /* - * Management node loopback does not need to use DMA. - * The only valid mark provided is 0 so simply - * return success if the mark is valid. - */ - if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) { - if (mark == SCIF_LOOPB_MAGIC_MARK) - return 0; - else - return -EINVAL; - } - if (mark & SCIF_REMOTE_FENCE) - err = scif_send_fence_wait(epd, mark); - else - err = _scif_fence_wait(epd, mark); - if (err < 0) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_fence_wait); - -int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval, - off_t roff, u64 rval, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_signal: ep %p loff 0x%lx lval 0x%llx roff 0x%lx rval 0x%llx flags 0x%x\n", - ep, loff, lval, roff, rval, flags); - err = scif_verify_epd(ep); - if (err) - return err; - - /* Invalid flags? */ - if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER | - SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE)) - return -EINVAL; - - /* At least one of init self or peer RMA should be set */ - if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))) - return -EINVAL; - - /* Exactly one of init self or peer RMA should be set but not both */ - if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER)) - return -EINVAL; - - /* At least one of SCIF_SIGNAL_LOCAL or SCIF_SIGNAL_REMOTE required */ - if (!(flags & (SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE))) - return -EINVAL; - - /* Only Dword offsets allowed */ - if ((flags & SCIF_SIGNAL_LOCAL) && (loff & (sizeof(u32) - 1))) - return -EINVAL; - - /* Only Dword aligned offsets allowed */ - if ((flags & SCIF_SIGNAL_REMOTE) && (roff & (sizeof(u32) - 1))) - return -EINVAL; - - if (flags & SCIF_FENCE_INIT_PEER) { - err = scif_send_fence_signal(epd, roff, rval, loff, - lval, flags); - } else { - /* Local Signal in Local RAS */ - if (flags & SCIF_SIGNAL_LOCAL) { - err = scif_prog_signal(epd, loff, lval, - SCIF_WINDOW_SELF); - if (err) - goto error_ret; - } - - /* Signal in Remote RAS */ - if (flags & SCIF_SIGNAL_REMOTE) - err = scif_prog_signal(epd, roff, - rval, SCIF_WINDOW_PEER); - } -error_ret: - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_fence_signal); diff --git a/drivers/misc/mic/scif/scif_main.c b/drivers/misc/mic/scif/scif_main.c deleted file mode 100644 index e2278bf9f11d..000000000000 --- a/drivers/misc/mic/scif/scif_main.c +++ /dev/null @@ -1,351 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include <linux/module.h> -#include <linux/idr.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" -#include "../bus/scif_bus.h" -#include "scif_peer_bus.h" -#include "scif_main.h" -#include "scif_map.h" - -struct scif_info scif_info = { - .mdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "scif", - .fops = &scif_fops, - } -}; - -struct scif_dev *scif_dev; -struct kmem_cache *unaligned_cache; -static atomic_t g_loopb_cnt; - -/* Runs in the context of intr_wq */ -static void scif_intr_bh_handler(struct work_struct *work) -{ - struct scif_dev *scifdev = - container_of(work, struct scif_dev, intr_bh); - - if (scifdev_self(scifdev)) - scif_loopb_msg_handler(scifdev, scifdev->qpairs); - else - scif_nodeqp_intrhandler(scifdev, scifdev->qpairs); -} - -int scif_setup_intr_wq(struct scif_dev *scifdev) -{ - if (!scifdev->intr_wq) { - snprintf(scifdev->intr_wqname, sizeof(scifdev->intr_wqname), - "SCIF INTR %d", scifdev->node); - scifdev->intr_wq = - alloc_ordered_workqueue(scifdev->intr_wqname, 0); - if (!scifdev->intr_wq) - return -ENOMEM; - INIT_WORK(&scifdev->intr_bh, scif_intr_bh_handler); - } - return 0; -} - -void scif_destroy_intr_wq(struct scif_dev *scifdev) -{ - if (scifdev->intr_wq) { - destroy_workqueue(scifdev->intr_wq); - scifdev->intr_wq = NULL; - } -} - -irqreturn_t scif_intr_handler(int irq, void *data) -{ - struct scif_dev *scifdev = data; - struct scif_hw_dev *sdev = scifdev->sdev; - - sdev->hw_ops->ack_interrupt(sdev, scifdev->db); - queue_work(scifdev->intr_wq, &scifdev->intr_bh); - return IRQ_HANDLED; -} - -static void scif_qp_setup_handler(struct work_struct *work) -{ - struct scif_dev *scifdev = container_of(work, struct scif_dev, - qp_dwork.work); - struct scif_hw_dev *sdev = scifdev->sdev; - dma_addr_t da = 0; - int err; - - if (scif_is_mgmt_node()) { - struct mic_bootparam *bp = sdev->dp; - - da = bp->scif_card_dma_addr; - scifdev->rdb = bp->h2c_scif_db; - } else { - struct mic_bootparam __iomem *bp = sdev->rdp; - - da = readq(&bp->scif_host_dma_addr); - scifdev->rdb = ioread8(&bp->c2h_scif_db); - } - if (da) { - err = scif_qp_response(da, scifdev); - if (err) - dev_err(&scifdev->sdev->dev, - "scif_qp_response err %d\n", err); - } else { - schedule_delayed_work(&scifdev->qp_dwork, - msecs_to_jiffies(1000)); - } -} - -static int scif_setup_scifdev(void) -{ - /* We support a maximum of 129 SCIF nodes including the mgmt node */ -#define MAX_SCIF_NODES 129 - int i; - u8 num_nodes = MAX_SCIF_NODES; - - scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL); - if (!scif_dev) - return -ENOMEM; - for (i = 0; i < num_nodes; i++) { - struct scif_dev *scifdev = &scif_dev[i]; - - scifdev->node = i; - scifdev->exit = OP_IDLE; - init_waitqueue_head(&scifdev->disconn_wq); - mutex_init(&scifdev->lock); - INIT_WORK(&scifdev->peer_add_work, scif_add_peer_device); - INIT_DELAYED_WORK(&scifdev->p2p_dwork, - scif_poll_qp_state); - INIT_DELAYED_WORK(&scifdev->qp_dwork, - scif_qp_setup_handler); - INIT_LIST_HEAD(&scifdev->p2p); - RCU_INIT_POINTER(scifdev->spdev, NULL); - } - return 0; -} - -static void scif_destroy_scifdev(void) -{ - kfree(scif_dev); - scif_dev = NULL; -} - -static int scif_probe(struct scif_hw_dev *sdev) -{ - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; - int rc; - - dev_set_drvdata(&sdev->dev, sdev); - scifdev->sdev = sdev; - - if (1 == atomic_add_return(1, &g_loopb_cnt)) { - struct scif_dev *loopb_dev = &scif_dev[sdev->snode]; - - loopb_dev->sdev = sdev; - rc = scif_setup_loopback_qp(loopb_dev); - if (rc) - goto exit; - } - - rc = scif_setup_intr_wq(scifdev); - if (rc) - goto destroy_loopb; - rc = scif_setup_qp(scifdev); - if (rc) - goto destroy_intr; - scifdev->db = sdev->hw_ops->next_db(sdev); - scifdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler, - "SCIF_INTR", scifdev, - scifdev->db); - if (IS_ERR(scifdev->cookie)) { - rc = PTR_ERR(scifdev->cookie); - goto free_qp; - } - if (scif_is_mgmt_node()) { - struct mic_bootparam *bp = sdev->dp; - - bp->c2h_scif_db = scifdev->db; - bp->scif_host_dma_addr = scifdev->qp_dma_addr; - } else { - struct mic_bootparam __iomem *bp = sdev->rdp; - - iowrite8(scifdev->db, &bp->h2c_scif_db); - writeq(scifdev->qp_dma_addr, &bp->scif_card_dma_addr); - } - schedule_delayed_work(&scifdev->qp_dwork, - msecs_to_jiffies(1000)); - return rc; -free_qp: - scif_free_qp(scifdev); -destroy_intr: - scif_destroy_intr_wq(scifdev); -destroy_loopb: - if (atomic_dec_and_test(&g_loopb_cnt)) - scif_destroy_loopback_qp(&scif_dev[sdev->snode]); -exit: - return rc; -} - -void scif_stop(struct scif_dev *scifdev) -{ - struct scif_dev *dev; - int i; - - for (i = scif_info.maxid; i >= 0; i--) { - dev = &scif_dev[i]; - if (scifdev_self(dev)) - continue; - scif_handle_remove_node(i); - } -} - -static void scif_remove(struct scif_hw_dev *sdev) -{ - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; - - if (scif_is_mgmt_node()) { - struct mic_bootparam *bp = sdev->dp; - - bp->c2h_scif_db = -1; - bp->scif_host_dma_addr = 0x0; - } else { - struct mic_bootparam __iomem *bp = sdev->rdp; - - iowrite8(-1, &bp->h2c_scif_db); - writeq(0x0, &bp->scif_card_dma_addr); - } - if (scif_is_mgmt_node()) { - scif_disconnect_node(scifdev->node, true); - } else { - scif_info.card_initiated_exit = true; - scif_stop(scifdev); - } - if (atomic_dec_and_test(&g_loopb_cnt)) - scif_destroy_loopback_qp(&scif_dev[sdev->snode]); - if (scifdev->cookie) { - sdev->hw_ops->free_irq(sdev, scifdev->cookie, scifdev); - scifdev->cookie = NULL; - } - scif_destroy_intr_wq(scifdev); - cancel_delayed_work(&scifdev->qp_dwork); - scif_free_qp(scifdev); - scifdev->rdb = -1; - scifdev->sdev = NULL; -} - -static struct scif_hw_dev_id id_table[] = { - { MIC_SCIF_DEV, SCIF_DEV_ANY_ID }, - { 0 }, -}; - -static struct scif_driver scif_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = scif_probe, - .remove = scif_remove, -}; - -static int _scif_init(void) -{ - int rc; - - mutex_init(&scif_info.eplock); - spin_lock_init(&scif_info.rmalock); - spin_lock_init(&scif_info.nb_connect_lock); - spin_lock_init(&scif_info.port_lock); - mutex_init(&scif_info.conflock); - mutex_init(&scif_info.connlock); - mutex_init(&scif_info.fencelock); - INIT_LIST_HEAD(&scif_info.uaccept); - INIT_LIST_HEAD(&scif_info.listen); - INIT_LIST_HEAD(&scif_info.zombie); - INIT_LIST_HEAD(&scif_info.connected); - INIT_LIST_HEAD(&scif_info.disconnected); - INIT_LIST_HEAD(&scif_info.rma); - INIT_LIST_HEAD(&scif_info.rma_tc); - INIT_LIST_HEAD(&scif_info.mmu_notif_cleanup); - INIT_LIST_HEAD(&scif_info.fence); - INIT_LIST_HEAD(&scif_info.nb_connect_list); - init_waitqueue_head(&scif_info.exitwq); - scif_info.rma_tc_limit = SCIF_RMA_TEMP_CACHE_LIMIT; - scif_info.en_msg_log = 0; - scif_info.p2p_enable = 1; - rc = scif_setup_scifdev(); - if (rc) - goto error; - unaligned_cache = kmem_cache_create("Unaligned_DMA", - SCIF_KMEM_UNALIGNED_BUF_SIZE, - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!unaligned_cache) { - rc = -ENOMEM; - goto free_sdev; - } - INIT_WORK(&scif_info.misc_work, scif_misc_handler); - INIT_WORK(&scif_info.mmu_notif_work, scif_mmu_notif_handler); - INIT_WORK(&scif_info.conn_work, scif_conn_handler); - idr_init(&scif_ports); - return 0; -free_sdev: - scif_destroy_scifdev(); -error: - return rc; -} - -static void _scif_exit(void) -{ - idr_destroy(&scif_ports); - kmem_cache_destroy(unaligned_cache); - scif_destroy_scifdev(); -} - -static int __init scif_init(void) -{ - struct miscdevice *mdev = &scif_info.mdev; - int rc; - - _scif_init(); - iova_cache_get(); - rc = scif_peer_bus_init(); - if (rc) - goto exit; - rc = scif_register_driver(&scif_driver); - if (rc) - goto peer_bus_exit; - rc = misc_register(mdev); - if (rc) - goto unreg_scif; - scif_init_debugfs(); - return 0; -unreg_scif: - scif_unregister_driver(&scif_driver); -peer_bus_exit: - scif_peer_bus_exit(); -exit: - _scif_exit(); - return rc; -} - -static void __exit scif_exit(void) -{ - scif_exit_debugfs(); - misc_deregister(&scif_info.mdev); - scif_unregister_driver(&scif_driver); - scif_peer_bus_exit(); - iova_cache_put(); - _scif_exit(); -} - -module_init(scif_init); -module_exit(scif_exit); - -MODULE_DEVICE_TABLE(scif, id_table); -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) SCIF driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/scif/scif_main.h b/drivers/misc/mic/scif/scif_main.h deleted file mode 100644 index bb3ab97d5b35..000000000000 --- a/drivers/misc/mic/scif/scif_main.h +++ /dev/null @@ -1,274 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_MAIN_H -#define SCIF_MAIN_H - -#include <linux/sched/signal.h> -#include <linux/pci.h> -#include <linux/miscdevice.h> -#include <linux/dmaengine.h> -#include <linux/iova.h> -#include <linux/anon_inodes.h> -#include <linux/file.h> -#include <linux/vmalloc.h> -#include <linux/scif.h> -#include "../common/mic_dev.h" - -#define SCIF_MGMT_NODE 0 -#define SCIF_DEFAULT_WATCHDOG_TO 30 -#define SCIF_NODE_ACCEPT_TIMEOUT (3 * HZ) -#define SCIF_NODE_ALIVE_TIMEOUT (SCIF_DEFAULT_WATCHDOG_TO * HZ) -#define SCIF_RMA_TEMP_CACHE_LIMIT 0x20000 - -/* - * Generic state used for certain node QP message exchanges - * like Unregister, Alloc etc. - */ -enum scif_msg_state { - OP_IDLE = 1, - OP_IN_PROGRESS, - OP_COMPLETED, - OP_FAILED -}; - -/* - * struct scif_info - Global SCIF information - * - * @nodeid: Node ID this node is to others - * @maxid: Max known node ID - * @total: Total number of SCIF nodes - * @nr_zombies: number of zombie endpoints - * @eplock: Lock to synchronize listening, zombie endpoint lists - * @connlock: Lock to synchronize connected and disconnected lists - * @nb_connect_lock: Synchronize non blocking connect operations - * @port_lock: Synchronize access to SCIF ports - * @uaccept: List of user acceptreq waiting for acceptreg - * @listen: List of listening end points - * @zombie: List of zombie end points with pending RMA's - * @connected: List of end points in connected state - * @disconnected: List of end points in disconnected state - * @nb_connect_list: List for non blocking connections - * @misc_work: miscellaneous SCIF tasks - * @conflock: Lock to synchronize SCIF node configuration changes - * @en_msg_log: Enable debug message logging - * @p2p_enable: Enable P2P SCIF network - * @mdev: The MISC device - * @conn_work: Work for workqueue handling all connections - * @exitwq: Wait queue for waiting for an EXIT node QP message response - * @loopb_dev: Dummy SCIF device used for loopback - * @loopb_wq: Workqueue used for handling loopback messages - * @loopb_wqname[16]: Name of loopback workqueue - * @loopb_work: Used for submitting work to loopb_wq - * @loopb_recv_q: List of messages received on the loopb_wq - * @card_initiated_exit: set when the card has initiated the exit - * @rmalock: Synchronize access to RMA operations - * @fencelock: Synchronize access to list of remote fences requested. - * @rma: List of temporary registered windows to be destroyed. - * @rma_tc: List of temporary registered & cached Windows to be destroyed - * @fence: List of remote fence requests - * @mmu_notif_work: Work for registration caching MMU notifier workqueue - * @mmu_notif_cleanup: List of temporary cached windows for reg cache - * @rma_tc_limit: RMA temporary cache limit - */ -struct scif_info { - u8 nodeid; - u8 maxid; - u8 total; - u32 nr_zombies; - struct mutex eplock; - struct mutex connlock; - spinlock_t nb_connect_lock; - spinlock_t port_lock; - struct list_head uaccept; - struct list_head listen; - struct list_head zombie; - struct list_head connected; - struct list_head disconnected; - struct list_head nb_connect_list; - struct work_struct misc_work; - struct mutex conflock; - u8 en_msg_log; - u8 p2p_enable; - struct miscdevice mdev; - struct work_struct conn_work; - wait_queue_head_t exitwq; - struct scif_dev *loopb_dev; - struct workqueue_struct *loopb_wq; - char loopb_wqname[16]; - struct work_struct loopb_work; - struct list_head loopb_recv_q; - bool card_initiated_exit; - spinlock_t rmalock; - struct mutex fencelock; - struct list_head rma; - struct list_head rma_tc; - struct list_head fence; - struct work_struct mmu_notif_work; - struct list_head mmu_notif_cleanup; - unsigned long rma_tc_limit; -}; - -/* - * struct scif_p2p_info - SCIF mapping information used for P2P - * - * @ppi_peer_id - SCIF peer node id - * @ppi_sg - Scatter list for bar information (One for mmio and one for aper) - * @sg_nentries - Number of entries in the scatterlist - * @ppi_da: DMA address for MMIO and APER bars - * @ppi_len: Length of MMIO and APER bars - * @ppi_list: Link in list of mapping information - */ -struct scif_p2p_info { - u8 ppi_peer_id; - struct scatterlist *ppi_sg[2]; - u64 sg_nentries[2]; - dma_addr_t ppi_da[2]; - u64 ppi_len[2]; -#define SCIF_PPI_MMIO 0 -#define SCIF_PPI_APER 1 - struct list_head ppi_list; -}; - -/* - * struct scif_dev - SCIF remote device specific fields - * - * @node: Node id - * @p2p: List of P2P mapping information - * @qpairs: The node queue pair for exchanging control messages - * @intr_wq: Workqueue for handling Node QP messages - * @intr_wqname: Name of node QP workqueue for handling interrupts - * @intr_bh: Used for submitting work to intr_wq - * @lock: Lock used for synchronizing access to the scif device - * @sdev: SCIF hardware device on the SCIF hardware bus - * @db: doorbell the peer will trigger to generate an interrupt on self - * @rdb: Doorbell to trigger on the peer to generate an interrupt on the peer - * @cookie: Cookie received while registering the interrupt handler - * @peer_add_work: Work for handling device_add for peer devices - * @p2p_dwork: Delayed work to enable polling for P2P state - * @qp_dwork: Delayed work for enabling polling for remote QP information - * @p2p_retry: Number of times to retry polling of P2P state - * @base_addr: P2P aperture bar base address - * @mic_mw mmio: The peer MMIO information used for P2P - * @spdev: SCIF peer device on the SCIF peer bus - * @node_remove_ack_pending: True if a node_remove_ack is pending - * @exit_ack_pending: true if an exit_ack is pending - * @disconn_wq: Used while waiting for a node remove response - * @disconn_rescnt: Keeps track of number of node remove requests sent - * @exit: Status of exit message - * @qp_dma_addr: Queue pair DMA address passed to the peer - * @dma_ch_idx: Round robin index for DMA channels - * @signal_pool: DMA pool used for scheduling scif_fence_signal DMA's -*/ -struct scif_dev { - u8 node; - struct list_head p2p; - struct scif_qp *qpairs; - struct workqueue_struct *intr_wq; - char intr_wqname[16]; - struct work_struct intr_bh; - struct mutex lock; - struct scif_hw_dev *sdev; - int db; - int rdb; - struct mic_irq *cookie; - struct work_struct peer_add_work; - struct delayed_work p2p_dwork; - struct delayed_work qp_dwork; - int p2p_retry; - dma_addr_t base_addr; - struct mic_mw mmio; - struct scif_peer_dev __rcu *spdev; - bool node_remove_ack_pending; - bool exit_ack_pending; - wait_queue_head_t disconn_wq; - atomic_t disconn_rescnt; - enum scif_msg_state exit; - dma_addr_t qp_dma_addr; - int dma_ch_idx; - struct dma_pool *signal_pool; -}; - -extern bool scif_reg_cache_enable; -extern bool scif_ulimit_check; -extern struct scif_info scif_info; -extern struct idr scif_ports; -extern struct bus_type scif_peer_bus; -extern struct scif_dev *scif_dev; -extern const struct file_operations scif_fops; -extern const struct file_operations scif_anon_fops; - -/* Size of the RB for the Node QP */ -#define SCIF_NODE_QP_SIZE 0x10000 - -#include "scif_nodeqp.h" -#include "scif_rma.h" -#include "scif_rma_list.h" - -/* - * scifdev_self: - * @dev: The remote SCIF Device - * - * Returns true if the SCIF Device passed is the self aka Loopback SCIF device. - */ -static inline int scifdev_self(struct scif_dev *dev) -{ - return dev->node == scif_info.nodeid; -} - -static inline bool scif_is_mgmt_node(void) -{ - return !scif_info.nodeid; -} - -/* - * scifdev_is_p2p: - * @dev: The remote SCIF Device - * - * Returns true if the SCIF Device is a MIC Peer to Peer SCIF device. - */ -static inline bool scifdev_is_p2p(struct scif_dev *dev) -{ - if (scif_is_mgmt_node()) - return false; - else - return dev != &scif_dev[SCIF_MGMT_NODE] && - !scifdev_self(dev); -} - -/* - * scifdev_alive: - * @scifdev: The remote SCIF Device - * - * Returns true if the remote SCIF Device is running or sleeping for - * this endpoint. - */ -static inline int _scifdev_alive(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - - rcu_read_lock(); - spdev = rcu_dereference(scifdev->spdev); - rcu_read_unlock(); - return !!spdev; -} - -#include "scif_epd.h" - -void __init scif_init_debugfs(void); -void scif_exit_debugfs(void); -int scif_setup_intr_wq(struct scif_dev *scifdev); -void scif_destroy_intr_wq(struct scif_dev *scifdev); -void scif_cleanup_scifdev(struct scif_dev *dev); -void scif_handle_remove_node(int node); -void scif_disconnect_node(u32 node_id, bool mgmt_initiated); -void scif_free_qp(struct scif_dev *dev); -void scif_misc_handler(struct work_struct *work); -void scif_stop(struct scif_dev *scifdev); -irqreturn_t scif_intr_handler(int irq, void *data); -#endif /* SCIF_MAIN_H */ diff --git a/drivers/misc/mic/scif/scif_map.h b/drivers/misc/mic/scif/scif_map.h deleted file mode 100644 index 96b760819bfc..000000000000 --- a/drivers/misc/mic/scif/scif_map.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_MAP_H -#define SCIF_MAP_H - -#include "../bus/scif_bus.h" - -static __always_inline void * -scif_alloc_coherent(dma_addr_t *dma_handle, - struct scif_dev *scifdev, size_t size, - gfp_t gfp) -{ - void *va; - - if (scifdev_self(scifdev)) { - va = kmalloc(size, gfp); - if (va) - *dma_handle = virt_to_phys(va); - } else { - va = dma_alloc_coherent(&scifdev->sdev->dev, - size, dma_handle, gfp); - if (va && scifdev_is_p2p(scifdev)) - *dma_handle = *dma_handle + scifdev->base_addr; - } - return va; -} - -static __always_inline void -scif_free_coherent(void *va, dma_addr_t local, - struct scif_dev *scifdev, size_t size) -{ - if (scifdev_self(scifdev)) { - kfree(va); - } else { - if (scifdev_is_p2p(scifdev) && local > scifdev->base_addr) - local = local - scifdev->base_addr; - dma_free_coherent(&scifdev->sdev->dev, - size, va, local); - } -} - -static __always_inline int -scif_map_single(dma_addr_t *dma_handle, - void *local, struct scif_dev *scifdev, size_t size) -{ - int err = 0; - - if (scifdev_self(scifdev)) { - *dma_handle = virt_to_phys((local)); - } else { - *dma_handle = dma_map_single(&scifdev->sdev->dev, - local, size, DMA_BIDIRECTIONAL); - if (dma_mapping_error(&scifdev->sdev->dev, *dma_handle)) - err = -ENOMEM; - else if (scifdev_is_p2p(scifdev)) - *dma_handle = *dma_handle + scifdev->base_addr; - } - if (err) - *dma_handle = 0; - return err; -} - -static __always_inline void -scif_unmap_single(dma_addr_t local, struct scif_dev *scifdev, - size_t size) -{ - if (!scifdev_self(scifdev)) { - if (scifdev_is_p2p(scifdev)) - local = local - scifdev->base_addr; - dma_unmap_single(&scifdev->sdev->dev, local, - size, DMA_BIDIRECTIONAL); - } -} - -static __always_inline void * -scif_ioremap(dma_addr_t phys, size_t size, struct scif_dev *scifdev) -{ - void *out_virt; - struct scif_hw_dev *sdev = scifdev->sdev; - - if (scifdev_self(scifdev)) - out_virt = phys_to_virt(phys); - else - out_virt = (void __force *) - sdev->hw_ops->remap(sdev, phys, size); - return out_virt; -} - -static __always_inline void -scif_iounmap(void *virt, size_t len, struct scif_dev *scifdev) -{ - if (!scifdev_self(scifdev)) { - struct scif_hw_dev *sdev = scifdev->sdev; - - sdev->hw_ops->unmap(sdev, (void __force __iomem *)virt); - } -} - -static __always_inline int -scif_map_page(dma_addr_t *dma_handle, struct page *page, - struct scif_dev *scifdev) -{ - int err = 0; - - if (scifdev_self(scifdev)) { - *dma_handle = page_to_phys(page); - } else { - struct scif_hw_dev *sdev = scifdev->sdev; - *dma_handle = dma_map_page(&sdev->dev, - page, 0x0, PAGE_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&sdev->dev, *dma_handle)) - err = -ENOMEM; - else if (scifdev_is_p2p(scifdev)) - *dma_handle = *dma_handle + scifdev->base_addr; - } - if (err) - *dma_handle = 0; - return err; -} -#endif /* SCIF_MAP_H */ diff --git a/drivers/misc/mic/scif/scif_mmap.c b/drivers/misc/mic/scif/scif_mmap.c deleted file mode 100644 index a151d416f39c..000000000000 --- a/drivers/misc/mic/scif/scif_mmap.c +++ /dev/null @@ -1,690 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" - -/* - * struct scif_vma_info - Information about a remote memory mapping - * created via scif_mmap(..) - * @vma: VM area struct - * @list: link to list of active vmas - */ -struct scif_vma_info { - struct vm_area_struct *vma; - struct list_head list; -}; - -void scif_recv_munmap(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_rma_req req; - struct scif_window *window = NULL; - struct scif_window *recv_window = - (struct scif_window *)msg->payload[0]; - struct scif_endpt *ep; - - ep = (struct scif_endpt *)recv_window->ep; - req.out_window = &window; - req.offset = recv_window->offset; - req.prot = recv_window->prot; - req.nr_bytes = recv_window->nr_pages << PAGE_SHIFT; - req.type = SCIF_WINDOW_FULL; - req.head = &ep->rma_info.reg_list; - msg->payload[0] = ep->remote_ep; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - if (scif_query_window(&req)) { - dev_err(&scifdev->sdev->dev, - "%s %d -ENXIO\n", __func__, __LINE__); - msg->uop = SCIF_UNREGISTER_ACK; - goto error; - } - - scif_put_window(window, window->nr_pages); - - if (!window->ref_count) { - atomic_inc(&ep->rma_info.tw_refcount); - ep->rma_info.async_list_del = 1; - list_del_init(&window->list); - scif_free_window_offset(ep, window, window->offset); - } -error: - mutex_unlock(&ep->rma_info.rma_lock); - if (window && !window->ref_count) - scif_queue_for_cleanup(window, &scif_info.rma); -} - -/* - * Remove valid remote memory mappings created via scif_mmap(..) from the - * process address space since the remote node is lost - */ -static void __scif_zap_mmaps(struct scif_endpt *ep) -{ - struct list_head *item; - struct scif_vma_info *info; - struct vm_area_struct *vma; - unsigned long size; - - spin_lock(&ep->lock); - list_for_each(item, &ep->rma_info.vma_list) { - info = list_entry(item, struct scif_vma_info, list); - vma = info->vma; - size = vma->vm_end - vma->vm_start; - zap_vma_ptes(vma, vma->vm_start, size); - dev_dbg(scif_info.mdev.this_device, - "%s ep %p zap vma %p size 0x%lx\n", - __func__, ep, info->vma, size); - } - spin_unlock(&ep->lock); -} - -/* - * Traverse the list of endpoints for a particular remote node and - * zap valid remote memory mappings since the remote node is lost - */ -static void _scif_zap_mmaps(int node, struct list_head *head) -{ - struct scif_endpt *ep; - struct list_head *item; - - mutex_lock(&scif_info.connlock); - list_for_each(item, head) { - ep = list_entry(item, struct scif_endpt, list); - if (ep->remote_dev->node == node) - __scif_zap_mmaps(ep); - } - mutex_unlock(&scif_info.connlock); -} - -/* - * Wrapper for removing remote memory mappings for a particular node. This API - * is called by peer nodes as part of handling a lost node. - */ -void scif_zap_mmaps(int node) -{ - _scif_zap_mmaps(node, &scif_info.connected); - _scif_zap_mmaps(node, &scif_info.disconnected); -} - -/* - * This API is only called while handling a lost node: - * a) Remote node is dead. - * b) Remote memory mappings have been zapped - * So we can traverse the remote_reg_list without any locks. Since - * the window has not yet been unregistered we can drop the ref count - * and queue it to the cleanup thread. - */ -static void __scif_cleanup_rma_for_zombies(struct scif_endpt *ep) -{ - struct list_head *pos, *tmp; - struct scif_window *window; - - list_for_each_safe(pos, tmp, &ep->rma_info.remote_reg_list) { - window = list_entry(pos, struct scif_window, list); - if (window->ref_count) - scif_put_window(window, window->nr_pages); - else - dev_err(scif_info.mdev.this_device, - "%s %d unexpected\n", - __func__, __LINE__); - if (!window->ref_count) { - atomic_inc(&ep->rma_info.tw_refcount); - list_del_init(&window->list); - scif_queue_for_cleanup(window, &scif_info.rma); - } - } -} - -/* Cleanup remote registration lists for zombie endpoints */ -void scif_cleanup_rma_for_zombies(int node) -{ - struct scif_endpt *ep; - struct list_head *item; - - mutex_lock(&scif_info.eplock); - list_for_each(item, &scif_info.zombie) { - ep = list_entry(item, struct scif_endpt, list); - if (ep->remote_dev && ep->remote_dev->node == node) - __scif_cleanup_rma_for_zombies(ep); - } - mutex_unlock(&scif_info.eplock); - flush_work(&scif_info.misc_work); -} - -/* Insert the VMA into the per endpoint VMA list */ -static int scif_insert_vma(struct scif_endpt *ep, struct vm_area_struct *vma) -{ - struct scif_vma_info *info; - int err = 0; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto done; - } - info->vma = vma; - spin_lock(&ep->lock); - list_add_tail(&info->list, &ep->rma_info.vma_list); - spin_unlock(&ep->lock); -done: - return err; -} - -/* Delete the VMA from the per endpoint VMA list */ -static void scif_delete_vma(struct scif_endpt *ep, struct vm_area_struct *vma) -{ - struct list_head *item; - struct scif_vma_info *info; - - spin_lock(&ep->lock); - list_for_each(item, &ep->rma_info.vma_list) { - info = list_entry(item, struct scif_vma_info, list); - if (info->vma == vma) { - list_del(&info->list); - kfree(info); - break; - } - } - spin_unlock(&ep->lock); -} - -static phys_addr_t scif_get_phys(phys_addr_t phys, struct scif_endpt *ep) -{ - struct scif_dev *scifdev = (struct scif_dev *)ep->remote_dev; - struct scif_hw_dev *sdev = scifdev->sdev; - phys_addr_t out_phys, apt_base = 0; - - /* - * If the DMA address is card relative then we need to add the - * aperture base for mmap to work correctly - */ - if (!scifdev_self(scifdev) && sdev->aper && sdev->card_rel_da) - apt_base = sdev->aper->pa; - out_phys = apt_base + phys; - return out_phys; -} - -int scif_get_pages(scif_epd_t epd, off_t offset, size_t len, - struct scif_range **pages) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_rma_req req; - struct scif_window *window = NULL; - int nr_pages, err, i; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI get_pinned_pages: ep %p offset 0x%lx len 0x%lx\n", - ep, offset, len); - err = scif_verify_epd(ep); - if (err) - return err; - - if (!len || (offset < 0) || - (offset + len < offset) || - (ALIGN(offset, PAGE_SIZE) != offset) || - (ALIGN(len, PAGE_SIZE) != len)) - return -EINVAL; - - nr_pages = len >> PAGE_SHIFT; - - req.out_window = &window; - req.offset = offset; - req.prot = 0; - req.nr_bytes = len; - req.type = SCIF_WINDOW_SINGLE; - req.head = &ep->rma_info.remote_reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error; - } - - /* Allocate scif_range */ - *pages = kzalloc(sizeof(**pages), GFP_KERNEL); - if (!*pages) { - err = -ENOMEM; - goto error; - } - - /* Allocate phys addr array */ - (*pages)->phys_addr = scif_zalloc(nr_pages * sizeof(dma_addr_t)); - if (!((*pages)->phys_addr)) { - err = -ENOMEM; - goto error; - } - - if (scif_is_mgmt_node() && !scifdev_self(ep->remote_dev)) { - /* Allocate virtual address array */ - ((*pages)->va = scif_zalloc(nr_pages * sizeof(void *))); - if (!(*pages)->va) { - err = -ENOMEM; - goto error; - } - } - /* Populate the values */ - (*pages)->cookie = window; - (*pages)->nr_pages = nr_pages; - (*pages)->prot_flags = window->prot; - - for (i = 0; i < nr_pages; i++) { - (*pages)->phys_addr[i] = - __scif_off_to_dma_addr(window, offset + - (i * PAGE_SIZE)); - (*pages)->phys_addr[i] = scif_get_phys((*pages)->phys_addr[i], - ep); - if (scif_is_mgmt_node() && !scifdev_self(ep->remote_dev)) - (*pages)->va[i] = - ep->remote_dev->sdev->aper->va + - (*pages)->phys_addr[i] - - ep->remote_dev->sdev->aper->pa; - } - - scif_get_window(window, nr_pages); -error: - mutex_unlock(&ep->rma_info.rma_lock); - if (err) { - if (*pages) { - scif_free((*pages)->phys_addr, - nr_pages * sizeof(dma_addr_t)); - scif_free((*pages)->va, - nr_pages * sizeof(void *)); - kfree(*pages); - *pages = NULL; - } - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - } - return err; -} -EXPORT_SYMBOL_GPL(scif_get_pages); - -int scif_put_pages(struct scif_range *pages) -{ - struct scif_endpt *ep; - struct scif_window *window; - struct scifmsg msg; - - if (!pages || !pages->cookie) - return -EINVAL; - - window = pages->cookie; - - if (!window || window->magic != SCIFEP_MAGIC) - return -EINVAL; - - ep = (struct scif_endpt *)window->ep; - /* - * If the state is SCIFEP_CONNECTED or SCIFEP_DISCONNECTED then the - * callee should be allowed to release references to the pages, - * else the endpoint was not connected in the first place, - * hence the ENOTCONN. - */ - if (ep->state != SCIFEP_CONNECTED && ep->state != SCIFEP_DISCONNECTED) - return -ENOTCONN; - - mutex_lock(&ep->rma_info.rma_lock); - - scif_put_window(window, pages->nr_pages); - - /* Initiate window destruction if ref count is zero */ - if (!window->ref_count) { - list_del(&window->list); - mutex_unlock(&ep->rma_info.rma_lock); - scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan); - /* Inform the peer about this window being destroyed. */ - msg.uop = SCIF_MUNMAP; - msg.src = ep->port; - msg.payload[0] = window->peer_window; - /* No error handling for notification messages */ - scif_nodeqp_send(ep->remote_dev, &msg); - /* Destroy this window from the peer's registered AS */ - scif_destroy_remote_window(window); - } else { - mutex_unlock(&ep->rma_info.rma_lock); - } - - scif_free(pages->phys_addr, pages->nr_pages * sizeof(dma_addr_t)); - scif_free(pages->va, pages->nr_pages * sizeof(void *)); - kfree(pages); - return 0; -} -EXPORT_SYMBOL_GPL(scif_put_pages); - -/* - * scif_rma_list_mmap: - * - * Traverse the remote registration list starting from start_window: - * 1) Create VtoP mappings via remap_pfn_range(..) - * 2) Once step 1) and 2) complete successfully then traverse the range of - * windows again and bump the reference count. - * RMA lock must be held. - */ -static int scif_rma_list_mmap(struct scif_window *start_window, s64 offset, - int nr_pages, struct vm_area_struct *vma) -{ - s64 end_offset, loop_offset = offset; - struct scif_window *window = start_window; - int loop_nr_pages, nr_pages_left = nr_pages; - struct scif_endpt *ep = (struct scif_endpt *)start_window->ep; - struct list_head *head = &ep->rma_info.remote_reg_list; - int i, err = 0; - dma_addr_t phys_addr; - struct scif_window_iter src_win_iter; - size_t contig_bytes = 0; - - might_sleep(); - list_for_each_entry_from(window, head, list) { - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min_t(int, - (end_offset - loop_offset) >> PAGE_SHIFT, - nr_pages_left); - scif_init_window_iter(window, &src_win_iter); - for (i = 0; i < loop_nr_pages; i++) { - phys_addr = scif_off_to_dma_addr(window, loop_offset, - &contig_bytes, - &src_win_iter); - phys_addr = scif_get_phys(phys_addr, ep); - err = remap_pfn_range(vma, - vma->vm_start + - loop_offset - offset, - phys_addr >> PAGE_SHIFT, - PAGE_SIZE, - vma->vm_page_prot); - if (err) - goto error; - loop_offset += PAGE_SIZE; - } - nr_pages_left -= loop_nr_pages; - if (!nr_pages_left) - break; - } - /* - * No more failures expected. Bump up the ref count for all - * the windows. Another traversal from start_window required - * for handling errors encountered across windows during - * remap_pfn_range(..). - */ - loop_offset = offset; - nr_pages_left = nr_pages; - window = start_window; - head = &ep->rma_info.remote_reg_list; - list_for_each_entry_from(window, head, list) { - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min_t(int, - (end_offset - loop_offset) >> PAGE_SHIFT, - nr_pages_left); - scif_get_window(window, loop_nr_pages); - nr_pages_left -= loop_nr_pages; - loop_offset += (loop_nr_pages << PAGE_SHIFT); - if (!nr_pages_left) - break; - } -error: - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} - -/* - * scif_rma_list_munmap: - * - * Traverse the remote registration list starting from window: - * 1) Decrement ref count. - * 2) If the ref count drops to zero then send a SCIF_MUNMAP message to peer. - * RMA lock must be held. - */ -static void scif_rma_list_munmap(struct scif_window *start_window, - s64 offset, int nr_pages) -{ - struct scifmsg msg; - s64 loop_offset = offset, end_offset; - int loop_nr_pages, nr_pages_left = nr_pages; - struct scif_endpt *ep = (struct scif_endpt *)start_window->ep; - struct list_head *head = &ep->rma_info.remote_reg_list; - struct scif_window *window = start_window, *_window; - - msg.uop = SCIF_MUNMAP; - msg.src = ep->port; - loop_offset = offset; - nr_pages_left = nr_pages; - list_for_each_entry_safe_from(window, _window, head, list) { - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min_t(int, - (end_offset - loop_offset) >> PAGE_SHIFT, - nr_pages_left); - scif_put_window(window, loop_nr_pages); - if (!window->ref_count) { - struct scif_dev *rdev = ep->remote_dev; - - scif_drain_dma_intr(rdev->sdev, - ep->rma_info.dma_chan); - /* Inform the peer about this munmap */ - msg.payload[0] = window->peer_window; - /* No error handling for Notification messages. */ - scif_nodeqp_send(ep->remote_dev, &msg); - list_del(&window->list); - /* Destroy this window from the peer's registered AS */ - scif_destroy_remote_window(window); - } - nr_pages_left -= loop_nr_pages; - loop_offset += (loop_nr_pages << PAGE_SHIFT); - if (!nr_pages_left) - break; - } -} - -/* - * The private data field of each VMA used to mmap a remote window - * points to an instance of struct vma_pvt - */ -struct vma_pvt { - struct scif_endpt *ep; /* End point for remote window */ - s64 offset; /* offset within remote window */ - bool valid_offset; /* offset is valid only if the original - * mmap request was for a single page - * else the offset within the vma is - * the correct offset - */ - struct kref ref; -}; - -static void vma_pvt_release(struct kref *ref) -{ - struct vma_pvt *vmapvt = container_of(ref, struct vma_pvt, ref); - - kfree(vmapvt); -} - -/** - * scif_vma_open - VMA open driver callback - * @vma: VMM memory area. - * The open method is called by the kernel to allow the subsystem implementing - * the VMA to initialize the area. This method is invoked any time a new - * reference to the VMA is made (when a process forks, for example). - * The one exception happens when the VMA is first created by mmap; - * in this case, the driver's mmap method is called instead. - * This function is also invoked when an existing VMA is split by the kernel - * due to a call to munmap on a subset of the VMA resulting in two VMAs. - * The kernel invokes this function only on one of the two VMAs. - */ -static void scif_vma_open(struct vm_area_struct *vma) -{ - struct vma_pvt *vmapvt = vma->vm_private_data; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI vma open: vma_start 0x%lx vma_end 0x%lx\n", - vma->vm_start, vma->vm_end); - scif_insert_vma(vmapvt->ep, vma); - kref_get(&vmapvt->ref); -} - -/** - * scif_munmap - VMA close driver callback. - * @vma: VMM memory area. - * When an area is destroyed, the kernel calls its close operation. - * Note that there's no usage count associated with VMA's; the area - * is opened and closed exactly once by each process that uses it. - */ -static void scif_munmap(struct vm_area_struct *vma) -{ - struct scif_endpt *ep; - struct vma_pvt *vmapvt = vma->vm_private_data; - int nr_pages = vma_pages(vma); - s64 offset; - struct scif_rma_req req; - struct scif_window *window = NULL; - int err; - - might_sleep(); - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI munmap: vma_start 0x%lx vma_end 0x%lx\n", - vma->vm_start, vma->vm_end); - ep = vmapvt->ep; - offset = vmapvt->valid_offset ? vmapvt->offset : - (vma->vm_pgoff) << PAGE_SHIFT; - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI munmap: ep %p nr_pages 0x%x offset 0x%llx\n", - ep, nr_pages, offset); - req.out_window = &window; - req.offset = offset; - req.nr_bytes = vma->vm_end - vma->vm_start; - req.prot = vma->vm_flags & (VM_READ | VM_WRITE); - req.type = SCIF_WINDOW_PARTIAL; - req.head = &ep->rma_info.remote_reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - - err = scif_query_window(&req); - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - else - scif_rma_list_munmap(window, offset, nr_pages); - - mutex_unlock(&ep->rma_info.rma_lock); - /* - * The kernel probably zeroes these out but we still want - * to clean up our own mess just in case. - */ - vma->vm_ops = NULL; - vma->vm_private_data = NULL; - kref_put(&vmapvt->ref, vma_pvt_release); - scif_delete_vma(ep, vma); -} - -static const struct vm_operations_struct scif_vm_ops = { - .open = scif_vma_open, - .close = scif_munmap, -}; - -/** - * scif_mmap - Map pages in virtual address space to a remote window. - * @vma: VMM memory area. - * @epd: endpoint descriptor - * - * Return: Upon successful completion, scif_mmap() returns zero - * else an apt error is returned as documented in scif.h - */ -int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd) -{ - struct scif_rma_req req; - struct scif_window *window = NULL; - struct scif_endpt *ep = (struct scif_endpt *)epd; - s64 start_offset = vma->vm_pgoff << PAGE_SHIFT; - int nr_pages = vma_pages(vma); - int err; - struct vma_pvt *vmapvt; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI mmap: ep %p start_offset 0x%llx nr_pages 0x%x\n", - ep, start_offset, nr_pages); - err = scif_verify_epd(ep); - if (err) - return err; - - might_sleep(); - - err = scif_insert_vma(ep, vma); - if (err) - return err; - - vmapvt = kzalloc(sizeof(*vmapvt), GFP_KERNEL); - if (!vmapvt) { - scif_delete_vma(ep, vma); - return -ENOMEM; - } - - vmapvt->ep = ep; - kref_init(&vmapvt->ref); - - req.out_window = &window; - req.offset = start_offset; - req.nr_bytes = vma->vm_end - vma->vm_start; - req.prot = vma->vm_flags & (VM_READ | VM_WRITE); - req.type = SCIF_WINDOW_PARTIAL; - req.head = &ep->rma_info.remote_reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unlock; - } - - /* Default prot for loopback */ - if (!scifdev_self(ep->remote_dev)) - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - - /* - * VM_DONTCOPY - Do not copy this vma on fork - * VM_DONTEXPAND - Cannot expand with mremap() - * VM_RESERVED - Count as reserved_vm like IO - * VM_PFNMAP - Page-ranges managed without "struct page" - * VM_IO - Memory mapped I/O or similar - * - * We do not want to copy this VMA automatically on a fork(), - * expand this VMA due to mremap() or swap out these pages since - * the VMA is actually backed by physical pages in the remote - * node's physical memory and not via a struct page. - */ - vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP; - - if (!scifdev_self(ep->remote_dev)) - vma->vm_flags |= VM_IO | VM_PFNMAP; - - /* Map this range of windows */ - err = scif_rma_list_mmap(window, start_offset, nr_pages, vma); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unlock; - } - /* Set up the driver call back */ - vma->vm_ops = &scif_vm_ops; - vma->vm_private_data = vmapvt; -error_unlock: - mutex_unlock(&ep->rma_info.rma_lock); - if (err) { - kfree(vmapvt); - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - scif_delete_vma(ep, vma); - } - return err; -} diff --git a/drivers/misc/mic/scif/scif_nm.c b/drivers/misc/mic/scif/scif_nm.c deleted file mode 100644 index c4d9422082b7..000000000000 --- a/drivers/misc/mic/scif/scif_nm.c +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_peer_bus.h" - -#include "scif_main.h" -#include "scif_map.h" - -/** - * scif_invalidate_ep() - Set state for all connected endpoints - * to disconnected and wake up all send/recv waitqueues - * - * @node: Node to invalidate - */ -static void scif_invalidate_ep(int node) -{ - struct scif_endpt *ep; - struct list_head *pos, *tmpq; - - flush_work(&scif_info.conn_work); - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - ep = list_entry(pos, struct scif_endpt, list); - if (ep->remote_dev->node == node) { - scif_unmap_all_windows(ep); - spin_lock(&ep->lock); - scif_cleanup_ep_qp(ep); - spin_unlock(&ep->lock); - } - } - list_for_each_safe(pos, tmpq, &scif_info.connected) { - ep = list_entry(pos, struct scif_endpt, list); - if (ep->remote_dev->node == node) { - list_del(pos); - spin_lock(&ep->lock); - ep->state = SCIFEP_DISCONNECTED; - list_add_tail(&ep->list, &scif_info.disconnected); - scif_cleanup_ep_qp(ep); - wake_up_interruptible(&ep->sendwq); - wake_up_interruptible(&ep->recvwq); - spin_unlock(&ep->lock); - scif_unmap_all_windows(ep); - } - } - mutex_unlock(&scif_info.connlock); -} - -void scif_free_qp(struct scif_dev *scifdev) -{ - struct scif_qp *qp = scifdev->qpairs; - - if (!qp) - return; - scif_unmap_single(qp->local_buf, scifdev, qp->inbound_q.size); - kfree(qp->inbound_q.rb_base); - scif_unmap_single(qp->local_qp, scifdev, sizeof(struct scif_qp)); - kfree(scifdev->qpairs); - scifdev->qpairs = NULL; -} - -static void scif_cleanup_qp(struct scif_dev *dev) -{ - struct scif_qp *qp = &dev->qpairs[0]; - - if (!qp) - return; - scif_iounmap((void *)qp->remote_qp, sizeof(struct scif_qp), dev); - scif_iounmap((void *)qp->outbound_q.rb_base, - sizeof(struct scif_qp), dev); - qp->remote_qp = NULL; - qp->local_write = 0; - qp->inbound_q.current_write_offset = 0; - qp->inbound_q.current_read_offset = 0; - if (scifdev_is_p2p(dev)) - scif_free_qp(dev); -} - -void scif_send_acks(struct scif_dev *dev) -{ - struct scifmsg msg; - - if (dev->node_remove_ack_pending) { - msg.uop = SCIF_NODE_REMOVE_ACK; - msg.src.node = scif_info.nodeid; - msg.dst.node = SCIF_MGMT_NODE; - msg.payload[0] = dev->node; - scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg); - dev->node_remove_ack_pending = false; - } - if (dev->exit_ack_pending) { - msg.uop = SCIF_EXIT_ACK; - msg.src.node = scif_info.nodeid; - msg.dst.node = dev->node; - scif_nodeqp_send(dev, &msg); - dev->exit_ack_pending = false; - } -} - -/** - * scif_cleanup_scifdev - Uninitialize SCIF data structures for remote - * SCIF device. - * @dev: Remote SCIF device. - */ -void scif_cleanup_scifdev(struct scif_dev *dev) -{ - struct scif_hw_dev *sdev = dev->sdev; - - if (!dev->sdev) - return; - if (scifdev_is_p2p(dev)) { - if (dev->cookie) { - sdev->hw_ops->free_irq(sdev, dev->cookie, dev); - dev->cookie = NULL; - } - scif_destroy_intr_wq(dev); - } - flush_work(&scif_info.misc_work); - scif_destroy_p2p(dev); - scif_invalidate_ep(dev->node); - scif_zap_mmaps(dev->node); - scif_cleanup_rma_for_zombies(dev->node); - flush_work(&scif_info.misc_work); - scif_send_acks(dev); - if (!dev->node && scif_info.card_initiated_exit) { - /* - * Send an SCIF_EXIT message which is the last message from MIC - * to the Host and wait for a SCIF_EXIT_ACK - */ - scif_send_exit(dev); - scif_info.card_initiated_exit = false; - } - scif_cleanup_qp(dev); -} - -/** - * scif_remove_node - * - * @node: Node to remove - */ -void scif_handle_remove_node(int node) -{ - struct scif_dev *scifdev = &scif_dev[node]; - - if (scif_peer_unregister_device(scifdev)) - scif_send_acks(scifdev); -} - -static int scif_send_rmnode_msg(int node, int remove_node) -{ - struct scifmsg notif_msg; - struct scif_dev *dev = &scif_dev[node]; - - notif_msg.uop = SCIF_NODE_REMOVE; - notif_msg.src.node = scif_info.nodeid; - notif_msg.dst.node = node; - notif_msg.payload[0] = remove_node; - return scif_nodeqp_send(dev, ¬if_msg); -} - -/** - * scif_node_disconnect - * - * @node_id: source node id [in] - * @mgmt_initiated: Disconnection initiated from the mgmt node - * - * Disconnect a node from the scif network. - */ -void scif_disconnect_node(u32 node_id, bool mgmt_initiated) -{ - int ret; - int msg_cnt = 0; - u32 i = 0; - struct scif_dev *scifdev = &scif_dev[node_id]; - - if (!node_id) - return; - - atomic_set(&scifdev->disconn_rescnt, 0); - - /* Destroy p2p network */ - for (i = 1; i <= scif_info.maxid; i++) { - if (i == node_id) - continue; - ret = scif_send_rmnode_msg(i, node_id); - if (!ret) - msg_cnt++; - } - /* Wait for the remote nodes to respond with SCIF_NODE_REMOVE_ACK */ - ret = wait_event_timeout(scifdev->disconn_wq, - (atomic_read(&scifdev->disconn_rescnt) - == msg_cnt), SCIF_NODE_ALIVE_TIMEOUT); - /* Tell the card to clean up */ - if (mgmt_initiated && _scifdev_alive(scifdev)) - /* - * Send an SCIF_EXIT message which is the last message from Host - * to the MIC and wait for a SCIF_EXIT_ACK - */ - scif_send_exit(scifdev); - atomic_set(&scifdev->disconn_rescnt, 0); - /* Tell the mgmt node to clean up */ - ret = scif_send_rmnode_msg(SCIF_MGMT_NODE, node_id); - if (!ret) - /* Wait for mgmt node to respond with SCIF_NODE_REMOVE_ACK */ - wait_event_timeout(scifdev->disconn_wq, - (atomic_read(&scifdev->disconn_rescnt) == 1), - SCIF_NODE_ALIVE_TIMEOUT); -} - -void scif_get_node_info(void) -{ - struct scifmsg msg; - DECLARE_COMPLETION_ONSTACK(node_info); - - msg.uop = SCIF_GET_NODE_INFO; - msg.src.node = scif_info.nodeid; - msg.dst.node = SCIF_MGMT_NODE; - msg.payload[3] = (u64)&node_info; - - if ((scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg))) - return; - - /* Wait for a response with SCIF_GET_NODE_INFO */ - wait_for_completion(&node_info); -} diff --git a/drivers/misc/mic/scif/scif_nodeqp.c b/drivers/misc/mic/scif/scif_nodeqp.c deleted file mode 100644 index 384ce08fa98a..000000000000 --- a/drivers/misc/mic/scif/scif_nodeqp.c +++ /dev/null @@ -1,1349 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "../bus/scif_bus.h" -#include "scif_peer_bus.h" -#include "scif_main.h" -#include "scif_nodeqp.h" -#include "scif_map.h" - -/* - ************************************************************************ - * SCIF node Queue Pair (QP) setup flow: - * - * 1) SCIF driver gets probed with a scif_hw_dev via the scif_hw_bus - * 2) scif_setup_qp(..) allocates the local qp and calls - * scif_setup_qp_connect(..) which allocates and maps the local - * buffer for the inbound QP - * 3) The local node updates the device page with the DMA address of the QP - * 4) A delayed work is scheduled (qp_dwork) which periodically reads if - * the peer node has updated its QP DMA address - * 5) Once a valid non zero address is found in the QP DMA address field - * in the device page, the local node maps the remote node's QP, - * updates its outbound QP and sends a SCIF_INIT message to the peer - * 6) The SCIF_INIT message is received by the peer node QP interrupt bottom - * half handler by calling scif_init(..) - * 7) scif_init(..) registers a new SCIF peer node by calling - * scif_peer_register_device(..) which signifies the addition of a new - * SCIF node - * 8) On the mgmt node, P2P network setup/teardown is initiated if all the - * remote nodes are online via scif_p2p_setup(..) - * 9) For P2P setup, the host maps the remote nodes' aperture and memory - * bars and sends a SCIF_NODE_ADD message to both nodes - * 10) As part of scif_nodeadd, both nodes set up their local inbound - * QPs and send a SCIF_NODE_ADD_ACK to the mgmt node - * 11) As part of scif_node_add_ack(..) the mgmt node forwards the - * SCIF_NODE_ADD_ACK to the remote nodes - * 12) As part of scif_node_add_ack(..) the remote nodes update their - * outbound QPs, make sure they can access memory on the remote node - * and then add a new SCIF peer node by calling - * scif_peer_register_device(..) which signifies the addition of a new - * SCIF node. - * 13) The SCIF network is now established across all nodes. - * - ************************************************************************ - * SCIF node QP teardown flow (initiated by non mgmt node): - * - * 1) SCIF driver gets a remove callback with a scif_hw_dev via the scif_hw_bus - * 2) The device page QP DMA address field is updated with 0x0 - * 3) A non mgmt node now cleans up all local data structures and sends a - * SCIF_EXIT message to the peer and waits for a SCIF_EXIT_ACK - * 4) As part of scif_exit(..) handling scif_disconnect_node(..) is called - * 5) scif_disconnect_node(..) sends a SCIF_NODE_REMOVE message to all the - * peers and waits for a SCIF_NODE_REMOVE_ACK - * 6) As part of scif_node_remove(..) a remote node unregisters the peer - * node from the SCIF network and sends a SCIF_NODE_REMOVE_ACK - * 7) When the mgmt node has received all the SCIF_NODE_REMOVE_ACKs - * it sends itself a node remove message whose handling cleans up local - * data structures and unregisters the peer node from the SCIF network - * 8) The mgmt node sends a SCIF_EXIT_ACK - * 9) Upon receipt of the SCIF_EXIT_ACK the node initiating the teardown - * completes the SCIF remove routine - * 10) The SCIF network is now torn down for the node initiating the - * teardown sequence - * - ************************************************************************ - * SCIF node QP teardown flow (initiated by mgmt node): - * - * 1) SCIF driver gets a remove callback with a scif_hw_dev via the scif_hw_bus - * 2) The device page QP DMA address field is updated with 0x0 - * 3) The mgmt node calls scif_disconnect_node(..) - * 4) scif_disconnect_node(..) sends a SCIF_NODE_REMOVE message to all the peers - * and waits for a SCIF_NODE_REMOVE_ACK - * 5) As part of scif_node_remove(..) a remote node unregisters the peer - * node from the SCIF network and sends a SCIF_NODE_REMOVE_ACK - * 6) When the mgmt node has received all the SCIF_NODE_REMOVE_ACKs - * it unregisters the peer node from the SCIF network - * 7) The mgmt node sends a SCIF_EXIT message and waits for a SCIF_EXIT_ACK. - * 8) A non mgmt node upon receipt of a SCIF_EXIT message calls scif_stop(..) - * which would clean up local data structures for all SCIF nodes and - * then send a SCIF_EXIT_ACK back to the mgmt node - * 9) Upon receipt of the SCIF_EXIT_ACK the the mgmt node sends itself a node - * remove message whose handling cleans up local data structures and - * destroys any P2P mappings. - * 10) The SCIF hardware device for which a remove callback was received is now - * disconnected from the SCIF network. - */ -/* - * Initializes "local" data structures for the QP. Allocates the QP - * ring buffer (rb) and initializes the "in bound" queue. - */ -int scif_setup_qp_connect(struct scif_qp *qp, dma_addr_t *qp_offset, - int local_size, struct scif_dev *scifdev) -{ - void *local_q = qp->inbound_q.rb_base; - int err = 0; - u32 tmp_rd = 0; - - spin_lock_init(&qp->send_lock); - spin_lock_init(&qp->recv_lock); - - /* Allocate rb only if not already allocated */ - if (!local_q) { - local_q = kzalloc(local_size, GFP_KERNEL); - if (!local_q) { - err = -ENOMEM; - return err; - } - } - - err = scif_map_single(&qp->local_buf, local_q, scifdev, local_size); - if (err) - goto kfree; - /* - * To setup the inbound_q, the buffer lives locally, the read pointer - * is remote and the write pointer is local. - */ - scif_rb_init(&qp->inbound_q, - &tmp_rd, - &qp->local_write, - local_q, get_count_order(local_size)); - /* - * The read pointer is NULL initially and it is unsafe to use the ring - * buffer til this changes! - */ - qp->inbound_q.read_ptr = NULL; - err = scif_map_single(qp_offset, qp, - scifdev, sizeof(struct scif_qp)); - if (err) - goto unmap; - qp->local_qp = *qp_offset; - return err; -unmap: - scif_unmap_single(qp->local_buf, scifdev, local_size); - qp->local_buf = 0; -kfree: - kfree(local_q); - return err; -} - -/* When the other side has already done it's allocation, this is called */ -int scif_setup_qp_accept(struct scif_qp *qp, dma_addr_t *qp_offset, - dma_addr_t phys, int local_size, - struct scif_dev *scifdev) -{ - void *local_q; - void *remote_q; - struct scif_qp *remote_qp; - int remote_size; - int err = 0; - - spin_lock_init(&qp->send_lock); - spin_lock_init(&qp->recv_lock); - /* Start by figuring out where we need to point */ - remote_qp = scif_ioremap(phys, sizeof(struct scif_qp), scifdev); - if (!remote_qp) - return -EIO; - qp->remote_qp = remote_qp; - if (qp->remote_qp->magic != SCIFEP_MAGIC) { - err = -EIO; - goto iounmap; - } - qp->remote_buf = remote_qp->local_buf; - remote_size = qp->remote_qp->inbound_q.size; - remote_q = scif_ioremap(qp->remote_buf, remote_size, scifdev); - if (!remote_q) { - err = -EIO; - goto iounmap; - } - qp->remote_qp->local_write = 0; - /* - * To setup the outbound_q, the buffer lives in remote memory, - * the read pointer is local, the write pointer is remote - */ - scif_rb_init(&qp->outbound_q, - &qp->local_read, - &qp->remote_qp->local_write, - remote_q, - get_count_order(remote_size)); - local_q = kzalloc(local_size, GFP_KERNEL); - if (!local_q) { - err = -ENOMEM; - goto iounmap_1; - } - err = scif_map_single(&qp->local_buf, local_q, scifdev, local_size); - if (err) - goto kfree; - qp->remote_qp->local_read = 0; - /* - * To setup the inbound_q, the buffer lives locally, the read pointer - * is remote and the write pointer is local - */ - scif_rb_init(&qp->inbound_q, - &qp->remote_qp->local_read, - &qp->local_write, - local_q, get_count_order(local_size)); - err = scif_map_single(qp_offset, qp, scifdev, - sizeof(struct scif_qp)); - if (err) - goto unmap; - qp->local_qp = *qp_offset; - return err; -unmap: - scif_unmap_single(qp->local_buf, scifdev, local_size); - qp->local_buf = 0; -kfree: - kfree(local_q); -iounmap_1: - scif_iounmap(remote_q, remote_size, scifdev); - qp->outbound_q.rb_base = NULL; -iounmap: - scif_iounmap(qp->remote_qp, sizeof(struct scif_qp), scifdev); - qp->remote_qp = NULL; - return err; -} - -int scif_setup_qp_connect_response(struct scif_dev *scifdev, - struct scif_qp *qp, u64 payload) -{ - int err = 0; - void *r_buf; - int remote_size; - phys_addr_t tmp_phys; - - qp->remote_qp = scif_ioremap(payload, sizeof(struct scif_qp), scifdev); - - if (!qp->remote_qp) { - err = -ENOMEM; - goto error; - } - - if (qp->remote_qp->magic != SCIFEP_MAGIC) { - dev_err(&scifdev->sdev->dev, - "SCIFEP_MAGIC mismatch between self %d remote %d\n", - scif_dev[scif_info.nodeid].node, scifdev->node); - err = -ENODEV; - goto error; - } - - tmp_phys = qp->remote_qp->local_buf; - remote_size = qp->remote_qp->inbound_q.size; - r_buf = scif_ioremap(tmp_phys, remote_size, scifdev); - - if (!r_buf) - return -EIO; - - qp->local_read = 0; - scif_rb_init(&qp->outbound_q, - &qp->local_read, - &qp->remote_qp->local_write, - r_buf, - get_count_order(remote_size)); - /* - * Because the node QP may already be processing an INIT message, set - * the read pointer so the cached read offset isn't lost - */ - qp->remote_qp->local_read = qp->inbound_q.current_read_offset; - /* - * resetup the inbound_q now that we know where the - * inbound_read really is. - */ - scif_rb_init(&qp->inbound_q, - &qp->remote_qp->local_read, - &qp->local_write, - qp->inbound_q.rb_base, - get_count_order(qp->inbound_q.size)); -error: - return err; -} - -static __always_inline void -scif_send_msg_intr(struct scif_dev *scifdev) -{ - struct scif_hw_dev *sdev = scifdev->sdev; - - if (scifdev_is_p2p(scifdev)) - sdev->hw_ops->send_p2p_intr(sdev, scifdev->rdb, &scifdev->mmio); - else - sdev->hw_ops->send_intr(sdev, scifdev->rdb); -} - -int scif_qp_response(phys_addr_t phys, struct scif_dev *scifdev) -{ - int err = 0; - struct scifmsg msg; - - err = scif_setup_qp_connect_response(scifdev, scifdev->qpairs, phys); - if (!err) { - /* - * Now that everything is setup and mapped, we're ready - * to tell the peer about our queue's location - */ - msg.uop = SCIF_INIT; - msg.dst.node = scifdev->node; - err = scif_nodeqp_send(scifdev, &msg); - } - return err; -} - -void scif_send_exit(struct scif_dev *scifdev) -{ - struct scifmsg msg; - int ret; - - scifdev->exit = OP_IN_PROGRESS; - msg.uop = SCIF_EXIT; - msg.src.node = scif_info.nodeid; - msg.dst.node = scifdev->node; - ret = scif_nodeqp_send(scifdev, &msg); - if (ret) - goto done; - /* Wait for a SCIF_EXIT_ACK message */ - wait_event_timeout(scif_info.exitwq, scifdev->exit == OP_COMPLETED, - SCIF_NODE_ALIVE_TIMEOUT); -done: - scifdev->exit = OP_IDLE; -} - -int scif_setup_qp(struct scif_dev *scifdev) -{ - int err = 0; - int local_size; - struct scif_qp *qp; - - local_size = SCIF_NODE_QP_SIZE; - - qp = kzalloc(sizeof(*qp), GFP_KERNEL); - if (!qp) { - err = -ENOMEM; - return err; - } - qp->magic = SCIFEP_MAGIC; - scifdev->qpairs = qp; - err = scif_setup_qp_connect(qp, &scifdev->qp_dma_addr, - local_size, scifdev); - if (err) - goto free_qp; - /* - * We're as setup as we can be. The inbound_q is setup, w/o a usable - * outbound q. When we get a message, the read_ptr will be updated, - * and we will pull the message. - */ - return err; -free_qp: - kfree(scifdev->qpairs); - scifdev->qpairs = NULL; - return err; -} - -static void scif_p2p_freesg(struct scatterlist *sg) -{ - kfree(sg); -} - -static struct scatterlist * -scif_p2p_setsg(phys_addr_t pa, int page_size, int page_cnt) -{ - struct scatterlist *sg; - struct page *page; - int i; - - sg = kmalloc_array(page_cnt, sizeof(struct scatterlist), GFP_KERNEL); - if (!sg) - return NULL; - sg_init_table(sg, page_cnt); - for (i = 0; i < page_cnt; i++) { - page = pfn_to_page(pa >> PAGE_SHIFT); - sg_set_page(&sg[i], page, page_size, 0); - pa += page_size; - } - return sg; -} - -/* Init p2p mappings required to access peerdev from scifdev */ -static struct scif_p2p_info * -scif_init_p2p_info(struct scif_dev *scifdev, struct scif_dev *peerdev) -{ - struct scif_p2p_info *p2p; - int num_mmio_pages, num_aper_pages, sg_page_shift, err, num_aper_chunks; - struct scif_hw_dev *psdev = peerdev->sdev; - struct scif_hw_dev *sdev = scifdev->sdev; - - num_mmio_pages = psdev->mmio->len >> PAGE_SHIFT; - num_aper_pages = psdev->aper->len >> PAGE_SHIFT; - - p2p = kzalloc(sizeof(*p2p), GFP_KERNEL); - if (!p2p) - return NULL; - p2p->ppi_sg[SCIF_PPI_MMIO] = scif_p2p_setsg(psdev->mmio->pa, - PAGE_SIZE, num_mmio_pages); - if (!p2p->ppi_sg[SCIF_PPI_MMIO]) - goto free_p2p; - p2p->sg_nentries[SCIF_PPI_MMIO] = num_mmio_pages; - sg_page_shift = get_order(min(psdev->aper->len, (u64)(1 << 30))); - num_aper_chunks = num_aper_pages >> (sg_page_shift - PAGE_SHIFT); - p2p->ppi_sg[SCIF_PPI_APER] = scif_p2p_setsg(psdev->aper->pa, - 1 << sg_page_shift, - num_aper_chunks); - p2p->sg_nentries[SCIF_PPI_APER] = num_aper_chunks; - err = dma_map_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - num_mmio_pages, PCI_DMA_BIDIRECTIONAL); - if (err != num_mmio_pages) - goto scif_p2p_free; - err = dma_map_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_APER], - num_aper_chunks, PCI_DMA_BIDIRECTIONAL); - if (err != num_aper_chunks) - goto dma_unmap; - p2p->ppi_da[SCIF_PPI_MMIO] = sg_dma_address(p2p->ppi_sg[SCIF_PPI_MMIO]); - p2p->ppi_da[SCIF_PPI_APER] = sg_dma_address(p2p->ppi_sg[SCIF_PPI_APER]); - p2p->ppi_len[SCIF_PPI_MMIO] = num_mmio_pages; - p2p->ppi_len[SCIF_PPI_APER] = num_aper_pages; - p2p->ppi_peer_id = peerdev->node; - return p2p; -dma_unmap: - dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], DMA_BIDIRECTIONAL); -scif_p2p_free: - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); -free_p2p: - kfree(p2p); - return NULL; -} - -/* Uninitialize and release resources from a p2p mapping */ -static void scif_deinit_p2p_info(struct scif_dev *scifdev, - struct scif_p2p_info *p2p) -{ - struct scif_hw_dev *sdev = scifdev->sdev; - - dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], DMA_BIDIRECTIONAL); - dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_APER], - p2p->sg_nentries[SCIF_PPI_APER], DMA_BIDIRECTIONAL); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); - kfree(p2p); -} - -/** - * scif_node_connect: Respond to SCIF_NODE_CONNECT interrupt message - * @scifdev: SCIF device - * @dst: Destination node - * - * Connect the src and dst node by setting up the p2p connection - * between them. Management node here acts like a proxy. - */ -static void scif_node_connect(struct scif_dev *scifdev, int dst) -{ - struct scif_dev *dev_j = scifdev; - struct scif_dev *dev_i = NULL; - struct scif_p2p_info *p2p_ij = NULL; /* bus addr for j from i */ - struct scif_p2p_info *p2p_ji = NULL; /* bus addr for i from j */ - struct scif_p2p_info *p2p; - struct list_head *pos, *tmp; - struct scifmsg msg; - int err; - u64 tmppayload; - - if (dst < 1 || dst > scif_info.maxid) - return; - - dev_i = &scif_dev[dst]; - - if (!_scifdev_alive(dev_i)) - return; - /* - * If the p2p connection is already setup or in the process of setting - * up then just ignore this request. The requested node will get - * informed by SCIF_NODE_ADD_ACK or SCIF_NODE_ADD_NACK - */ - if (!list_empty(&dev_i->p2p)) { - list_for_each_safe(pos, tmp, &dev_i->p2p) { - p2p = list_entry(pos, struct scif_p2p_info, ppi_list); - if (p2p->ppi_peer_id == dev_j->node) - return; - } - } - p2p_ij = scif_init_p2p_info(dev_i, dev_j); - if (!p2p_ij) - return; - p2p_ji = scif_init_p2p_info(dev_j, dev_i); - if (!p2p_ji) { - scif_deinit_p2p_info(dev_i, p2p_ij); - return; - } - list_add_tail(&p2p_ij->ppi_list, &dev_i->p2p); - list_add_tail(&p2p_ji->ppi_list, &dev_j->p2p); - - /* - * Send a SCIF_NODE_ADD to dev_i, pass it its bus address - * as seen from dev_j - */ - msg.uop = SCIF_NODE_ADD; - msg.src.node = dev_j->node; - msg.dst.node = dev_i->node; - - msg.payload[0] = p2p_ji->ppi_da[SCIF_PPI_APER]; - msg.payload[1] = p2p_ij->ppi_da[SCIF_PPI_MMIO]; - msg.payload[2] = p2p_ij->ppi_da[SCIF_PPI_APER]; - msg.payload[3] = p2p_ij->ppi_len[SCIF_PPI_APER] << PAGE_SHIFT; - - err = scif_nodeqp_send(dev_i, &msg); - if (err) { - dev_err(&scifdev->sdev->dev, - "%s %d error %d\n", __func__, __LINE__, err); - return; - } - - /* Same as above but to dev_j */ - msg.uop = SCIF_NODE_ADD; - msg.src.node = dev_i->node; - msg.dst.node = dev_j->node; - - tmppayload = msg.payload[0]; - msg.payload[0] = msg.payload[2]; - msg.payload[2] = tmppayload; - msg.payload[1] = p2p_ji->ppi_da[SCIF_PPI_MMIO]; - msg.payload[3] = p2p_ji->ppi_len[SCIF_PPI_APER] << PAGE_SHIFT; - - scif_nodeqp_send(dev_j, &msg); -} - -static void scif_p2p_setup(void) -{ - int i, j; - - if (!scif_info.p2p_enable) - return; - - for (i = 1; i <= scif_info.maxid; i++) - if (!_scifdev_alive(&scif_dev[i])) - return; - - for (i = 1; i <= scif_info.maxid; i++) { - for (j = 1; j <= scif_info.maxid; j++) { - struct scif_dev *scifdev = &scif_dev[i]; - - if (i == j) - continue; - scif_node_connect(scifdev, j); - } - } -} - -static char *message_types[] = {"BAD", - "INIT", - "EXIT", - "SCIF_EXIT_ACK", - "SCIF_NODE_ADD", - "SCIF_NODE_ADD_ACK", - "SCIF_NODE_ADD_NACK", - "REMOVE_NODE", - "REMOVE_NODE_ACK", - "CNCT_REQ", - "CNCT_GNT", - "CNCT_GNTACK", - "CNCT_GNTNACK", - "CNCT_REJ", - "DISCNCT", - "DISCNT_ACK", - "CLIENT_SENT", - "CLIENT_RCVD", - "SCIF_GET_NODE_INFO", - "REGISTER", - "REGISTER_ACK", - "REGISTER_NACK", - "UNREGISTER", - "UNREGISTER_ACK", - "UNREGISTER_NACK", - "ALLOC_REQ", - "ALLOC_GNT", - "ALLOC_REJ", - "FREE_PHYS", - "FREE_VIRT", - "MUNMAP", - "MARK", - "MARK_ACK", - "MARK_NACK", - "WAIT", - "WAIT_ACK", - "WAIT_NACK", - "SIGNAL_LOCAL", - "SIGNAL_REMOTE", - "SIG_ACK", - "SIG_NACK"}; - -static void -scif_display_message(struct scif_dev *scifdev, struct scifmsg *msg, - const char *label) -{ - if (!scif_info.en_msg_log) - return; - if (msg->uop > SCIF_MAX_MSG) { - dev_err(&scifdev->sdev->dev, - "%s: unknown msg type %d\n", label, msg->uop); - return; - } - dev_info(&scifdev->sdev->dev, - "%s: msg type %s, src %d:%d, dest %d:%d payload 0x%llx:0x%llx:0x%llx:0x%llx\n", - label, message_types[msg->uop], msg->src.node, msg->src.port, - msg->dst.node, msg->dst.port, msg->payload[0], msg->payload[1], - msg->payload[2], msg->payload[3]); -} - -int _scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_qp *qp = scifdev->qpairs; - int err = -ENOMEM, loop_cnt = 0; - - scif_display_message(scifdev, msg, "Sent"); - if (!qp) { - err = -EINVAL; - goto error; - } - spin_lock(&qp->send_lock); - - while ((err = scif_rb_write(&qp->outbound_q, - msg, sizeof(struct scifmsg)))) { - mdelay(1); -#define SCIF_NODEQP_SEND_TO_MSEC (3 * 1000) - if (loop_cnt++ > (SCIF_NODEQP_SEND_TO_MSEC)) { - err = -ENODEV; - break; - } - } - if (!err) - scif_rb_commit(&qp->outbound_q); - spin_unlock(&qp->send_lock); - if (!err) { - if (scifdev_self(scifdev)) - /* - * For loopback we need to emulate an interrupt by - * queuing work for the queue handling real node - * Qp interrupts. - */ - queue_work(scifdev->intr_wq, &scifdev->intr_bh); - else - scif_send_msg_intr(scifdev); - } -error: - if (err) - dev_dbg(&scifdev->sdev->dev, - "%s %d error %d uop %d\n", - __func__, __LINE__, err, msg->uop); - return err; -} - -/** - * scif_nodeqp_send - Send a message on the node queue pair - * @scifdev: Scif Device. - * @msg: The message to be sent. - */ -int scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg) -{ - int err; - struct device *spdev = NULL; - - if (msg->uop > SCIF_EXIT_ACK) { - /* Don't send messages once the exit flow has begun */ - if (OP_IDLE != scifdev->exit) - return -ENODEV; - spdev = scif_get_peer_dev(scifdev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - } - err = _scif_nodeqp_send(scifdev, msg); - if (msg->uop > SCIF_EXIT_ACK) - scif_put_peer_dev(spdev); - return err; -} - -/* - * scif_misc_handler: - * - * Work queue handler for servicing miscellaneous SCIF tasks. - * Examples include: - * 1) Remote fence requests. - * 2) Destruction of temporary registered windows - * created during scif_vreadfrom()/scif_vwriteto(). - * 3) Cleanup of zombie endpoints. - */ -void scif_misc_handler(struct work_struct *work) -{ - scif_rma_handle_remote_fences(); - scif_rma_destroy_windows(); - scif_rma_destroy_tcw_invalid(); - scif_cleanup_zombie_epd(); -} - -/** - * scif_init() - Respond to SCIF_INIT interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - */ -static __always_inline void -scif_init(struct scif_dev *scifdev, struct scifmsg *msg) -{ - /* - * Allow the thread waiting for device page updates for the peer QP DMA - * address to complete initializing the inbound_q. - */ - flush_delayed_work(&scifdev->qp_dwork); - - scif_peer_register_device(scifdev); - - if (scif_is_mgmt_node()) { - mutex_lock(&scif_info.conflock); - scif_p2p_setup(); - mutex_unlock(&scif_info.conflock); - } -} - -/** - * scif_exit() - Respond to SCIF_EXIT interrupt message - * @scifdev: Remote SCIF device node - * @unused: Interrupt message (unused) - * - * This function stops the SCIF interface for the node which sent - * the SCIF_EXIT message and starts waiting for that node to - * resetup the queue pair again. - */ -static __always_inline void -scif_exit(struct scif_dev *scifdev, struct scifmsg *unused) -{ - scifdev->exit_ack_pending = true; - if (scif_is_mgmt_node()) - scif_disconnect_node(scifdev->node, false); - else - scif_stop(scifdev); - schedule_delayed_work(&scifdev->qp_dwork, - msecs_to_jiffies(1000)); -} - -/** - * scif_exitack() - Respond to SCIF_EXIT_ACK interrupt message - * @scifdev: Remote SCIF device node - * @unused: Interrupt message (unused) - * - */ -static __always_inline void -scif_exit_ack(struct scif_dev *scifdev, struct scifmsg *unused) -{ - scifdev->exit = OP_COMPLETED; - wake_up(&scif_info.exitwq); -} - -/** - * scif_node_add() - Respond to SCIF_NODE_ADD interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * When the mgmt node driver has finished initializing a MIC node queue pair it - * marks the node as online. It then looks for all currently online MIC cards - * and send a SCIF_NODE_ADD message to identify the ID of the new card for - * peer to peer initialization - * - * The local node allocates its incoming queue and sends its address in the - * SCIF_NODE_ADD_ACK message back to the mgmt node, the mgmt node "reflects" - * this message to the new node - */ -static __always_inline void -scif_node_add(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_dev *newdev; - dma_addr_t qp_offset; - int qp_connect; - struct scif_hw_dev *sdev; - - dev_dbg(&scifdev->sdev->dev, - "Scifdev %d:%d received NODE_ADD msg for node %d\n", - scifdev->node, msg->dst.node, msg->src.node); - dev_dbg(&scifdev->sdev->dev, - "Remote address for this node's aperture %llx\n", - msg->payload[0]); - newdev = &scif_dev[msg->src.node]; - newdev->node = msg->src.node; - newdev->sdev = scif_dev[SCIF_MGMT_NODE].sdev; - sdev = newdev->sdev; - - if (scif_setup_intr_wq(newdev)) { - dev_err(&scifdev->sdev->dev, - "failed to setup interrupts for %d\n", msg->src.node); - goto interrupt_setup_error; - } - newdev->mmio.va = ioremap(msg->payload[1], sdev->mmio->len); - if (!newdev->mmio.va) { - dev_err(&scifdev->sdev->dev, - "failed to map mmio for %d\n", msg->src.node); - goto mmio_map_error; - } - newdev->qpairs = kzalloc(sizeof(*newdev->qpairs), GFP_KERNEL); - if (!newdev->qpairs) - goto qp_alloc_error; - /* - * Set the base address of the remote node's memory since it gets - * added to qp_offset - */ - newdev->base_addr = msg->payload[0]; - - qp_connect = scif_setup_qp_connect(newdev->qpairs, &qp_offset, - SCIF_NODE_QP_SIZE, newdev); - if (qp_connect) { - dev_err(&scifdev->sdev->dev, - "failed to setup qp_connect %d\n", qp_connect); - goto qp_connect_error; - } - - newdev->db = sdev->hw_ops->next_db(sdev); - newdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler, - "SCIF_INTR", newdev, - newdev->db); - if (IS_ERR(newdev->cookie)) - goto qp_connect_error; - newdev->qpairs->magic = SCIFEP_MAGIC; - newdev->qpairs->qp_state = SCIF_QP_OFFLINE; - - msg->uop = SCIF_NODE_ADD_ACK; - msg->dst.node = msg->src.node; - msg->src.node = scif_info.nodeid; - msg->payload[0] = qp_offset; - msg->payload[2] = newdev->db; - scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], msg); - return; -qp_connect_error: - kfree(newdev->qpairs); - newdev->qpairs = NULL; -qp_alloc_error: - iounmap(newdev->mmio.va); - newdev->mmio.va = NULL; -mmio_map_error: -interrupt_setup_error: - dev_err(&scifdev->sdev->dev, - "node add failed for node %d\n", msg->src.node); - msg->uop = SCIF_NODE_ADD_NACK; - msg->dst.node = msg->src.node; - msg->src.node = scif_info.nodeid; - scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], msg); -} - -void scif_poll_qp_state(struct work_struct *work) -{ -#define SCIF_NODE_QP_RETRY 100 -#define SCIF_NODE_QP_TIMEOUT 100 - struct scif_dev *peerdev = container_of(work, struct scif_dev, - p2p_dwork.work); - struct scif_qp *qp = &peerdev->qpairs[0]; - - if (qp->qp_state != SCIF_QP_ONLINE || - qp->remote_qp->qp_state != SCIF_QP_ONLINE) { - if (peerdev->p2p_retry++ == SCIF_NODE_QP_RETRY) { - dev_err(&peerdev->sdev->dev, - "Warning: QP check timeout with state %d\n", - qp->qp_state); - goto timeout; - } - schedule_delayed_work(&peerdev->p2p_dwork, - msecs_to_jiffies(SCIF_NODE_QP_TIMEOUT)); - return; - } - return; -timeout: - dev_err(&peerdev->sdev->dev, - "%s %d remote node %d offline, state = 0x%x\n", - __func__, __LINE__, peerdev->node, qp->qp_state); - qp->remote_qp->qp_state = SCIF_QP_OFFLINE; - scif_peer_unregister_device(peerdev); - scif_cleanup_scifdev(peerdev); -} - -/** - * scif_node_add_ack() - Respond to SCIF_NODE_ADD_ACK interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * After a MIC node receives the SCIF_NODE_ADD_ACK message it send this - * message to the mgmt node to confirm the sequence is finished. - * - */ -static __always_inline void -scif_node_add_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_dev *peerdev; - struct scif_qp *qp; - struct scif_dev *dst_dev = &scif_dev[msg->dst.node]; - - dev_dbg(&scifdev->sdev->dev, - "Scifdev %d received SCIF_NODE_ADD_ACK msg src %d dst %d\n", - scifdev->node, msg->src.node, msg->dst.node); - dev_dbg(&scifdev->sdev->dev, - "payload %llx %llx %llx %llx\n", msg->payload[0], - msg->payload[1], msg->payload[2], msg->payload[3]); - if (scif_is_mgmt_node()) { - /* - * the lock serializes with scif_qp_response_ack. The mgmt node - * is forwarding the NODE_ADD_ACK message from src to dst we - * need to make sure that the dst has already received a - * NODE_ADD for src and setup its end of the qp to dst - */ - mutex_lock(&scif_info.conflock); - msg->payload[1] = scif_info.maxid; - scif_nodeqp_send(dst_dev, msg); - mutex_unlock(&scif_info.conflock); - return; - } - peerdev = &scif_dev[msg->src.node]; - peerdev->sdev = scif_dev[SCIF_MGMT_NODE].sdev; - peerdev->node = msg->src.node; - - qp = &peerdev->qpairs[0]; - - if ((scif_setup_qp_connect_response(peerdev, &peerdev->qpairs[0], - msg->payload[0]))) - goto local_error; - peerdev->rdb = msg->payload[2]; - qp->remote_qp->qp_state = SCIF_QP_ONLINE; - - scif_peer_register_device(peerdev); - - schedule_delayed_work(&peerdev->p2p_dwork, 0); - return; -local_error: - scif_cleanup_scifdev(peerdev); -} - -/** - * scif_node_add_nack: Respond to SCIF_NODE_ADD_NACK interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * SCIF_NODE_ADD failed, so inform the waiting wq. - */ -static __always_inline void -scif_node_add_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - if (scif_is_mgmt_node()) { - struct scif_dev *dst_dev = &scif_dev[msg->dst.node]; - - dev_dbg(&scifdev->sdev->dev, - "SCIF_NODE_ADD_NACK received from %d\n", scifdev->node); - scif_nodeqp_send(dst_dev, msg); - } -} - -/** - * scif_node_remove: Handle SCIF_NODE_REMOVE message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * Handle node removal. - */ -static __always_inline void -scif_node_remove(struct scif_dev *scifdev, struct scifmsg *msg) -{ - int node = msg->payload[0]; - struct scif_dev *scdev = &scif_dev[node]; - - scdev->node_remove_ack_pending = true; - scif_handle_remove_node(node); -} - -/** - * scif_node_remove_ack: Handle SCIF_NODE_REMOVE_ACK message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * The peer has acked a SCIF_NODE_REMOVE message. - */ -static __always_inline void -scif_node_remove_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_dev *sdev = &scif_dev[msg->payload[0]]; - - atomic_inc(&sdev->disconn_rescnt); - wake_up(&sdev->disconn_wq); -} - -/** - * scif_get_node_info: Respond to SCIF_GET_NODE_INFO interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * Retrieve node info i.e maxid and total from the mgmt node. - */ -static __always_inline void -scif_get_node_info_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - if (scif_is_mgmt_node()) { - swap(msg->dst.node, msg->src.node); - mutex_lock(&scif_info.conflock); - msg->payload[1] = scif_info.maxid; - msg->payload[2] = scif_info.total; - mutex_unlock(&scif_info.conflock); - scif_nodeqp_send(scifdev, msg); - } else { - struct completion *node_info = - (struct completion *)msg->payload[3]; - - mutex_lock(&scif_info.conflock); - scif_info.maxid = msg->payload[1]; - scif_info.total = msg->payload[2]; - complete_all(node_info); - mutex_unlock(&scif_info.conflock); - } -} - -static void -scif_msg_unknown(struct scif_dev *scifdev, struct scifmsg *msg) -{ - /* Bogus Node Qp Message? */ - dev_err(&scifdev->sdev->dev, - "Unknown message 0x%xn scifdev->node 0x%x\n", - msg->uop, scifdev->node); -} - -static void (*scif_intr_func[SCIF_MAX_MSG + 1]) - (struct scif_dev *, struct scifmsg *msg) = { - scif_msg_unknown, /* Error */ - scif_init, /* SCIF_INIT */ - scif_exit, /* SCIF_EXIT */ - scif_exit_ack, /* SCIF_EXIT_ACK */ - scif_node_add, /* SCIF_NODE_ADD */ - scif_node_add_ack, /* SCIF_NODE_ADD_ACK */ - scif_node_add_nack, /* SCIF_NODE_ADD_NACK */ - scif_node_remove, /* SCIF_NODE_REMOVE */ - scif_node_remove_ack, /* SCIF_NODE_REMOVE_ACK */ - scif_cnctreq, /* SCIF_CNCT_REQ */ - scif_cnctgnt, /* SCIF_CNCT_GNT */ - scif_cnctgnt_ack, /* SCIF_CNCT_GNTACK */ - scif_cnctgnt_nack, /* SCIF_CNCT_GNTNACK */ - scif_cnctrej, /* SCIF_CNCT_REJ */ - scif_discnct, /* SCIF_DISCNCT */ - scif_discnt_ack, /* SCIF_DISCNT_ACK */ - scif_clientsend, /* SCIF_CLIENT_SENT */ - scif_clientrcvd, /* SCIF_CLIENT_RCVD */ - scif_get_node_info_resp,/* SCIF_GET_NODE_INFO */ - scif_recv_reg, /* SCIF_REGISTER */ - scif_recv_reg_ack, /* SCIF_REGISTER_ACK */ - scif_recv_reg_nack, /* SCIF_REGISTER_NACK */ - scif_recv_unreg, /* SCIF_UNREGISTER */ - scif_recv_unreg_ack, /* SCIF_UNREGISTER_ACK */ - scif_recv_unreg_nack, /* SCIF_UNREGISTER_NACK */ - scif_alloc_req, /* SCIF_ALLOC_REQ */ - scif_alloc_gnt_rej, /* SCIF_ALLOC_GNT */ - scif_alloc_gnt_rej, /* SCIF_ALLOC_REJ */ - scif_free_virt, /* SCIF_FREE_VIRT */ - scif_recv_munmap, /* SCIF_MUNMAP */ - scif_recv_mark, /* SCIF_MARK */ - scif_recv_mark_resp, /* SCIF_MARK_ACK */ - scif_recv_mark_resp, /* SCIF_MARK_NACK */ - scif_recv_wait, /* SCIF_WAIT */ - scif_recv_wait_resp, /* SCIF_WAIT_ACK */ - scif_recv_wait_resp, /* SCIF_WAIT_NACK */ - scif_recv_sig_local, /* SCIF_SIG_LOCAL */ - scif_recv_sig_remote, /* SCIF_SIG_REMOTE */ - scif_recv_sig_resp, /* SCIF_SIG_ACK */ - scif_recv_sig_resp, /* SCIF_SIG_NACK */ -}; - -static int scif_max_msg_id = SCIF_MAX_MSG; -/** - * scif_nodeqp_msg_handler() - Common handler for node messages - * @scifdev: Remote device to respond to - * @qp: Remote memory pointer - * @msg: The message to be handled. - * - * This routine calls the appropriate routine to handle a Node Qp - * message receipt - */ -static void -scif_nodeqp_msg_handler(struct scif_dev *scifdev, - struct scif_qp *qp, struct scifmsg *msg) -{ - scif_display_message(scifdev, msg, "Rcvd"); - - if (msg->uop > (u32)scif_max_msg_id) { - /* Bogus Node Qp Message? */ - dev_err(&scifdev->sdev->dev, - "Unknown message 0x%xn scifdev->node 0x%x\n", - msg->uop, scifdev->node); - return; - } - - scif_intr_func[msg->uop](scifdev, msg); -} - -/** - * scif_nodeqp_intrhandler() - Interrupt handler for node messages - * @scifdev: Remote device to respond to - * @qp: Remote memory pointer - * - * This routine is triggered by the interrupt mechanism. It reads - * messages from the node queue RB and calls the Node QP Message handling - * routine. - */ -void scif_nodeqp_intrhandler(struct scif_dev *scifdev, struct scif_qp *qp) -{ - struct scifmsg msg; - int read_size; - - do { - read_size = scif_rb_get_next(&qp->inbound_q, &msg, sizeof(msg)); - if (!read_size) - break; - scif_nodeqp_msg_handler(scifdev, qp, &msg); - /* - * The node queue pair is unmapped so skip the read pointer - * update after receipt of a SCIF_EXIT_ACK - */ - if (SCIF_EXIT_ACK == msg.uop) - break; - scif_rb_update_read_ptr(&qp->inbound_q); - } while (1); -} - -/** - * scif_loopb_wq_handler - Loopback Workqueue Handler. - * @unused: loop back work (unused) - * - * This work queue routine is invoked by the loopback work queue handler. - * It grabs the recv lock, dequeues any available messages from the head - * of the loopback message list, calls the node QP message handler, - * waits for it to return, then frees up this message and dequeues more - * elements of the list if available. - */ -static void scif_loopb_wq_handler(struct work_struct *unused) -{ - struct scif_dev *scifdev = scif_info.loopb_dev; - struct scif_qp *qp = scifdev->qpairs; - struct scif_loopb_msg *msg; - - do { - msg = NULL; - spin_lock(&qp->recv_lock); - if (!list_empty(&scif_info.loopb_recv_q)) { - msg = list_first_entry(&scif_info.loopb_recv_q, - struct scif_loopb_msg, - list); - list_del(&msg->list); - } - spin_unlock(&qp->recv_lock); - - if (msg) { - scif_nodeqp_msg_handler(scifdev, qp, &msg->msg); - kfree(msg); - } - } while (msg); -} - -/** - * scif_loopb_msg_handler() - Workqueue handler for loopback messages. - * @scifdev: SCIF device - * @qp: Queue pair. - * - * This work queue routine is triggered when a loopback message is received. - * - * We need special handling for receiving Node Qp messages on a loopback SCIF - * device via two workqueues for receiving messages. - * - * The reason we need the extra workqueue which is not required with *normal* - * non-loopback SCIF devices is the potential classic deadlock described below: - * - * Thread A tries to send a message on a loopback SCIF device and blocks since - * there is no space in the RB while it has the send_lock held or another - * lock called lock X for example. - * - * Thread B: The Loopback Node QP message receive workqueue receives the message - * and tries to send a message (eg an ACK) to the loopback SCIF device. It tries - * to grab the send lock again or lock X and deadlocks with Thread A. The RB - * cannot be drained any further due to this classic deadlock. - * - * In order to avoid deadlocks as mentioned above we have an extra level of - * indirection achieved by having two workqueues. - * 1) The first workqueue whose handler is scif_loopb_msg_handler reads - * messages from the Node QP RB, adds them to a list and queues work for the - * second workqueue. - * - * 2) The second workqueue whose handler is scif_loopb_wq_handler dequeues - * messages from the list, handles them, frees up the memory and dequeues - * more elements from the list if possible. - */ -int -scif_loopb_msg_handler(struct scif_dev *scifdev, struct scif_qp *qp) -{ - int read_size; - struct scif_loopb_msg *msg; - - do { - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return -ENOMEM; - read_size = scif_rb_get_next(&qp->inbound_q, &msg->msg, - sizeof(struct scifmsg)); - if (read_size != sizeof(struct scifmsg)) { - kfree(msg); - scif_rb_update_read_ptr(&qp->inbound_q); - break; - } - spin_lock(&qp->recv_lock); - list_add_tail(&msg->list, &scif_info.loopb_recv_q); - spin_unlock(&qp->recv_lock); - queue_work(scif_info.loopb_wq, &scif_info.loopb_work); - scif_rb_update_read_ptr(&qp->inbound_q); - } while (read_size == sizeof(struct scifmsg)); - return read_size; -} - -/** - * scif_setup_loopback_qp - One time setup work for Loopback Node Qp. - * @scifdev: SCIF device - * - * Sets up the required loopback workqueues, queue pairs and ring buffers - */ -int scif_setup_loopback_qp(struct scif_dev *scifdev) -{ - int err = 0; - void *local_q; - struct scif_qp *qp; - - err = scif_setup_intr_wq(scifdev); - if (err) - goto exit; - INIT_LIST_HEAD(&scif_info.loopb_recv_q); - snprintf(scif_info.loopb_wqname, sizeof(scif_info.loopb_wqname), - "SCIF LOOPB %d", scifdev->node); - scif_info.loopb_wq = - alloc_ordered_workqueue(scif_info.loopb_wqname, 0); - if (!scif_info.loopb_wq) { - err = -ENOMEM; - goto destroy_intr; - } - INIT_WORK(&scif_info.loopb_work, scif_loopb_wq_handler); - /* Allocate Self Qpair */ - scifdev->qpairs = kzalloc(sizeof(*scifdev->qpairs), GFP_KERNEL); - if (!scifdev->qpairs) { - err = -ENOMEM; - goto destroy_loopb_wq; - } - - qp = scifdev->qpairs; - qp->magic = SCIFEP_MAGIC; - spin_lock_init(&qp->send_lock); - spin_lock_init(&qp->recv_lock); - - local_q = kzalloc(SCIF_NODE_QP_SIZE, GFP_KERNEL); - if (!local_q) { - err = -ENOMEM; - goto free_qpairs; - } - /* - * For loopback the inbound_q and outbound_q are essentially the same - * since the Node sends a message on the loopback interface to the - * outbound_q which is then received on the inbound_q. - */ - scif_rb_init(&qp->outbound_q, - &qp->local_read, - &qp->local_write, - local_q, get_count_order(SCIF_NODE_QP_SIZE)); - - scif_rb_init(&qp->inbound_q, - &qp->local_read, - &qp->local_write, - local_q, get_count_order(SCIF_NODE_QP_SIZE)); - scif_info.nodeid = scifdev->node; - - scif_peer_register_device(scifdev); - - scif_info.loopb_dev = scifdev; - return err; -free_qpairs: - kfree(scifdev->qpairs); -destroy_loopb_wq: - destroy_workqueue(scif_info.loopb_wq); -destroy_intr: - scif_destroy_intr_wq(scifdev); -exit: - return err; -} - -/** - * scif_destroy_loopback_qp - One time uninit work for Loopback Node Qp - * @scifdev: SCIF device - * - * Destroys the workqueues and frees up the Ring Buffer and Queue Pair memory. - */ -int scif_destroy_loopback_qp(struct scif_dev *scifdev) -{ - scif_peer_unregister_device(scifdev); - destroy_workqueue(scif_info.loopb_wq); - scif_destroy_intr_wq(scifdev); - kfree(scifdev->qpairs->outbound_q.rb_base); - kfree(scifdev->qpairs); - scifdev->sdev = NULL; - scif_info.loopb_dev = NULL; - return 0; -} - -void scif_destroy_p2p(struct scif_dev *scifdev) -{ - struct scif_dev *peer_dev; - struct scif_p2p_info *p2p; - struct list_head *pos, *tmp; - int bd; - - mutex_lock(&scif_info.conflock); - /* Free P2P mappings in the given node for all its peer nodes */ - list_for_each_safe(pos, tmp, &scifdev->p2p) { - p2p = list_entry(pos, struct scif_p2p_info, ppi_list); - dma_unmap_sg(&scifdev->sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], - DMA_BIDIRECTIONAL); - dma_unmap_sg(&scifdev->sdev->dev, p2p->ppi_sg[SCIF_PPI_APER], - p2p->sg_nentries[SCIF_PPI_APER], - DMA_BIDIRECTIONAL); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); - list_del(pos); - kfree(p2p); - } - - /* Free P2P mapping created in the peer nodes for the given node */ - for (bd = SCIF_MGMT_NODE + 1; bd <= scif_info.maxid; bd++) { - peer_dev = &scif_dev[bd]; - list_for_each_safe(pos, tmp, &peer_dev->p2p) { - p2p = list_entry(pos, struct scif_p2p_info, ppi_list); - if (p2p->ppi_peer_id == scifdev->node) { - dma_unmap_sg(&peer_dev->sdev->dev, - p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], - DMA_BIDIRECTIONAL); - dma_unmap_sg(&peer_dev->sdev->dev, - p2p->ppi_sg[SCIF_PPI_APER], - p2p->sg_nentries[SCIF_PPI_APER], - DMA_BIDIRECTIONAL); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); - list_del(pos); - kfree(p2p); - } - } - } - mutex_unlock(&scif_info.conflock); -} diff --git a/drivers/misc/mic/scif/scif_nodeqp.h b/drivers/misc/mic/scif/scif_nodeqp.h deleted file mode 100644 index 95896273138e..000000000000 --- a/drivers/misc/mic/scif/scif_nodeqp.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - * - */ -#ifndef SCIF_NODEQP -#define SCIF_NODEQP - -#include "scif_rb.h" -#include "scif_peer_bus.h" - -#define SCIF_INIT 1 /* First message sent to the peer node for discovery */ -#define SCIF_EXIT 2 /* Last message from the peer informing intent to exit */ -#define SCIF_EXIT_ACK 3 /* Response to SCIF_EXIT message */ -#define SCIF_NODE_ADD 4 /* Tell Online nodes a new node exits */ -#define SCIF_NODE_ADD_ACK 5 /* Confirm to mgmt node sequence is finished */ -#define SCIF_NODE_ADD_NACK 6 /* SCIF_NODE_ADD failed */ -#define SCIF_NODE_REMOVE 7 /* Request to deactivate a SCIF node */ -#define SCIF_NODE_REMOVE_ACK 8 /* Response to a SCIF_NODE_REMOVE message */ -#define SCIF_CNCT_REQ 9 /* Phys addr of Request connection to a port */ -#define SCIF_CNCT_GNT 10 /* Phys addr of new Grant connection request */ -#define SCIF_CNCT_GNTACK 11 /* Error type Reject a connection request */ -#define SCIF_CNCT_GNTNACK 12 /* Error type Reject a connection request */ -#define SCIF_CNCT_REJ 13 /* Error type Reject a connection request */ -#define SCIF_DISCNCT 14 /* Notify peer that connection is being terminated */ -#define SCIF_DISCNT_ACK 15 /* Notify peer that connection is being terminated */ -#define SCIF_CLIENT_SENT 16 /* Notify the peer that data has been written */ -#define SCIF_CLIENT_RCVD 17 /* Notify the peer that data has been read */ -#define SCIF_GET_NODE_INFO 18 /* Get current node mask from the mgmt node*/ -#define SCIF_REGISTER 19 /* Tell peer about a new registered window */ -#define SCIF_REGISTER_ACK 20 /* Notify peer about unregistration success */ -#define SCIF_REGISTER_NACK 21 /* Notify peer about registration success */ -#define SCIF_UNREGISTER 22 /* Tell peer about unregistering a window */ -#define SCIF_UNREGISTER_ACK 23 /* Notify peer about registration failure */ -#define SCIF_UNREGISTER_NACK 24 /* Notify peer about unregistration failure */ -#define SCIF_ALLOC_REQ 25 /* Request a mapped buffer */ -#define SCIF_ALLOC_GNT 26 /* Notify peer about allocation success */ -#define SCIF_ALLOC_REJ 27 /* Notify peer about allocation failure */ -#define SCIF_FREE_VIRT 28 /* Free previously allocated virtual memory */ -#define SCIF_MUNMAP 29 /* Acknowledgment for a SCIF_MMAP request */ -#define SCIF_MARK 30 /* SCIF Remote Fence Mark Request */ -#define SCIF_MARK_ACK 31 /* SCIF Remote Fence Mark Success */ -#define SCIF_MARK_NACK 32 /* SCIF Remote Fence Mark Failure */ -#define SCIF_WAIT 33 /* SCIF Remote Fence Wait Request */ -#define SCIF_WAIT_ACK 34 /* SCIF Remote Fence Wait Success */ -#define SCIF_WAIT_NACK 35 /* SCIF Remote Fence Wait Failure */ -#define SCIF_SIG_LOCAL 36 /* SCIF Remote Fence Local Signal Request */ -#define SCIF_SIG_REMOTE 37 /* SCIF Remote Fence Remote Signal Request */ -#define SCIF_SIG_ACK 38 /* SCIF Remote Fence Remote Signal Success */ -#define SCIF_SIG_NACK 39 /* SCIF Remote Fence Remote Signal Failure */ -#define SCIF_MAX_MSG SCIF_SIG_NACK - -/* - * struct scifmsg - Node QP message format - * - * @src: Source information - * @dst: Destination information - * @uop: The message opcode - * @payload: Unique payload format for each message - */ -struct scifmsg { - struct scif_port_id src; - struct scif_port_id dst; - u32 uop; - u64 payload[4]; -} __packed; - -/* - * struct scif_allocmsg - Used with SCIF_ALLOC_REQ to request - * the remote note to allocate memory - * - * phys_addr: Physical address of the buffer - * vaddr: Virtual address of the buffer - * size: Size of the buffer - * state: Current state - * allocwq: wait queue for status - */ -struct scif_allocmsg { - dma_addr_t phys_addr; - unsigned long vaddr; - size_t size; - enum scif_msg_state state; - wait_queue_head_t allocwq; -}; - -/* - * struct scif_qp - Node Queue Pair - * - * Interesting structure -- a little difficult because we can only - * write across the PCIe, so any r/w pointer we need to read is - * local. We only need to read the read pointer on the inbound_q - * and read the write pointer in the outbound_q - * - * @magic: Magic value to ensure the peer sees the QP correctly - * @outbound_q: The outbound ring buffer for sending messages - * @inbound_q: The inbound ring buffer for receiving messages - * @local_write: Local write index - * @local_read: Local read index - * @remote_qp: The remote queue pair - * @local_buf: DMA address of local ring buffer - * @local_qp: DMA address of the local queue pair data structure - * @remote_buf: DMA address of remote ring buffer - * @qp_state: QP state i.e. online or offline used for P2P - * @send_lock: synchronize access to outbound queue - * @recv_lock: Synchronize access to inbound queue - */ -struct scif_qp { - u64 magic; -#define SCIFEP_MAGIC 0x5c1f000000005c1fULL - struct scif_rb outbound_q; - struct scif_rb inbound_q; - - u32 local_write __aligned(64); - u32 local_read __aligned(64); - struct scif_qp *remote_qp; - dma_addr_t local_buf; - dma_addr_t local_qp; - dma_addr_t remote_buf; - u32 qp_state; -#define SCIF_QP_OFFLINE 0xdead -#define SCIF_QP_ONLINE 0xc0de - spinlock_t send_lock; - spinlock_t recv_lock; -}; - -/* - * struct scif_loopb_msg - An element in the loopback Node QP message list. - * - * @msg - The SCIF node QP message - * @list - link in the list of messages - */ -struct scif_loopb_msg { - struct scifmsg msg; - struct list_head list; -}; - -int scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg); -int _scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_nodeqp_intrhandler(struct scif_dev *scifdev, struct scif_qp *qp); -int scif_loopb_msg_handler(struct scif_dev *scifdev, struct scif_qp *qp); -int scif_setup_qp(struct scif_dev *scifdev); -int scif_qp_response(phys_addr_t phys, struct scif_dev *dev); -int scif_setup_qp_connect(struct scif_qp *qp, dma_addr_t *qp_offset, - int local_size, struct scif_dev *scifdev); -int scif_setup_qp_accept(struct scif_qp *qp, dma_addr_t *qp_offset, - dma_addr_t phys, int local_size, - struct scif_dev *scifdev); -int scif_setup_qp_connect_response(struct scif_dev *scifdev, - struct scif_qp *qp, u64 payload); -int scif_setup_loopback_qp(struct scif_dev *scifdev); -int scif_destroy_loopback_qp(struct scif_dev *scifdev); -void scif_poll_qp_state(struct work_struct *work); -void scif_destroy_p2p(struct scif_dev *scifdev); -void scif_send_exit(struct scif_dev *scifdev); -static inline struct device *scif_get_peer_dev(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - struct device *spdev_ret; - - rcu_read_lock(); - spdev = rcu_dereference(scifdev->spdev); - if (spdev) - spdev_ret = get_device(&spdev->dev); - else - spdev_ret = ERR_PTR(-ENODEV); - rcu_read_unlock(); - return spdev_ret; -} - -static inline void scif_put_peer_dev(struct device *dev) -{ - put_device(dev); -} -#endif /* SCIF_NODEQP */ diff --git a/drivers/misc/mic/scif/scif_peer_bus.c b/drivers/misc/mic/scif/scif_peer_bus.c deleted file mode 100644 index 6d608308bb60..000000000000 --- a/drivers/misc/mic/scif/scif_peer_bus.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include "../bus/scif_bus.h" -#include "scif_peer_bus.h" - -static inline struct scif_peer_dev * -dev_to_scif_peer(struct device *dev) -{ - return container_of(dev, struct scif_peer_dev, dev); -} - -struct bus_type scif_peer_bus = { - .name = "scif_peer_bus", -}; - -static void scif_peer_release_dev(struct device *d) -{ - struct scif_peer_dev *sdev = dev_to_scif_peer(d); - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; - - scif_cleanup_scifdev(scifdev); - kfree(sdev); -} - -static int scif_peer_initialize_device(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - int ret; - - spdev = kzalloc(sizeof(*spdev), GFP_KERNEL); - if (!spdev) { - ret = -ENOMEM; - goto err; - } - - spdev->dev.parent = scifdev->sdev->dev.parent; - spdev->dev.release = scif_peer_release_dev; - spdev->dnode = scifdev->node; - spdev->dev.bus = &scif_peer_bus; - dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode); - - device_initialize(&spdev->dev); - get_device(&spdev->dev); - rcu_assign_pointer(scifdev->spdev, spdev); - - mutex_lock(&scif_info.conflock); - scif_info.total++; - scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid); - mutex_unlock(&scif_info.conflock); - return 0; -err: - dev_err(&scifdev->sdev->dev, - "dnode %d: initialize_device rc %d\n", scifdev->node, ret); - return ret; -} - -static int scif_peer_add_device(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev); - char pool_name[16]; - int ret; - - ret = device_add(&spdev->dev); - put_device(&spdev->dev); - if (ret) { - dev_err(&scifdev->sdev->dev, - "dnode %d: peer device_add failed\n", scifdev->node); - goto put_spdev; - } - - scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode); - scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev, - sizeof(struct scif_status), 1, - 0); - if (!scifdev->signal_pool) { - dev_err(&scifdev->sdev->dev, - "dnode %d: dmam_pool_create failed\n", scifdev->node); - ret = -ENOMEM; - goto del_spdev; - } - dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode); - return 0; -del_spdev: - device_del(&spdev->dev); -put_spdev: - RCU_INIT_POINTER(scifdev->spdev, NULL); - synchronize_rcu(); - put_device(&spdev->dev); - - mutex_lock(&scif_info.conflock); - scif_info.total--; - mutex_unlock(&scif_info.conflock); - return ret; -} - -void scif_add_peer_device(struct work_struct *work) -{ - struct scif_dev *scifdev = container_of(work, struct scif_dev, - peer_add_work); - - scif_peer_add_device(scifdev); -} - -/* - * Peer device registration is split into a device_initialize and a device_add. - * The reason for doing this is as follows: First, peer device registration - * itself cannot be done in the message processing thread and must be delegated - * to another workqueue, otherwise if SCIF client probe, called during peer - * device registration, calls scif_connect(..), it will block the message - * processing thread causing a deadlock. Next, device_initialize is done in the - * "top-half" message processing thread and device_add in the "bottom-half" - * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing - * concurrently with SCIF_INIT message processing is unable to get a reference - * on the peer device, thereby failing the connect request. - */ -void scif_peer_register_device(struct scif_dev *scifdev) -{ - int ret; - - mutex_lock(&scifdev->lock); - ret = scif_peer_initialize_device(scifdev); - if (ret) - goto exit; - schedule_work(&scifdev->peer_add_work); -exit: - mutex_unlock(&scifdev->lock); -} - -int scif_peer_unregister_device(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - - mutex_lock(&scifdev->lock); - /* Flush work to ensure device register is complete */ - flush_work(&scifdev->peer_add_work); - - /* - * Continue holding scifdev->lock since theoretically unregister_device - * can be called simultaneously from multiple threads - */ - spdev = rcu_dereference(scifdev->spdev); - if (!spdev) { - mutex_unlock(&scifdev->lock); - return -ENODEV; - } - - RCU_INIT_POINTER(scifdev->spdev, NULL); - synchronize_rcu(); - mutex_unlock(&scifdev->lock); - - dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode); - device_unregister(&spdev->dev); - - mutex_lock(&scif_info.conflock); - scif_info.total--; - mutex_unlock(&scif_info.conflock); - return 0; -} - -int scif_peer_bus_init(void) -{ - return bus_register(&scif_peer_bus); -} - -void scif_peer_bus_exit(void) -{ - bus_unregister(&scif_peer_bus); -} diff --git a/drivers/misc/mic/scif/scif_peer_bus.h b/drivers/misc/mic/scif/scif_peer_bus.h deleted file mode 100644 index 2ea4c51c18c1..000000000000 --- a/drivers/misc/mic/scif/scif_peer_bus.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef _SCIF_PEER_BUS_H_ -#define _SCIF_PEER_BUS_H_ - -#include <linux/device.h> -#include <linux/mic_common.h> -#include <linux/scif.h> - -struct scif_dev; - -void scif_add_peer_device(struct work_struct *work); -void scif_peer_register_device(struct scif_dev *sdev); -int scif_peer_unregister_device(struct scif_dev *scifdev); -int scif_peer_bus_init(void); -void scif_peer_bus_exit(void); -#endif /* _SCIF_PEER_BUS_H */ diff --git a/drivers/misc/mic/scif/scif_ports.c b/drivers/misc/mic/scif/scif_ports.c deleted file mode 100644 index 4bdb5ef9a139..000000000000 --- a/drivers/misc/mic/scif/scif_ports.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include <linux/idr.h> - -#include "scif_main.h" - -#define SCIF_PORT_COUNT 0x10000 /* Ports available */ - -struct idr scif_ports; - -/** - * struct scif_port - SCIF port information - * - * @ref_cnt: Reference count since there can be multiple endpoints - * created via scif_accept(..) simultaneously using a port. - */ -struct scif_port { - int ref_cnt; -}; - -/** - * __scif_get_port - Reserve a specified port # for SCIF and add it - * to the global list. - * @start: lowest port # to be reserved (inclusive). - * @end: highest port # to be reserved (exclusive). - * - * @return : Allocated SCIF port #, or -ENOSPC if port unavailable. - * On memory allocation failure, returns -ENOMEM. - */ -static int __scif_get_port(int start, int end) -{ - int id; - struct scif_port *port = kzalloc(sizeof(*port), GFP_ATOMIC); - - if (!port) - return -ENOMEM; - spin_lock(&scif_info.port_lock); - id = idr_alloc(&scif_ports, port, start, end, GFP_ATOMIC); - if (id >= 0) - port->ref_cnt++; - spin_unlock(&scif_info.port_lock); - return id; -} - -/** - * scif_rsrv_port - Reserve a specified port # for SCIF. - * @port : port # to be reserved. - * - * @return : Allocated SCIF port #, or -ENOSPC if port unavailable. - * On memory allocation failure, returns -ENOMEM. - */ -int scif_rsrv_port(u16 port) -{ - return __scif_get_port(port, port + 1); -} - -/** - * scif_get_new_port - Get and reserve any port # for SCIF in the range - * SCIF_PORT_RSVD + 1 to SCIF_PORT_COUNT - 1. - * - * @return : Allocated SCIF port #, or -ENOSPC if no ports available. - * On memory allocation failure, returns -ENOMEM. - */ -int scif_get_new_port(void) -{ - return __scif_get_port(SCIF_PORT_RSVD + 1, SCIF_PORT_COUNT); -} - -/** - * scif_get_port - Increment the reference count for a SCIF port - * @id : SCIF port - * - * @return : None - */ -void scif_get_port(u16 id) -{ - struct scif_port *port; - - if (!id) - return; - spin_lock(&scif_info.port_lock); - port = idr_find(&scif_ports, id); - if (port) - port->ref_cnt++; - spin_unlock(&scif_info.port_lock); -} - -/** - * scif_put_port - Release a reserved SCIF port - * @id : SCIF port to be released. - * - * @return : None - */ -void scif_put_port(u16 id) -{ - struct scif_port *port; - - if (!id) - return; - spin_lock(&scif_info.port_lock); - port = idr_find(&scif_ports, id); - if (port) { - port->ref_cnt--; - if (!port->ref_cnt) { - idr_remove(&scif_ports, id); - kfree(port); - } - } - spin_unlock(&scif_info.port_lock); -} diff --git a/drivers/misc/mic/scif/scif_rb.c b/drivers/misc/mic/scif/scif_rb.c deleted file mode 100644 index e425882ae06d..000000000000 --- a/drivers/misc/mic/scif/scif_rb.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include <linux/circ_buf.h> -#include <linux/types.h> -#include <linux/io.h> -#include <linux/errno.h> - -#include "scif_rb.h" - -#define scif_rb_ring_cnt(head, tail, size) CIRC_CNT(head, tail, size) -#define scif_rb_ring_space(head, tail, size) CIRC_SPACE(head, tail, size) - -/** - * scif_rb_init - Initializes the ring buffer - * @rb: ring buffer - * @read_ptr: A pointer to the read offset - * @write_ptr: A pointer to the write offset - * @rb_base: A pointer to the base of the ring buffer - * @size: The size of the ring buffer in powers of two - */ -void scif_rb_init(struct scif_rb *rb, u32 *read_ptr, u32 *write_ptr, - void *rb_base, u8 size) -{ - rb->rb_base = rb_base; - rb->size = (1 << size); - rb->read_ptr = read_ptr; - rb->write_ptr = write_ptr; - rb->current_read_offset = *read_ptr; - rb->current_write_offset = *write_ptr; -} - -/* Copies a message to the ring buffer -- handles the wrap around case */ -static void memcpy_torb(struct scif_rb *rb, void *header, - void *msg, u32 size) -{ - u32 size1, size2; - - if (header + size >= rb->rb_base + rb->size) { - /* Need to call two copies if it wraps around */ - size1 = (u32)(rb->rb_base + rb->size - header); - size2 = size - size1; - memcpy_toio((void __iomem __force *)header, msg, size1); - memcpy_toio((void __iomem __force *)rb->rb_base, - msg + size1, size2); - } else { - memcpy_toio((void __iomem __force *)header, msg, size); - } -} - -/* Copies a message from the ring buffer -- handles the wrap around case */ -static void memcpy_fromrb(struct scif_rb *rb, void *header, - void *msg, u32 size) -{ - u32 size1, size2; - - if (header + size >= rb->rb_base + rb->size) { - /* Need to call two copies if it wraps around */ - size1 = (u32)(rb->rb_base + rb->size - header); - size2 = size - size1; - memcpy_fromio(msg, (void __iomem __force *)header, size1); - memcpy_fromio(msg + size1, - (void __iomem __force *)rb->rb_base, size2); - } else { - memcpy_fromio(msg, (void __iomem __force *)header, size); - } -} - -/** - * scif_rb_space - Query space available for writing to the RB - * @rb: ring buffer - * - * Return: size available for writing to RB in bytes. - */ -u32 scif_rb_space(struct scif_rb *rb) -{ - rb->current_read_offset = *rb->read_ptr; - /* - * Update from the HW read pointer only once the peer has exposed the - * new empty slot. This barrier is paired with the memory barrier - * scif_rb_update_read_ptr() - */ - mb(); - return scif_rb_ring_space(rb->current_write_offset, - rb->current_read_offset, rb->size); -} - -/** - * scif_rb_write - Write a message to the RB - * @rb: ring buffer - * @msg: buffer to send the message. Must be at least size bytes long - * @size: the size (in bytes) to be copied to the RB - * - * This API does not block if there isn't enough space in the RB. - * Returns: 0 on success or -ENOMEM on failure - */ -int scif_rb_write(struct scif_rb *rb, void *msg, u32 size) -{ - void *header; - - if (scif_rb_space(rb) < size) - return -ENOMEM; - header = rb->rb_base + rb->current_write_offset; - memcpy_torb(rb, header, msg, size); - /* - * Wait until scif_rb_commit(). Update the local ring - * buffer data, not the shared data until commit. - */ - rb->current_write_offset = - (rb->current_write_offset + size) & (rb->size - 1); - return 0; -} - -/** - * scif_rb_commit - To submit the message to let the peer fetch it - * @rb: ring buffer - */ -void scif_rb_commit(struct scif_rb *rb) -{ - /* - * We must ensure ordering between the all the data committed - * previously before we expose the new message to the peer by - * updating the write_ptr. This write barrier is paired with - * the read barrier in scif_rb_count(..) - */ - wmb(); - WRITE_ONCE(*rb->write_ptr, rb->current_write_offset); -#ifdef CONFIG_INTEL_MIC_CARD - /* - * X100 Si bug: For the case where a Core is performing an EXT_WR - * followed by a Doorbell Write, the Core must perform two EXT_WR to the - * same address with the same data before it does the Doorbell Write. - * This way, if ordering is violated for the Interrupt Message, it will - * fall just behind the first Posted associated with the first EXT_WR. - */ - WRITE_ONCE(*rb->write_ptr, rb->current_write_offset); -#endif -} - -/** - * scif_rb_get - To get next message from the ring buffer - * @rb: ring buffer - * @size: Number of bytes to be read - * - * Return: NULL if no bytes to be read from the ring buffer, otherwise the - * pointer to the next byte - */ -static void *scif_rb_get(struct scif_rb *rb, u32 size) -{ - void *header = NULL; - - if (scif_rb_count(rb, size) >= size) - header = rb->rb_base + rb->current_read_offset; - return header; -} - -/* - * scif_rb_get_next - Read from ring buffer. - * @rb: ring buffer - * @msg: buffer to hold the message. Must be at least size bytes long - * @size: Number of bytes to be read - * - * Return: number of bytes read if available bytes are >= size, otherwise - * returns zero. - */ -u32 scif_rb_get_next(struct scif_rb *rb, void *msg, u32 size) -{ - void *header = NULL; - int read_size = 0; - - header = scif_rb_get(rb, size); - if (header) { - u32 next_cmd_offset = - (rb->current_read_offset + size) & (rb->size - 1); - - read_size = size; - rb->current_read_offset = next_cmd_offset; - memcpy_fromrb(rb, header, msg, size); - } - return read_size; -} - -/** - * scif_rb_update_read_ptr - * @rb: ring buffer - */ -void scif_rb_update_read_ptr(struct scif_rb *rb) -{ - u32 new_offset; - - new_offset = rb->current_read_offset; - /* - * We must ensure ordering between the all the data committed or read - * previously before we expose the empty slot to the peer by updating - * the read_ptr. This barrier is paired with the memory barrier in - * scif_rb_space(..) - */ - mb(); - WRITE_ONCE(*rb->read_ptr, new_offset); -#ifdef CONFIG_INTEL_MIC_CARD - /* - * X100 Si Bug: For the case where a Core is performing an EXT_WR - * followed by a Doorbell Write, the Core must perform two EXT_WR to the - * same address with the same data before it does the Doorbell Write. - * This way, if ordering is violated for the Interrupt Message, it will - * fall just behind the first Posted associated with the first EXT_WR. - */ - WRITE_ONCE(*rb->read_ptr, new_offset); -#endif -} - -/** - * scif_rb_count - * @rb: ring buffer - * @size: Number of bytes expected to be read - * - * Return: number of bytes that can be read from the RB - */ -u32 scif_rb_count(struct scif_rb *rb, u32 size) -{ - if (scif_rb_ring_cnt(rb->current_write_offset, - rb->current_read_offset, - rb->size) < size) { - rb->current_write_offset = *rb->write_ptr; - /* - * Update from the HW write pointer if empty only once the peer - * has exposed the new message. This read barrier is paired - * with the write barrier in scif_rb_commit(..) - */ - smp_rmb(); - } - return scif_rb_ring_cnt(rb->current_write_offset, - rb->current_read_offset, - rb->size); -} diff --git a/drivers/misc/mic/scif/scif_rb.h b/drivers/misc/mic/scif/scif_rb.h deleted file mode 100644 index 166dffe3093d..000000000000 --- a/drivers/misc/mic/scif/scif_rb.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - */ -#ifndef SCIF_RB_H -#define SCIF_RB_H -/* - * This file describes a general purpose, byte based ring buffer. Writers to the - * ring buffer need to synchronize using a lock. The same is true for readers, - * although in practice, the ring buffer has a single reader. It is lockless - * between producer and consumer so it can handle being used across the PCIe - * bus. The ring buffer ensures that there are no reads across the PCIe bus for - * performance reasons. Two of these are used to form a single bidirectional - * queue-pair across PCIe. - */ -/* - * struct scif_rb - SCIF Ring Buffer - * - * @rb_base: The base of the memory used for storing RB messages - * @read_ptr: Pointer to the read offset - * @write_ptr: Pointer to the write offset - * @size: Size of the memory in rb_base - * @current_read_offset: Cached read offset for performance - * @current_write_offset: Cached write offset for performance - */ -struct scif_rb { - void *rb_base; - u32 *read_ptr; - u32 *write_ptr; - u32 size; - u32 current_read_offset; - u32 current_write_offset; -}; - -/* methods used by both */ -void scif_rb_init(struct scif_rb *rb, u32 *read_ptr, u32 *write_ptr, - void *rb_base, u8 size); -/* writer only methods */ -/* write a new command, then scif_rb_commit() */ -int scif_rb_write(struct scif_rb *rb, void *msg, u32 size); -/* after write(), then scif_rb_commit() */ -void scif_rb_commit(struct scif_rb *rb); -/* query space available for writing to a RB. */ -u32 scif_rb_space(struct scif_rb *rb); - -/* reader only methods */ -/* read a new message from the ring buffer of size bytes */ -u32 scif_rb_get_next(struct scif_rb *rb, void *msg, u32 size); -/* update the read pointer so that the space can be reused */ -void scif_rb_update_read_ptr(struct scif_rb *rb); -/* count the number of bytes that can be read */ -u32 scif_rb_count(struct scif_rb *rb, u32 size); -#endif diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c deleted file mode 100644 index 18fb9d8b8a4b..000000000000 --- a/drivers/misc/mic/scif/scif_rma.c +++ /dev/null @@ -1,1760 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include <linux/intel-iommu.h> -#include <linux/pagemap.h> -#include <linux/sched/mm.h> -#include <linux/sched/signal.h> - -#include "scif_main.h" -#include "scif_map.h" - -/* Used to skip ulimit checks for registrations with SCIF_MAP_KERNEL flag */ -#define SCIF_MAP_ULIMIT 0x40 - -bool scif_ulimit_check = 1; - -/** - * scif_rma_ep_init: - * @ep: end point - * - * Initialize RMA per EP data structures. - */ -void scif_rma_ep_init(struct scif_endpt *ep) -{ - struct scif_endpt_rma_info *rma = &ep->rma_info; - - mutex_init(&rma->rma_lock); - init_iova_domain(&rma->iovad, PAGE_SIZE, SCIF_IOVA_START_PFN); - spin_lock_init(&rma->tc_lock); - mutex_init(&rma->mmn_lock); - INIT_LIST_HEAD(&rma->reg_list); - INIT_LIST_HEAD(&rma->remote_reg_list); - atomic_set(&rma->tw_refcount, 0); - atomic_set(&rma->tcw_refcount, 0); - atomic_set(&rma->tcw_total_pages, 0); - atomic_set(&rma->fence_refcount, 0); - - rma->async_list_del = 0; - rma->dma_chan = NULL; - INIT_LIST_HEAD(&rma->mmn_list); - INIT_LIST_HEAD(&rma->vma_list); - init_waitqueue_head(&rma->markwq); -} - -/** - * scif_rma_ep_can_uninit: - * @ep: end point - * - * Returns 1 if an endpoint can be uninitialized and 0 otherwise. - */ -int scif_rma_ep_can_uninit(struct scif_endpt *ep) -{ - int ret = 0; - - mutex_lock(&ep->rma_info.rma_lock); - /* Destroy RMA Info only if both lists are empty */ - if (list_empty(&ep->rma_info.reg_list) && - list_empty(&ep->rma_info.remote_reg_list) && - list_empty(&ep->rma_info.mmn_list) && - !atomic_read(&ep->rma_info.tw_refcount) && - !atomic_read(&ep->rma_info.tcw_refcount) && - !atomic_read(&ep->rma_info.fence_refcount)) - ret = 1; - mutex_unlock(&ep->rma_info.rma_lock); - return ret; -} - -/** - * scif_create_pinned_pages: - * @nr_pages: number of pages in window - * @prot: read/write protection - * - * Allocate and prepare a set of pinned pages. - */ -static struct scif_pinned_pages * -scif_create_pinned_pages(int nr_pages, int prot) -{ - struct scif_pinned_pages *pin; - - might_sleep(); - pin = scif_zalloc(sizeof(*pin)); - if (!pin) - goto error; - - pin->pages = scif_zalloc(nr_pages * sizeof(*pin->pages)); - if (!pin->pages) - goto error_free_pinned_pages; - - pin->prot = prot; - pin->magic = SCIFEP_MAGIC; - return pin; - -error_free_pinned_pages: - scif_free(pin, sizeof(*pin)); -error: - return NULL; -} - -/** - * scif_destroy_pinned_pages: - * @pin: A set of pinned pages. - * - * Deallocate resources for pinned pages. - */ -static int scif_destroy_pinned_pages(struct scif_pinned_pages *pin) -{ - int j; - int writeable = pin->prot & SCIF_PROT_WRITE; - int kernel = SCIF_MAP_KERNEL & pin->map_flags; - - if (kernel) { - for (j = 0; j < pin->nr_pages; j++) { - if (pin->pages[j] && !kernel) { - if (writeable) - set_page_dirty_lock(pin->pages[j]); - put_page(pin->pages[j]); - } - } - } else - unpin_user_pages_dirty_lock(pin->pages, pin->nr_pages, - writeable); - scif_free(pin->pages, - pin->nr_pages * sizeof(*pin->pages)); - scif_free(pin, sizeof(*pin)); - return 0; -} - -/* - * scif_create_window: - * @ep: end point - * @nr_pages: number of pages - * @offset: registration offset - * @temp: true if a temporary window is being created - * - * Allocate and prepare a self registration window. - */ -struct scif_window *scif_create_window(struct scif_endpt *ep, int nr_pages, - s64 offset, bool temp) -{ - struct scif_window *window; - - might_sleep(); - window = scif_zalloc(sizeof(*window)); - if (!window) - goto error; - - window->dma_addr = scif_zalloc(nr_pages * sizeof(*window->dma_addr)); - if (!window->dma_addr) - goto error_free_window; - - window->num_pages = scif_zalloc(nr_pages * sizeof(*window->num_pages)); - if (!window->num_pages) - goto error_free_window; - - window->offset = offset; - window->ep = (u64)ep; - window->magic = SCIFEP_MAGIC; - window->reg_state = OP_IDLE; - init_waitqueue_head(&window->regwq); - window->unreg_state = OP_IDLE; - init_waitqueue_head(&window->unregwq); - INIT_LIST_HEAD(&window->list); - window->type = SCIF_WINDOW_SELF; - window->temp = temp; - return window; - -error_free_window: - scif_free(window->dma_addr, - nr_pages * sizeof(*window->dma_addr)); - scif_free(window, sizeof(*window)); -error: - return NULL; -} - -/** - * scif_destroy_incomplete_window: - * @ep: end point - * @window: registration window - * - * Deallocate resources for self window. - */ -static void scif_destroy_incomplete_window(struct scif_endpt *ep, - struct scif_window *window) -{ - int err; - int nr_pages = window->nr_pages; - struct scif_allocmsg *alloc = &window->alloc_handle; - struct scifmsg msg; - -retry: - /* Wait for a SCIF_ALLOC_GNT/REJ message */ - err = wait_event_timeout(alloc->allocwq, - alloc->state != OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - - mutex_lock(&ep->rma_info.rma_lock); - if (alloc->state == OP_COMPLETED) { - msg.uop = SCIF_FREE_VIRT; - msg.src = ep->port; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = window->alloc_handle.vaddr; - msg.payload[2] = (u64)window; - msg.payload[3] = SCIF_REGISTER; - _scif_nodeqp_send(ep->remote_dev, &msg); - } - mutex_unlock(&ep->rma_info.rma_lock); - - scif_free_window_offset(ep, window, window->offset); - scif_free(window->dma_addr, nr_pages * sizeof(*window->dma_addr)); - scif_free(window->num_pages, nr_pages * sizeof(*window->num_pages)); - scif_free(window, sizeof(*window)); -} - -/** - * scif_unmap_window: - * @remote_dev: SCIF remote device - * @window: registration window - * - * Delete any DMA mappings created for a registered self window - */ -void scif_unmap_window(struct scif_dev *remote_dev, struct scif_window *window) -{ - int j; - - if (scif_is_iommu_enabled() && !scifdev_self(remote_dev)) { - if (window->st) { - dma_unmap_sg(&remote_dev->sdev->dev, - window->st->sgl, window->st->nents, - DMA_BIDIRECTIONAL); - sg_free_table(window->st); - kfree(window->st); - window->st = NULL; - } - } else { - for (j = 0; j < window->nr_contig_chunks; j++) { - if (window->dma_addr[j]) { - scif_unmap_single(window->dma_addr[j], - remote_dev, - window->num_pages[j] << - PAGE_SHIFT); - window->dma_addr[j] = 0x0; - } - } - } -} - -static inline struct mm_struct *__scif_acquire_mm(void) -{ - if (scif_ulimit_check) - return get_task_mm(current); - return NULL; -} - -static inline void __scif_release_mm(struct mm_struct *mm) -{ - if (mm) - mmput(mm); -} - -static inline int -__scif_dec_pinned_vm_lock(struct mm_struct *mm, - int nr_pages) -{ - if (!mm || !nr_pages || !scif_ulimit_check) - return 0; - - atomic64_sub(nr_pages, &mm->pinned_vm); - return 0; -} - -static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm, - int nr_pages) -{ - unsigned long locked, lock_limit; - - if (!mm || !nr_pages || !scif_ulimit_check) - return 0; - - lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - locked = atomic64_add_return(nr_pages, &mm->pinned_vm); - - if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { - atomic64_sub(nr_pages, &mm->pinned_vm); - dev_err(scif_info.mdev.this_device, - "locked(%lu) > lock_limit(%lu)\n", - locked, lock_limit); - return -ENOMEM; - } - return 0; -} - -/** - * scif_destroy_window: - * @ep: end point - * @window: registration window - * - * Deallocate resources for self window. - */ -int scif_destroy_window(struct scif_endpt *ep, struct scif_window *window) -{ - int j; - struct scif_pinned_pages *pinned_pages = window->pinned_pages; - int nr_pages = window->nr_pages; - - might_sleep(); - if (!window->temp && window->mm) { - __scif_dec_pinned_vm_lock(window->mm, window->nr_pages); - __scif_release_mm(window->mm); - window->mm = NULL; - } - - scif_free_window_offset(ep, window, window->offset); - scif_unmap_window(ep->remote_dev, window); - /* - * Decrement references for this set of pinned pages from - * this window. - */ - j = atomic_sub_return(1, &pinned_pages->ref_count); - if (j < 0) - dev_err(scif_info.mdev.this_device, - "%s %d incorrect ref count %d\n", - __func__, __LINE__, j); - /* - * If the ref count for pinned_pages is zero then someone - * has already called scif_unpin_pages() for it and we should - * destroy the page cache. - */ - if (!j) - scif_destroy_pinned_pages(window->pinned_pages); - scif_free(window->dma_addr, nr_pages * sizeof(*window->dma_addr)); - scif_free(window->num_pages, nr_pages * sizeof(*window->num_pages)); - window->magic = 0; - scif_free(window, sizeof(*window)); - return 0; -} - -/** - * scif_create_remote_lookup: - * @remote_dev: SCIF remote device - * @window: remote window - * - * Allocate and prepare lookup entries for the remote - * end to copy over the physical addresses. - * Returns 0 on success and appropriate errno on failure. - */ -static int scif_create_remote_lookup(struct scif_dev *remote_dev, - struct scif_window *window) -{ - int i, j, err = 0; - int nr_pages = window->nr_pages; - bool vmalloc_dma_phys, vmalloc_num_pages; - - might_sleep(); - /* Map window */ - err = scif_map_single(&window->mapped_offset, - window, remote_dev, sizeof(*window)); - if (err) - goto error_window; - - /* Compute the number of lookup entries. 21 == 2MB Shift */ - window->nr_lookup = ALIGN(nr_pages * PAGE_SIZE, - ((2) * 1024 * 1024)) >> 21; - - window->dma_addr_lookup.lookup = - scif_alloc_coherent(&window->dma_addr_lookup.offset, - remote_dev, window->nr_lookup * - sizeof(*window->dma_addr_lookup.lookup), - GFP_KERNEL | __GFP_ZERO); - if (!window->dma_addr_lookup.lookup) { - err = -ENOMEM; - goto error_window; - } - - window->num_pages_lookup.lookup = - scif_alloc_coherent(&window->num_pages_lookup.offset, - remote_dev, window->nr_lookup * - sizeof(*window->num_pages_lookup.lookup), - GFP_KERNEL | __GFP_ZERO); - if (!window->num_pages_lookup.lookup) { - err = -ENOMEM; - goto error_window; - } - - vmalloc_dma_phys = is_vmalloc_addr(&window->dma_addr[0]); - vmalloc_num_pages = is_vmalloc_addr(&window->num_pages[0]); - - /* Now map each of the pages containing physical addresses */ - for (i = 0, j = 0; i < nr_pages; i += SCIF_NR_ADDR_IN_PAGE, j++) { - err = scif_map_page(&window->dma_addr_lookup.lookup[j], - vmalloc_dma_phys ? - vmalloc_to_page(&window->dma_addr[i]) : - virt_to_page(&window->dma_addr[i]), - remote_dev); - if (err) - goto error_window; - err = scif_map_page(&window->num_pages_lookup.lookup[j], - vmalloc_num_pages ? - vmalloc_to_page(&window->num_pages[i]) : - virt_to_page(&window->num_pages[i]), - remote_dev); - if (err) - goto error_window; - } - return 0; -error_window: - return err; -} - -/** - * scif_destroy_remote_lookup: - * @remote_dev: SCIF remote device - * @window: remote window - * - * Destroy lookup entries used for the remote - * end to copy over the physical addresses. - */ -static void scif_destroy_remote_lookup(struct scif_dev *remote_dev, - struct scif_window *window) -{ - int i, j; - - if (window->nr_lookup) { - struct scif_rma_lookup *lup = &window->dma_addr_lookup; - struct scif_rma_lookup *npup = &window->num_pages_lookup; - - for (i = 0, j = 0; i < window->nr_pages; - i += SCIF_NR_ADDR_IN_PAGE, j++) { - if (lup->lookup && lup->lookup[j]) - scif_unmap_single(lup->lookup[j], - remote_dev, - PAGE_SIZE); - if (npup->lookup && npup->lookup[j]) - scif_unmap_single(npup->lookup[j], - remote_dev, - PAGE_SIZE); - } - if (lup->lookup) - scif_free_coherent(lup->lookup, lup->offset, - remote_dev, window->nr_lookup * - sizeof(*lup->lookup)); - if (npup->lookup) - scif_free_coherent(npup->lookup, npup->offset, - remote_dev, window->nr_lookup * - sizeof(*npup->lookup)); - if (window->mapped_offset) - scif_unmap_single(window->mapped_offset, - remote_dev, sizeof(*window)); - window->nr_lookup = 0; - } -} - -/** - * scif_create_remote_window: - * @scifdev: SCIF device - * @nr_pages: number of pages in window - * - * Allocate and prepare a remote registration window. - */ -static struct scif_window * -scif_create_remote_window(struct scif_dev *scifdev, int nr_pages) -{ - struct scif_window *window; - - might_sleep(); - window = scif_zalloc(sizeof(*window)); - if (!window) - goto error_ret; - - window->magic = SCIFEP_MAGIC; - window->nr_pages = nr_pages; - - window->dma_addr = scif_zalloc(nr_pages * sizeof(*window->dma_addr)); - if (!window->dma_addr) - goto error_window; - - window->num_pages = scif_zalloc(nr_pages * - sizeof(*window->num_pages)); - if (!window->num_pages) - goto error_window; - - if (scif_create_remote_lookup(scifdev, window)) - goto error_window; - - window->type = SCIF_WINDOW_PEER; - window->unreg_state = OP_IDLE; - INIT_LIST_HEAD(&window->list); - return window; -error_window: - scif_destroy_remote_window(window); -error_ret: - return NULL; -} - -/** - * scif_destroy_remote_window: - * @window: remote registration window - * - * Deallocate resources for remote window. - */ -void -scif_destroy_remote_window(struct scif_window *window) -{ - scif_free(window->dma_addr, window->nr_pages * - sizeof(*window->dma_addr)); - scif_free(window->num_pages, window->nr_pages * - sizeof(*window->num_pages)); - window->magic = 0; - scif_free(window, sizeof(*window)); -} - -/** - * scif_iommu_map: create DMA mappings if the IOMMU is enabled - * @remote_dev: SCIF remote device - * @window: remote registration window - * - * Map the physical pages using dma_map_sg(..) and then detect the number - * of contiguous DMA mappings allocated - */ -static int scif_iommu_map(struct scif_dev *remote_dev, - struct scif_window *window) -{ - struct scatterlist *sg; - int i, err; - scif_pinned_pages_t pin = window->pinned_pages; - - window->st = kzalloc(sizeof(*window->st), GFP_KERNEL); - if (!window->st) - return -ENOMEM; - - err = sg_alloc_table(window->st, window->nr_pages, GFP_KERNEL); - if (err) - return err; - - for_each_sg(window->st->sgl, sg, window->st->nents, i) - sg_set_page(sg, pin->pages[i], PAGE_SIZE, 0x0); - - err = dma_map_sg(&remote_dev->sdev->dev, window->st->sgl, - window->st->nents, DMA_BIDIRECTIONAL); - if (!err) - return -ENOMEM; - /* Detect contiguous ranges of DMA mappings */ - sg = window->st->sgl; - for (i = 0; sg; i++) { - dma_addr_t last_da; - - window->dma_addr[i] = sg_dma_address(sg); - window->num_pages[i] = sg_dma_len(sg) >> PAGE_SHIFT; - last_da = sg_dma_address(sg) + sg_dma_len(sg); - while ((sg = sg_next(sg)) && sg_dma_address(sg) == last_da) { - window->num_pages[i] += - (sg_dma_len(sg) >> PAGE_SHIFT); - last_da = window->dma_addr[i] + - sg_dma_len(sg); - } - window->nr_contig_chunks++; - } - return 0; -} - -/** - * scif_map_window: - * @remote_dev: SCIF remote device - * @window: self registration window - * - * Map pages of a window into the aperture/PCI. - * Also determine addresses required for DMA. - */ -int -scif_map_window(struct scif_dev *remote_dev, struct scif_window *window) -{ - int i, j, k, err = 0, nr_contig_pages; - scif_pinned_pages_t pin; - phys_addr_t phys_prev, phys_curr; - - might_sleep(); - - pin = window->pinned_pages; - - if (intel_iommu_enabled && !scifdev_self(remote_dev)) - return scif_iommu_map(remote_dev, window); - - for (i = 0, j = 0; i < window->nr_pages; i += nr_contig_pages, j++) { - phys_prev = page_to_phys(pin->pages[i]); - nr_contig_pages = 1; - - /* Detect physically contiguous chunks */ - for (k = i + 1; k < window->nr_pages; k++) { - phys_curr = page_to_phys(pin->pages[k]); - if (phys_curr != (phys_prev + PAGE_SIZE)) - break; - phys_prev = phys_curr; - nr_contig_pages++; - } - window->num_pages[j] = nr_contig_pages; - window->nr_contig_chunks++; - if (scif_is_mgmt_node()) { - /* - * Management node has to deal with SMPT on X100 and - * hence the DMA mapping is required - */ - err = scif_map_single(&window->dma_addr[j], - phys_to_virt(page_to_phys( - pin->pages[i])), - remote_dev, - nr_contig_pages << PAGE_SHIFT); - if (err) - return err; - } else { - window->dma_addr[j] = page_to_phys(pin->pages[i]); - } - } - return err; -} - -/** - * scif_send_scif_unregister: - * @ep: end point - * @window: self registration window - * - * Send a SCIF_UNREGISTER message. - */ -static int scif_send_scif_unregister(struct scif_endpt *ep, - struct scif_window *window) -{ - struct scifmsg msg; - - msg.uop = SCIF_UNREGISTER; - msg.src = ep->port; - msg.payload[0] = window->alloc_handle.vaddr; - msg.payload[1] = (u64)window; - return scif_nodeqp_send(ep->remote_dev, &msg); -} - -/** - * scif_unregister_window: - * @window: self registration window - * - * Send an unregistration request and wait for a response. - */ -int scif_unregister_window(struct scif_window *window) -{ - int err = 0; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - bool send_msg = false; - - might_sleep(); - switch (window->unreg_state) { - case OP_IDLE: - { - window->unreg_state = OP_IN_PROGRESS; - send_msg = true; - } - fallthrough; - case OP_IN_PROGRESS: - { - scif_get_window(window, 1); - mutex_unlock(&ep->rma_info.rma_lock); - if (send_msg) { - err = scif_send_scif_unregister(ep, window); - if (err) { - window->unreg_state = OP_COMPLETED; - goto done; - } - } else { - /* Return ENXIO since unregistration is in progress */ - mutex_lock(&ep->rma_info.rma_lock); - return -ENXIO; - } -retry: - /* Wait for a SCIF_UNREGISTER_(N)ACK message */ - err = wait_event_timeout(window->unregwq, - window->unreg_state != OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - if (!err) { - err = -ENODEV; - window->unreg_state = OP_COMPLETED; - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - } - if (err > 0) - err = 0; -done: - mutex_lock(&ep->rma_info.rma_lock); - scif_put_window(window, 1); - break; - } - case OP_FAILED: - { - if (!scifdev_alive(ep)) { - err = -ENODEV; - window->unreg_state = OP_COMPLETED; - } - break; - } - case OP_COMPLETED: - break; - default: - err = -ENODEV; - } - - if (window->unreg_state == OP_COMPLETED && window->ref_count) - scif_put_window(window, window->nr_pages); - - if (!window->ref_count) { - atomic_inc(&ep->rma_info.tw_refcount); - list_del_init(&window->list); - scif_free_window_offset(ep, window, window->offset); - mutex_unlock(&ep->rma_info.rma_lock); - if ((!!(window->pinned_pages->map_flags & SCIF_MAP_KERNEL)) && - scifdev_alive(ep)) { - scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan); - } else { - if (!__scif_dec_pinned_vm_lock(window->mm, - window->nr_pages)) { - __scif_release_mm(window->mm); - window->mm = NULL; - } - } - scif_queue_for_cleanup(window, &scif_info.rma); - mutex_lock(&ep->rma_info.rma_lock); - } - return err; -} - -/** - * scif_send_alloc_request: - * @ep: end point - * @window: self registration window - * - * Send a remote window allocation request - */ -static int scif_send_alloc_request(struct scif_endpt *ep, - struct scif_window *window) -{ - struct scifmsg msg; - struct scif_allocmsg *alloc = &window->alloc_handle; - - /* Set up the Alloc Handle */ - alloc->state = OP_IN_PROGRESS; - init_waitqueue_head(&alloc->allocwq); - - /* Send out an allocation request */ - msg.uop = SCIF_ALLOC_REQ; - msg.payload[1] = window->nr_pages; - msg.payload[2] = (u64)&window->alloc_handle; - return _scif_nodeqp_send(ep->remote_dev, &msg); -} - -/** - * scif_prep_remote_window: - * @ep: end point - * @window: self registration window - * - * Send a remote window allocation request, wait for an allocation response, - * and prepares the remote window by copying over the page lists - */ -static int scif_prep_remote_window(struct scif_endpt *ep, - struct scif_window *window) -{ - struct scifmsg msg; - struct scif_window *remote_window; - struct scif_allocmsg *alloc = &window->alloc_handle; - dma_addr_t *dma_phys_lookup, *tmp, *num_pages_lookup, *tmp1; - int i = 0, j = 0; - int nr_contig_chunks, loop_nr_contig_chunks; - int remaining_nr_contig_chunks, nr_lookup; - int err, map_err; - - map_err = scif_map_window(ep->remote_dev, window); - if (map_err) - dev_err(&ep->remote_dev->sdev->dev, - "%s %d map_err %d\n", __func__, __LINE__, map_err); - remaining_nr_contig_chunks = window->nr_contig_chunks; - nr_contig_chunks = window->nr_contig_chunks; -retry: - /* Wait for a SCIF_ALLOC_GNT/REJ message */ - err = wait_event_timeout(alloc->allocwq, - alloc->state != OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - mutex_lock(&ep->rma_info.rma_lock); - /* Synchronize with the thread waking up allocwq */ - mutex_unlock(&ep->rma_info.rma_lock); - if (!err && scifdev_alive(ep)) - goto retry; - - if (!err) - err = -ENODEV; - - if (err > 0) - err = 0; - else - return err; - - /* Bail out. The remote end rejected this request */ - if (alloc->state == OP_FAILED) - return -ENOMEM; - - if (map_err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, map_err); - msg.uop = SCIF_FREE_VIRT; - msg.src = ep->port; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = window->alloc_handle.vaddr; - msg.payload[2] = (u64)window; - msg.payload[3] = SCIF_REGISTER; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = _scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - return err; - } - - remote_window = scif_ioremap(alloc->phys_addr, sizeof(*window), - ep->remote_dev); - - /* Compute the number of lookup entries. 21 == 2MB Shift */ - nr_lookup = ALIGN(nr_contig_chunks, SCIF_NR_ADDR_IN_PAGE) - >> ilog2(SCIF_NR_ADDR_IN_PAGE); - - dma_phys_lookup = - scif_ioremap(remote_window->dma_addr_lookup.offset, - nr_lookup * - sizeof(*remote_window->dma_addr_lookup.lookup), - ep->remote_dev); - num_pages_lookup = - scif_ioremap(remote_window->num_pages_lookup.offset, - nr_lookup * - sizeof(*remote_window->num_pages_lookup.lookup), - ep->remote_dev); - - while (remaining_nr_contig_chunks) { - loop_nr_contig_chunks = min_t(int, remaining_nr_contig_chunks, - (int)SCIF_NR_ADDR_IN_PAGE); - /* #1/2 - Copy physical addresses over to the remote side */ - - /* #2/2 - Copy DMA addresses (addresses that are fed into the - * DMA engine) We transfer bus addresses which are then - * converted into a MIC physical address on the remote - * side if it is a MIC, if the remote node is a mgmt node we - * transfer the MIC physical address - */ - tmp = scif_ioremap(dma_phys_lookup[j], - loop_nr_contig_chunks * - sizeof(*window->dma_addr), - ep->remote_dev); - tmp1 = scif_ioremap(num_pages_lookup[j], - loop_nr_contig_chunks * - sizeof(*window->num_pages), - ep->remote_dev); - if (scif_is_mgmt_node()) { - memcpy_toio((void __force __iomem *)tmp, - &window->dma_addr[i], loop_nr_contig_chunks - * sizeof(*window->dma_addr)); - memcpy_toio((void __force __iomem *)tmp1, - &window->num_pages[i], loop_nr_contig_chunks - * sizeof(*window->num_pages)); - } else { - if (scifdev_is_p2p(ep->remote_dev)) { - /* - * add remote node's base address for this node - * to convert it into a MIC address - */ - int m; - dma_addr_t dma_addr; - - for (m = 0; m < loop_nr_contig_chunks; m++) { - dma_addr = window->dma_addr[i + m] + - ep->remote_dev->base_addr; - writeq(dma_addr, - (void __force __iomem *)&tmp[m]); - } - memcpy_toio((void __force __iomem *)tmp1, - &window->num_pages[i], - loop_nr_contig_chunks - * sizeof(*window->num_pages)); - } else { - /* Mgmt node or loopback - transfer DMA - * addresses as is, this is the same as a - * MIC physical address (we use the dma_addr - * and not the phys_addr array since the - * phys_addr is only setup if there is a mmap() - * request from the mgmt node) - */ - memcpy_toio((void __force __iomem *)tmp, - &window->dma_addr[i], - loop_nr_contig_chunks * - sizeof(*window->dma_addr)); - memcpy_toio((void __force __iomem *)tmp1, - &window->num_pages[i], - loop_nr_contig_chunks * - sizeof(*window->num_pages)); - } - } - remaining_nr_contig_chunks -= loop_nr_contig_chunks; - i += loop_nr_contig_chunks; - j++; - scif_iounmap(tmp, loop_nr_contig_chunks * - sizeof(*window->dma_addr), ep->remote_dev); - scif_iounmap(tmp1, loop_nr_contig_chunks * - sizeof(*window->num_pages), ep->remote_dev); - } - - /* Prepare the remote window for the peer */ - remote_window->peer_window = (u64)window; - remote_window->offset = window->offset; - remote_window->prot = window->prot; - remote_window->nr_contig_chunks = nr_contig_chunks; - remote_window->ep = ep->remote_ep; - scif_iounmap(num_pages_lookup, - nr_lookup * - sizeof(*remote_window->num_pages_lookup.lookup), - ep->remote_dev); - scif_iounmap(dma_phys_lookup, - nr_lookup * - sizeof(*remote_window->dma_addr_lookup.lookup), - ep->remote_dev); - scif_iounmap(remote_window, sizeof(*remote_window), ep->remote_dev); - window->peer_window = alloc->vaddr; - return err; -} - -/** - * scif_send_scif_register: - * @ep: end point - * @window: self registration window - * - * Send a SCIF_REGISTER message if EP is connected and wait for a - * SCIF_REGISTER_(N)ACK message else send a SCIF_FREE_VIRT - * message so that the peer can free its remote window allocated earlier. - */ -static int scif_send_scif_register(struct scif_endpt *ep, - struct scif_window *window) -{ - int err = 0; - struct scifmsg msg; - - msg.src = ep->port; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = window->alloc_handle.vaddr; - msg.payload[2] = (u64)window; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) { - msg.uop = SCIF_REGISTER; - window->reg_state = OP_IN_PROGRESS; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - spin_unlock(&ep->lock); - if (!err) { -retry: - /* Wait for a SCIF_REGISTER_(N)ACK message */ - err = wait_event_timeout(window->regwq, - window->reg_state != - OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - err = !err ? -ENODEV : 0; - if (window->reg_state == OP_FAILED) - err = -ENOTCONN; - } - } else { - msg.uop = SCIF_FREE_VIRT; - msg.payload[3] = SCIF_REGISTER; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - spin_unlock(&ep->lock); - if (!err) - err = -ENOTCONN; - } - return err; -} - -/** - * scif_get_window_offset: - * @ep: end point descriptor - * @flags: flags - * @offset: offset hint - * @num_pages: number of pages - * @out_offset: computed offset returned by reference. - * - * Compute/Claim a new offset for this EP. - */ -int scif_get_window_offset(struct scif_endpt *ep, int flags, s64 offset, - int num_pages, s64 *out_offset) -{ - s64 page_index; - struct iova *iova_ptr; - int err = 0; - - if (flags & SCIF_MAP_FIXED) { - page_index = SCIF_IOVA_PFN(offset); - iova_ptr = reserve_iova(&ep->rma_info.iovad, page_index, - page_index + num_pages - 1); - if (!iova_ptr) - err = -EADDRINUSE; - } else { - iova_ptr = alloc_iova(&ep->rma_info.iovad, num_pages, - SCIF_DMA_63BIT_PFN - 1, 0); - if (!iova_ptr) - err = -ENOMEM; - } - if (!err) - *out_offset = (iova_ptr->pfn_lo) << PAGE_SHIFT; - return err; -} - -/** - * scif_free_window_offset: - * @ep: end point descriptor - * @window: registration window - * @offset: Offset to be freed - * - * Free offset for this EP. The callee is supposed to grab - * the RMA mutex before calling this API. - */ -void scif_free_window_offset(struct scif_endpt *ep, - struct scif_window *window, s64 offset) -{ - if ((window && !window->offset_freed) || !window) { - free_iova(&ep->rma_info.iovad, offset >> PAGE_SHIFT); - if (window) - window->offset_freed = true; - } -} - -/** - * scif_alloc_req: Respond to SCIF_ALLOC_REQ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side is requesting a memory allocation. - */ -void scif_alloc_req(struct scif_dev *scifdev, struct scifmsg *msg) -{ - int err; - struct scif_window *window = NULL; - int nr_pages = msg->payload[1]; - - window = scif_create_remote_window(scifdev, nr_pages); - if (!window) { - err = -ENOMEM; - goto error; - } - - /* The peer's allocation request is granted */ - msg->uop = SCIF_ALLOC_GNT; - msg->payload[0] = (u64)window; - msg->payload[1] = window->mapped_offset; - err = scif_nodeqp_send(scifdev, msg); - if (err) - scif_destroy_remote_window(window); - return; -error: - /* The peer's allocation request is rejected */ - dev_err(&scifdev->sdev->dev, - "%s %d error %d alloc_ptr %p nr_pages 0x%x\n", - __func__, __LINE__, err, window, nr_pages); - msg->uop = SCIF_ALLOC_REJ; - scif_nodeqp_send(scifdev, msg); -} - -/** - * scif_alloc_gnt_rej: Respond to SCIF_ALLOC_GNT/REJ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side responded to a memory allocation. - */ -void scif_alloc_gnt_rej(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_allocmsg *handle = (struct scif_allocmsg *)msg->payload[2]; - struct scif_window *window = container_of(handle, struct scif_window, - alloc_handle); - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - handle->vaddr = msg->payload[0]; - handle->phys_addr = msg->payload[1]; - if (msg->uop == SCIF_ALLOC_GNT) - handle->state = OP_COMPLETED; - else - handle->state = OP_FAILED; - wake_up(&handle->allocwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_free_virt: Respond to SCIF_FREE_VIRT interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Free up memory kmalloc'd earlier. - */ -void scif_free_virt(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = (struct scif_window *)msg->payload[1]; - - scif_destroy_remote_window(window); -} - -static void -scif_fixup_aper_base(struct scif_dev *dev, struct scif_window *window) -{ - int j; - struct scif_hw_dev *sdev = dev->sdev; - phys_addr_t apt_base = 0; - - /* - * Add the aperture base if the DMA address is not card relative - * since the DMA addresses need to be an offset into the bar - */ - if (!scifdev_self(dev) && window->type == SCIF_WINDOW_PEER && - sdev->aper && !sdev->card_rel_da) - apt_base = sdev->aper->pa; - else - return; - - for (j = 0; j < window->nr_contig_chunks; j++) { - if (window->num_pages[j]) - window->dma_addr[j] += apt_base; - else - break; - } -} - -/** - * scif_recv_reg: Respond to SCIF_REGISTER interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Update remote window list with a new registered window. - */ -void scif_recv_reg(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_window *window = - (struct scif_window *)msg->payload[1]; - - mutex_lock(&ep->rma_info.rma_lock); - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) { - msg->uop = SCIF_REGISTER_ACK; - scif_nodeqp_send(ep->remote_dev, msg); - scif_fixup_aper_base(ep->remote_dev, window); - /* No further failures expected. Insert new window */ - scif_insert_window(window, &ep->rma_info.remote_reg_list); - } else { - msg->uop = SCIF_REGISTER_NACK; - scif_nodeqp_send(ep->remote_dev, msg); - } - spin_unlock(&ep->lock); - mutex_unlock(&ep->rma_info.rma_lock); - /* free up any lookup resources now that page lists are transferred */ - scif_destroy_remote_lookup(ep->remote_dev, window); - /* - * We could not insert the window but we need to - * destroy the window. - */ - if (msg->uop == SCIF_REGISTER_NACK) - scif_destroy_remote_window(window); -} - -/** - * scif_recv_unreg: Respond to SCIF_UNREGISTER interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remove window from remote registration list; - */ -void scif_recv_unreg(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_rma_req req; - struct scif_window *window = NULL; - struct scif_window *recv_window = - (struct scif_window *)msg->payload[0]; - struct scif_endpt *ep; - int del_window = 0; - - ep = (struct scif_endpt *)recv_window->ep; - req.out_window = &window; - req.offset = recv_window->offset; - req.prot = 0; - req.nr_bytes = recv_window->nr_pages << PAGE_SHIFT; - req.type = SCIF_WINDOW_FULL; - req.head = &ep->rma_info.remote_reg_list; - msg->payload[0] = ep->remote_ep; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - if (scif_query_window(&req)) { - dev_err(&scifdev->sdev->dev, - "%s %d -ENXIO\n", __func__, __LINE__); - msg->uop = SCIF_UNREGISTER_ACK; - goto error; - } - if (window) { - if (window->ref_count) - scif_put_window(window, window->nr_pages); - else - dev_err(&scifdev->sdev->dev, - "%s %d ref count should be +ve\n", - __func__, __LINE__); - window->unreg_state = OP_COMPLETED; - if (!window->ref_count) { - msg->uop = SCIF_UNREGISTER_ACK; - atomic_inc(&ep->rma_info.tw_refcount); - ep->rma_info.async_list_del = 1; - list_del_init(&window->list); - del_window = 1; - } else { - /* NACK! There are valid references to this window */ - msg->uop = SCIF_UNREGISTER_NACK; - } - } else { - /* The window did not make its way to the list at all. ACK */ - msg->uop = SCIF_UNREGISTER_ACK; - scif_destroy_remote_window(recv_window); - } -error: - mutex_unlock(&ep->rma_info.rma_lock); - if (del_window) - scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan); - scif_nodeqp_send(ep->remote_dev, msg); - if (del_window) - scif_queue_for_cleanup(window, &scif_info.rma); -} - -/** - * scif_recv_reg_ack: Respond to SCIF_REGISTER_ACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to complete registration. - */ -void scif_recv_reg_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[2]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->reg_state = OP_COMPLETED; - wake_up(&window->regwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_recv_reg_nack: Respond to SCIF_REGISTER_NACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to inform it that registration - * cannot be completed. - */ -void scif_recv_reg_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[2]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->reg_state = OP_FAILED; - wake_up(&window->regwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_recv_unreg_ack: Respond to SCIF_UNREGISTER_ACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to complete unregistration. - */ -void scif_recv_unreg_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[1]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->unreg_state = OP_COMPLETED; - wake_up(&window->unregwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_recv_unreg_nack: Respond to SCIF_UNREGISTER_NACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to inform it that unregistration - * cannot be completed immediately. - */ -void scif_recv_unreg_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[1]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->unreg_state = OP_FAILED; - wake_up(&window->unregwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -int __scif_pin_pages(void *addr, size_t len, int *out_prot, - int map_flags, scif_pinned_pages_t *pages) -{ - struct scif_pinned_pages *pinned_pages; - int nr_pages, err = 0, i; - bool vmalloc_addr = false; - bool try_upgrade = false; - int prot = *out_prot; - int ulimit = 0; - struct mm_struct *mm = NULL; - - /* Unsupported flags */ - if (map_flags & ~(SCIF_MAP_KERNEL | SCIF_MAP_ULIMIT)) - return -EINVAL; - ulimit = !!(map_flags & SCIF_MAP_ULIMIT); - - /* Unsupported protection requested */ - if (prot & ~(SCIF_PROT_READ | SCIF_PROT_WRITE)) - return -EINVAL; - - /* addr/len must be page aligned. len should be non zero */ - if (!len || - (ALIGN((u64)addr, PAGE_SIZE) != (u64)addr) || - (ALIGN((u64)len, PAGE_SIZE) != (u64)len)) - return -EINVAL; - - might_sleep(); - - nr_pages = len >> PAGE_SHIFT; - - /* Allocate a set of pinned pages */ - pinned_pages = scif_create_pinned_pages(nr_pages, prot); - if (!pinned_pages) - return -ENOMEM; - - if (map_flags & SCIF_MAP_KERNEL) { - if (is_vmalloc_addr(addr)) - vmalloc_addr = true; - - for (i = 0; i < nr_pages; i++) { - if (vmalloc_addr) - pinned_pages->pages[i] = - vmalloc_to_page(addr + (i * PAGE_SIZE)); - else - pinned_pages->pages[i] = - virt_to_page(addr + (i * PAGE_SIZE)); - } - pinned_pages->nr_pages = nr_pages; - pinned_pages->map_flags = SCIF_MAP_KERNEL; - } else { - /* - * SCIF supports registration caching. If a registration has - * been requested with read only permissions, then we try - * to pin the pages with RW permissions so that a subsequent - * transfer with RW permission can hit the cache instead of - * invalidating it. If the upgrade fails with RW then we - * revert back to R permission and retry - */ - if (prot == SCIF_PROT_READ) - try_upgrade = true; - prot |= SCIF_PROT_WRITE; -retry: - mm = current->mm; - if (ulimit) { - err = __scif_check_inc_pinned_vm(mm, nr_pages); - if (err) { - pinned_pages->nr_pages = 0; - goto error_unmap; - } - } - - pinned_pages->nr_pages = pin_user_pages_fast( - (u64)addr, - nr_pages, - (prot & SCIF_PROT_WRITE) ? FOLL_WRITE : 0, - pinned_pages->pages); - if (nr_pages != pinned_pages->nr_pages) { - if (pinned_pages->nr_pages < 0) - pinned_pages->nr_pages = 0; - if (try_upgrade) { - if (ulimit) - __scif_dec_pinned_vm_lock(mm, nr_pages); - /* Roll back any pinned pages */ - unpin_user_pages(pinned_pages->pages, - pinned_pages->nr_pages); - prot &= ~SCIF_PROT_WRITE; - try_upgrade = false; - goto retry; - } - } - pinned_pages->map_flags = 0; - } - - if (pinned_pages->nr_pages < nr_pages) { - err = -EFAULT; - goto dec_pinned; - } - - *out_prot = prot; - atomic_set(&pinned_pages->ref_count, 1); - *pages = pinned_pages; - return err; -dec_pinned: - if (ulimit) - __scif_dec_pinned_vm_lock(mm, nr_pages); - /* Something went wrong! Rollback */ -error_unmap: - scif_destroy_pinned_pages(pinned_pages); - *pages = NULL; - dev_dbg(scif_info.mdev.this_device, - "%s %d err %d len 0x%lx\n", __func__, __LINE__, err, len); - return err; -} - -int scif_pin_pages(void *addr, size_t len, int prot, - int map_flags, scif_pinned_pages_t *pages) -{ - return __scif_pin_pages(addr, len, &prot, map_flags, pages); -} -EXPORT_SYMBOL_GPL(scif_pin_pages); - -int scif_unpin_pages(scif_pinned_pages_t pinned_pages) -{ - int err = 0, ret; - - if (!pinned_pages || SCIFEP_MAGIC != pinned_pages->magic) - return -EINVAL; - - ret = atomic_sub_return(1, &pinned_pages->ref_count); - if (ret < 0) { - dev_err(scif_info.mdev.this_device, - "%s %d scif_unpin_pages called without pinning? rc %d\n", - __func__, __LINE__, ret); - return -EINVAL; - } - /* - * Destroy the window if the ref count for this set of pinned - * pages has dropped to zero. If it is positive then there is - * a valid registered window which is backed by these pages and - * it will be destroyed once all such windows are unregistered. - */ - if (!ret) - err = scif_destroy_pinned_pages(pinned_pages); - - return err; -} -EXPORT_SYMBOL_GPL(scif_unpin_pages); - -static inline void -scif_insert_local_window(struct scif_window *window, struct scif_endpt *ep) -{ - mutex_lock(&ep->rma_info.rma_lock); - scif_insert_window(window, &ep->rma_info.reg_list); - mutex_unlock(&ep->rma_info.rma_lock); -} - -off_t scif_register_pinned_pages(scif_epd_t epd, - scif_pinned_pages_t pinned_pages, - off_t offset, int map_flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - s64 computed_offset; - struct scif_window *window; - int err; - size_t len; - struct device *spdev; - - /* Unsupported flags */ - if (map_flags & ~SCIF_MAP_FIXED) - return -EINVAL; - - len = pinned_pages->nr_pages << PAGE_SHIFT; - - /* - * Offset is not page aligned/negative or offset+len - * wraps around with SCIF_MAP_FIXED. - */ - if ((map_flags & SCIF_MAP_FIXED) && - ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset < 0) || - (len > LONG_MAX - offset))) - return -EINVAL; - - might_sleep(); - - err = scif_verify_epd(ep); - if (err) - return err; - /* - * It is an error to pass pinned_pages to scif_register_pinned_pages() - * after calling scif_unpin_pages(). - */ - if (!atomic_add_unless(&pinned_pages->ref_count, 1, 0)) - return -EINVAL; - - /* Compute the offset for this registration */ - err = scif_get_window_offset(ep, map_flags, offset, - len, &computed_offset); - if (err) { - atomic_sub(1, &pinned_pages->ref_count); - return err; - } - - /* Allocate and prepare self registration window */ - window = scif_create_window(ep, pinned_pages->nr_pages, - computed_offset, false); - if (!window) { - atomic_sub(1, &pinned_pages->ref_count); - scif_free_window_offset(ep, NULL, computed_offset); - return -ENOMEM; - } - - window->pinned_pages = pinned_pages; - window->nr_pages = pinned_pages->nr_pages; - window->prot = pinned_pages->prot; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - scif_destroy_window(ep, window); - return err; - } - err = scif_send_alloc_request(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unmap; - } - - /* Prepare the remote registration window */ - err = scif_prep_remote_window(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unmap; - } - - /* Tell the peer about the new window */ - err = scif_send_scif_register(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unmap; - } - - scif_put_peer_dev(spdev); - /* No further failures expected. Insert new window */ - scif_insert_local_window(window, ep); - return computed_offset; -error_unmap: - scif_destroy_window(ep, window); - scif_put_peer_dev(spdev); - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_register_pinned_pages); - -off_t scif_register(scif_epd_t epd, void *addr, size_t len, off_t offset, - int prot, int map_flags) -{ - scif_pinned_pages_t pinned_pages; - off_t err; - struct scif_endpt *ep = (struct scif_endpt *)epd; - s64 computed_offset; - struct scif_window *window; - struct mm_struct *mm = NULL; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI register: ep %p addr %p len 0x%lx offset 0x%lx prot 0x%x map_flags 0x%x\n", - epd, addr, len, offset, prot, map_flags); - /* Unsupported flags */ - if (map_flags & ~(SCIF_MAP_FIXED | SCIF_MAP_KERNEL)) - return -EINVAL; - - /* - * Offset is not page aligned/negative or offset+len - * wraps around with SCIF_MAP_FIXED. - */ - if ((map_flags & SCIF_MAP_FIXED) && - ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset < 0) || - (len > LONG_MAX - offset))) - return -EINVAL; - - /* Unsupported protection requested */ - if (prot & ~(SCIF_PROT_READ | SCIF_PROT_WRITE)) - return -EINVAL; - - /* addr/len must be page aligned. len should be non zero */ - if (!len || (ALIGN((u64)addr, PAGE_SIZE) != (u64)addr) || - (ALIGN(len, PAGE_SIZE) != len)) - return -EINVAL; - - might_sleep(); - - err = scif_verify_epd(ep); - if (err) - return err; - - /* Compute the offset for this registration */ - err = scif_get_window_offset(ep, map_flags, offset, - len >> PAGE_SHIFT, &computed_offset); - if (err) - return err; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - scif_free_window_offset(ep, NULL, computed_offset); - return err; - } - /* Allocate and prepare self registration window */ - window = scif_create_window(ep, len >> PAGE_SHIFT, - computed_offset, false); - if (!window) { - scif_free_window_offset(ep, NULL, computed_offset); - scif_put_peer_dev(spdev); - return -ENOMEM; - } - - window->nr_pages = len >> PAGE_SHIFT; - - err = scif_send_alloc_request(ep, window); - if (err) { - scif_destroy_incomplete_window(ep, window); - scif_put_peer_dev(spdev); - return err; - } - - if (!(map_flags & SCIF_MAP_KERNEL)) { - mm = __scif_acquire_mm(); - map_flags |= SCIF_MAP_ULIMIT; - } - /* Pin down the pages */ - err = __scif_pin_pages(addr, len, &prot, - map_flags & (SCIF_MAP_KERNEL | SCIF_MAP_ULIMIT), - &pinned_pages); - if (err) { - scif_destroy_incomplete_window(ep, window); - __scif_release_mm(mm); - goto error; - } - - window->pinned_pages = pinned_pages; - window->prot = pinned_pages->prot; - window->mm = mm; - - /* Prepare the remote registration window */ - err = scif_prep_remote_window(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %ld\n", __func__, __LINE__, err); - goto error_unmap; - } - - /* Tell the peer about the new window */ - err = scif_send_scif_register(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %ld\n", __func__, __LINE__, err); - goto error_unmap; - } - - scif_put_peer_dev(spdev); - /* No further failures expected. Insert new window */ - scif_insert_local_window(window, ep); - dev_dbg(&ep->remote_dev->sdev->dev, - "SCIFAPI register: ep %p addr %p len 0x%lx computed_offset 0x%llx\n", - epd, addr, len, computed_offset); - return computed_offset; -error_unmap: - scif_destroy_window(ep, window); -error: - scif_put_peer_dev(spdev); - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %ld\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_register); - -int -scif_unregister(scif_epd_t epd, off_t offset, size_t len) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_window *window = NULL; - struct scif_rma_req req; - int nr_pages, err; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI unregister: ep %p offset 0x%lx len 0x%lx\n", - ep, offset, len); - /* len must be page aligned. len should be non zero */ - if (!len || - (ALIGN((u64)len, PAGE_SIZE) != (u64)len)) - return -EINVAL; - - /* Offset is not page aligned or offset+len wraps around */ - if ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset < 0) || - (len > LONG_MAX - offset)) - return -EINVAL; - - err = scif_verify_epd(ep); - if (err) - return err; - - might_sleep(); - nr_pages = len >> PAGE_SHIFT; - - req.out_window = &window; - req.offset = offset; - req.prot = 0; - req.nr_bytes = len; - req.type = SCIF_WINDOW_FULL; - req.head = &ep->rma_info.reg_list; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error; - } - /* Unregister all the windows in this range */ - err = scif_rma_list_unregister(window, offset, nr_pages); - if (err) - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); -error: - mutex_unlock(&ep->rma_info.rma_lock); - scif_put_peer_dev(spdev); - return err; -} -EXPORT_SYMBOL_GPL(scif_unregister); diff --git a/drivers/misc/mic/scif/scif_rma.h b/drivers/misc/mic/scif/scif_rma.h deleted file mode 100644 index 964dd0fc3657..000000000000 --- a/drivers/misc/mic/scif/scif_rma.h +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - * - */ -#ifndef SCIF_RMA_H -#define SCIF_RMA_H - -#include <linux/intel-iommu.h> -#include <linux/mmu_notifier.h> - -#include "../bus/scif_bus.h" - -/* If this bit is set then the mark is a remote fence mark */ -#define SCIF_REMOTE_FENCE_BIT 31 -/* Magic value used to indicate a remote fence request */ -#define SCIF_REMOTE_FENCE BIT_ULL(SCIF_REMOTE_FENCE_BIT) - -#define SCIF_MAX_UNALIGNED_BUF_SIZE (1024 * 1024ULL) -#define SCIF_KMEM_UNALIGNED_BUF_SIZE (SCIF_MAX_UNALIGNED_BUF_SIZE + \ - (L1_CACHE_BYTES << 1)) - -#define SCIF_IOVA_START_PFN (1) -#define SCIF_IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) -#define SCIF_DMA_64BIT_PFN SCIF_IOVA_PFN(DMA_BIT_MASK(64)) -#define SCIF_DMA_63BIT_PFN SCIF_IOVA_PFN(DMA_BIT_MASK(63)) - -/* - * struct scif_endpt_rma_info - Per Endpoint Remote Memory Access Information - * - * @reg_list: List of registration windows for self - * @remote_reg_list: List of registration windows for peer - * @iovad: Offset generator - * @rma_lock: Synchronizes access to self/remote list and also protects the - * window from being destroyed while RMAs are in progress. - * @tc_lock: Synchronizes access to temporary cached windows list - * for SCIF Registration Caching. - * @mmn_lock: Synchronizes access to the list of MMU notifiers registered - * @tw_refcount: Keeps track of number of outstanding temporary registered - * windows created by scif_vreadfrom/scif_vwriteto which have - * not been destroyed. - * @tcw_refcount: Same as tw_refcount but for temporary cached windows - * @tcw_total_pages: Same as tcw_refcount but in terms of pages pinned - * @mmn_list: MMU notifier so that we can destroy the windows when required - * @fence_refcount: Keeps track of number of outstanding remote fence - * requests which have been received by the peer. - * @dma_chan: DMA channel used for all DMA transfers for this endpoint. - * @async_list_del: Detect asynchronous list entry deletion - * @vma_list: List of vmas with remote memory mappings - * @markwq: Wait queue used for scif_fence_mark/scif_fence_wait -*/ -struct scif_endpt_rma_info { - struct list_head reg_list; - struct list_head remote_reg_list; - struct iova_domain iovad; - struct mutex rma_lock; - spinlock_t tc_lock; - struct mutex mmn_lock; - atomic_t tw_refcount; - atomic_t tcw_refcount; - atomic_t tcw_total_pages; - struct list_head mmn_list; - atomic_t fence_refcount; - struct dma_chan *dma_chan; - int async_list_del; - struct list_head vma_list; - wait_queue_head_t markwq; -}; - -/* - * struct scif_fence_info - used for tracking fence requests - * - * @state: State of this transfer - * @wq: Fences wait on this queue - * @dma_mark: Used for storing the DMA mark - */ -struct scif_fence_info { - enum scif_msg_state state; - struct completion comp; - int dma_mark; -}; - -/* - * struct scif_remote_fence_info - used for tracking remote fence requests - * - * @msg: List of SCIF node QP fence messages - * @list: Link to list of remote fence requests - */ -struct scif_remote_fence_info { - struct scifmsg msg; - struct list_head list; -}; - -/* - * Specifies whether an RMA operation can span across partial windows, a single - * window or multiple contiguous windows. Mmaps can span across partial windows. - * Unregistration can span across complete windows. scif_get_pages() can span a - * single window. A window can also be of type self or peer. - */ -enum scif_window_type { - SCIF_WINDOW_PARTIAL, - SCIF_WINDOW_SINGLE, - SCIF_WINDOW_FULL, - SCIF_WINDOW_SELF, - SCIF_WINDOW_PEER -}; - -/* The number of physical addresses that can be stored in a PAGE. */ -#define SCIF_NR_ADDR_IN_PAGE (0x1000 >> 3) - -/* - * struct scif_rma_lookup - RMA lookup data structure for page list transfers - * - * Store an array of lookup offsets. Each offset in this array maps - * one 4K page containing 512 physical addresses i.e. 2MB. 512 such - * offsets in a 4K page will correspond to 1GB of registered address space. - - * @lookup: Array of offsets - * @offset: DMA offset of lookup array - */ -struct scif_rma_lookup { - dma_addr_t *lookup; - dma_addr_t offset; -}; - -/* - * struct scif_pinned_pages - A set of pinned pages obtained with - * scif_pin_pages() which could be part of multiple registered - * windows across different end points. - * - * @nr_pages: Number of pages which is defined as a s64 instead of an int - * to avoid sign extension with buffers >= 2GB - * @prot: read/write protections - * @map_flags: Flags specified during the pin operation - * @ref_count: Reference count bumped in terms of number of pages - * @magic: A magic value - * @pages: Array of pointers to struct pages populated with get_user_pages(..) - */ -struct scif_pinned_pages { - s64 nr_pages; - int prot; - int map_flags; - atomic_t ref_count; - u64 magic; - struct page **pages; -}; - -/* - * struct scif_status - Stores DMA status update information - * - * @src_dma_addr: Source buffer DMA address - * @val: src location for value to be written to the destination - * @ep: SCIF endpoint - */ -struct scif_status { - dma_addr_t src_dma_addr; - u64 val; - struct scif_endpt *ep; -}; - -/* - * struct scif_cb_arg - Stores the argument of the callback func - * - * @src_dma_addr: Source buffer DMA address - * @status: DMA status - * @ep: SCIF endpoint - */ -struct scif_cb_arg { - dma_addr_t src_dma_addr; - struct scif_status *status; - struct scif_endpt *ep; -}; - -/* - * struct scif_window - Registration Window for Self and Remote - * - * @nr_pages: Number of pages which is defined as a s64 instead of an int - * to avoid sign extension with buffers >= 2GB - * @nr_contig_chunks: Number of contiguous physical chunks - * @prot: read/write protections - * @ref_count: reference count in terms of number of pages - * @magic: Cookie to detect corruption - * @offset: registered offset - * @va_for_temp: va address that this window represents - * @dma_mark: Used to determine if all DMAs against the window are done - * @ep: Pointer to EP. Useful for passing EP around with messages to - avoid expensive list traversals. - * @list: link to list of windows for the endpoint - * @type: self or peer window - * @peer_window: Pointer to peer window. Useful for sending messages to peer - * without requiring an extra list traversal - * @unreg_state: unregistration state - * @offset_freed: True if the offset has been freed - * @temp: True for temporary windows created via scif_vreadfrom/scif_vwriteto - * @mm: memory descriptor for the task_struct which initiated the RMA - * @st: scatter gather table for DMA mappings with IOMMU enabled - * @pinned_pages: The set of pinned_pages backing this window - * @alloc_handle: Handle for sending ALLOC_REQ - * @regwq: Wait Queue for an registration (N)ACK - * @reg_state: Registration state - * @unregwq: Wait Queue for an unregistration (N)ACK - * @dma_addr_lookup: Lookup for physical addresses used for DMA - * @nr_lookup: Number of entries in lookup - * @mapped_offset: Offset used to map the window by the peer - * @dma_addr: Array of physical addresses used for Mgmt node & MIC initiated DMA - * @num_pages: Array specifying number of pages for each physical address - */ -struct scif_window { - s64 nr_pages; - int nr_contig_chunks; - int prot; - int ref_count; - u64 magic; - s64 offset; - unsigned long va_for_temp; - int dma_mark; - u64 ep; - struct list_head list; - enum scif_window_type type; - u64 peer_window; - enum scif_msg_state unreg_state; - bool offset_freed; - bool temp; - struct mm_struct *mm; - struct sg_table *st; - union { - struct { - struct scif_pinned_pages *pinned_pages; - struct scif_allocmsg alloc_handle; - wait_queue_head_t regwq; - enum scif_msg_state reg_state; - wait_queue_head_t unregwq; - }; - struct { - struct scif_rma_lookup dma_addr_lookup; - struct scif_rma_lookup num_pages_lookup; - int nr_lookup; - dma_addr_t mapped_offset; - }; - }; - dma_addr_t *dma_addr; - u64 *num_pages; -} __packed; - -/* - * scif_mmu_notif - SCIF mmu notifier information - * - * @mmu_notifier ep_mmu_notifier: MMU notifier operations - * @tc_reg_list: List of temp registration windows for self - * @mm: memory descriptor for the task_struct which initiated the RMA - * @ep: SCIF endpoint - * @list: link to list of MMU notifier information - */ -struct scif_mmu_notif { -#ifdef CONFIG_MMU_NOTIFIER - struct mmu_notifier ep_mmu_notifier; -#endif - struct list_head tc_reg_list; - struct mm_struct *mm; - struct scif_endpt *ep; - struct list_head list; -}; - -enum scif_rma_dir { - SCIF_LOCAL_TO_REMOTE, - SCIF_REMOTE_TO_LOCAL -}; - -extern struct kmem_cache *unaligned_cache; -/* Initialize RMA for this EP */ -void scif_rma_ep_init(struct scif_endpt *ep); -/* Check if epd can be uninitialized */ -int scif_rma_ep_can_uninit(struct scif_endpt *ep); -/* Obtain a new offset. Callee must grab RMA lock */ -int scif_get_window_offset(struct scif_endpt *ep, int flags, - s64 offset, int nr_pages, s64 *out_offset); -/* Free offset. Callee must grab RMA lock */ -void scif_free_window_offset(struct scif_endpt *ep, - struct scif_window *window, s64 offset); -/* Create self registration window */ -struct scif_window *scif_create_window(struct scif_endpt *ep, int nr_pages, - s64 offset, bool temp); -/* Destroy self registration window.*/ -int scif_destroy_window(struct scif_endpt *ep, struct scif_window *window); -void scif_unmap_window(struct scif_dev *remote_dev, struct scif_window *window); -/* Map pages of self window to Aperture/PCI */ -int scif_map_window(struct scif_dev *remote_dev, - struct scif_window *window); -/* Unregister a self window */ -int scif_unregister_window(struct scif_window *window); -/* Destroy remote registration window */ -void -scif_destroy_remote_window(struct scif_window *window); -/* remove valid remote memory mappings from process address space */ -void scif_zap_mmaps(int node); -/* Query if any applications have remote memory mappings */ -bool scif_rma_do_apps_have_mmaps(int node); -/* Cleanup remote registration lists for zombie endpoints */ -void scif_cleanup_rma_for_zombies(int node); -/* Reserve a DMA channel for a particular endpoint */ -int scif_reserve_dma_chan(struct scif_endpt *ep); -/* Setup a DMA mark for an endpoint */ -int _scif_fence_mark(scif_epd_t epd, int *mark); -int scif_prog_signal(scif_epd_t epd, off_t offset, u64 val, - enum scif_window_type type); -void scif_alloc_req(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_alloc_gnt_rej(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_free_virt(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_reg(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_unreg(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_reg_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_reg_nack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_unreg_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_unreg_nack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_munmap(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_sig_resp(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_mmu_notif_handler(struct work_struct *work); -void scif_rma_handle_remote_fences(void); -void scif_rma_destroy_windows(void); -void scif_rma_destroy_tcw_invalid(void); -int scif_drain_dma_intr(struct scif_hw_dev *sdev, struct dma_chan *chan); - -struct scif_window_iter { - s64 offset; - int index; -}; - -static inline void -scif_init_window_iter(struct scif_window *window, struct scif_window_iter *iter) -{ - iter->offset = window->offset; - iter->index = 0; -} - -dma_addr_t scif_off_to_dma_addr(struct scif_window *window, s64 off, - size_t *nr_bytes, - struct scif_window_iter *iter); -static inline -dma_addr_t __scif_off_to_dma_addr(struct scif_window *window, s64 off) -{ - return scif_off_to_dma_addr(window, off, NULL, NULL); -} - -static inline bool scif_unaligned(off_t src_offset, off_t dst_offset) -{ - src_offset = src_offset & (L1_CACHE_BYTES - 1); - dst_offset = dst_offset & (L1_CACHE_BYTES - 1); - return !(src_offset == dst_offset); -} - -/* - * scif_zalloc: - * @size: Size of the allocation request. - * - * Helper API which attempts to allocate zeroed pages via - * __get_free_pages(..) first and then falls back on - * vzalloc(..) if that fails. - */ -static inline void *scif_zalloc(size_t size) -{ - void *ret = NULL; - size_t align = ALIGN(size, PAGE_SIZE); - - if (align && get_order(align) < MAX_ORDER) - ret = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(align)); - return ret ? ret : vzalloc(align); -} - -/* - * scif_free: - * @addr: Address to be freed. - * @size: Size of the allocation. - * Helper API which frees memory allocated via scif_zalloc(). - */ -static inline void scif_free(void *addr, size_t size) -{ - size_t align = ALIGN(size, PAGE_SIZE); - - if (is_vmalloc_addr(addr)) - vfree(addr); - else - free_pages((unsigned long)addr, get_order(align)); -} - -static inline void scif_get_window(struct scif_window *window, int nr_pages) -{ - window->ref_count += nr_pages; -} - -static inline void scif_put_window(struct scif_window *window, int nr_pages) -{ - window->ref_count -= nr_pages; -} - -static inline void scif_set_window_ref(struct scif_window *window, int nr_pages) -{ - window->ref_count = nr_pages; -} - -static inline void -scif_queue_for_cleanup(struct scif_window *window, struct list_head *list) -{ - spin_lock(&scif_info.rmalock); - list_add_tail(&window->list, list); - spin_unlock(&scif_info.rmalock); - schedule_work(&scif_info.misc_work); -} - -static inline void __scif_rma_destroy_tcw_helper(struct scif_window *window) -{ - list_del_init(&window->list); - scif_queue_for_cleanup(window, &scif_info.rma_tc); -} - -static inline bool scif_is_iommu_enabled(void) -{ -#ifdef CONFIG_INTEL_IOMMU - return intel_iommu_enabled; -#else - return false; -#endif -} -#endif /* SCIF_RMA_H */ diff --git a/drivers/misc/mic/scif/scif_rma_list.c b/drivers/misc/mic/scif/scif_rma_list.c deleted file mode 100644 index ef923ba134c8..000000000000 --- a/drivers/misc/mic/scif/scif_rma_list.c +++ /dev/null @@ -1,282 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include <linux/mmu_notifier.h> -#include <linux/highmem.h> - -/* - * scif_insert_tcw: - * - * Insert a temp window to the temp registration list sorted by va_for_temp. - * RMA lock must be held. - */ -void scif_insert_tcw(struct scif_window *window, struct list_head *head) -{ - struct scif_window *curr = NULL; - struct scif_window *prev = list_entry(head, struct scif_window, list); - struct list_head *item; - - INIT_LIST_HEAD(&window->list); - /* Compare with tail and if the entry is new tail add it to the end */ - if (!list_empty(head)) { - curr = list_entry(head->prev, struct scif_window, list); - if (curr->va_for_temp < window->va_for_temp) { - list_add_tail(&window->list, head); - return; - } - } - list_for_each(item, head) { - curr = list_entry(item, struct scif_window, list); - if (curr->va_for_temp > window->va_for_temp) - break; - prev = curr; - } - list_add(&window->list, &prev->list); -} - -/* - * scif_insert_window: - * - * Insert a window to the self registration list sorted by offset. - * RMA lock must be held. - */ -void scif_insert_window(struct scif_window *window, struct list_head *head) -{ - struct scif_window *curr = NULL, *prev = NULL; - struct list_head *item; - - INIT_LIST_HEAD(&window->list); - list_for_each(item, head) { - curr = list_entry(item, struct scif_window, list); - if (curr->offset > window->offset) - break; - prev = curr; - } - if (!prev) - list_add(&window->list, head); - else - list_add(&window->list, &prev->list); - scif_set_window_ref(window, window->nr_pages); -} - -/* - * scif_query_tcw: - * - * Query the temp cached registration list of ep for an overlapping window - * in case of permission mismatch, destroy the previous window. if permissions - * match and overlap is partial, destroy the window but return the new range - * RMA lock must be held. - */ -int scif_query_tcw(struct scif_endpt *ep, struct scif_rma_req *req) -{ - struct list_head *item, *temp, *head = req->head; - struct scif_window *window; - u64 start_va_window, start_va_req = req->va_for_temp; - u64 end_va_window, end_va_req = start_va_req + req->nr_bytes; - - if (!req->nr_bytes) - return -EINVAL; - /* - * Avoid traversing the entire list to find out that there - * is no entry that matches - */ - if (!list_empty(head)) { - window = list_last_entry(head, struct scif_window, list); - end_va_window = window->va_for_temp + - (window->nr_pages << PAGE_SHIFT); - if (start_va_req > end_va_window) - return -ENXIO; - } - list_for_each_safe(item, temp, head) { - window = list_entry(item, struct scif_window, list); - start_va_window = window->va_for_temp; - end_va_window = window->va_for_temp + - (window->nr_pages << PAGE_SHIFT); - if (start_va_req < start_va_window && - end_va_req < start_va_window) - break; - if (start_va_req >= end_va_window) - continue; - if ((window->prot & req->prot) == req->prot) { - if (start_va_req >= start_va_window && - end_va_req <= end_va_window) { - *req->out_window = window; - return 0; - } - /* expand window */ - if (start_va_req < start_va_window) { - req->nr_bytes += - start_va_window - start_va_req; - req->va_for_temp = start_va_window; - } - if (end_va_req >= end_va_window) - req->nr_bytes += end_va_window - end_va_req; - } - /* Destroy the old window to create a new one */ - __scif_rma_destroy_tcw_helper(window); - break; - } - return -ENXIO; -} - -/* - * scif_query_window: - * - * Query the registration list and check if a valid contiguous - * range of windows exist. - * RMA lock must be held. - */ -int scif_query_window(struct scif_rma_req *req) -{ - struct list_head *item; - struct scif_window *window; - s64 end_offset, offset = req->offset; - u64 tmp_min, nr_bytes_left = req->nr_bytes; - - if (!req->nr_bytes) - return -EINVAL; - - list_for_each(item, req->head) { - window = list_entry(item, struct scif_window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - if (offset < window->offset) - /* Offset not found! */ - return -ENXIO; - if (offset >= end_offset) - continue; - /* Check read/write protections. */ - if ((window->prot & req->prot) != req->prot) - return -EPERM; - if (nr_bytes_left == req->nr_bytes) - /* Store the first window */ - *req->out_window = window; - tmp_min = min((u64)end_offset - offset, nr_bytes_left); - nr_bytes_left -= tmp_min; - offset += tmp_min; - /* - * Range requested encompasses - * multiple windows contiguously. - */ - if (!nr_bytes_left) { - /* Done for partial window */ - if (req->type == SCIF_WINDOW_PARTIAL || - req->type == SCIF_WINDOW_SINGLE) - return 0; - /* Extra logic for full windows */ - if (offset == end_offset) - /* Spanning multiple whole windows */ - return 0; - /* Not spanning multiple whole windows */ - return -ENXIO; - } - if (req->type == SCIF_WINDOW_SINGLE) - break; - } - dev_err(scif_info.mdev.this_device, - "%s %d ENXIO\n", __func__, __LINE__); - return -ENXIO; -} - -/* - * scif_rma_list_unregister: - * - * Traverse the self registration list starting from window: - * 1) Call scif_unregister_window(..) - * RMA lock must be held. - */ -int scif_rma_list_unregister(struct scif_window *window, - s64 offset, int nr_pages) -{ - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - struct list_head *head = &ep->rma_info.reg_list; - s64 end_offset; - int err = 0; - int loop_nr_pages; - struct scif_window *_window; - - list_for_each_entry_safe_from(window, _window, head, list) { - end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min((int)((end_offset - offset) >> PAGE_SHIFT), - nr_pages); - err = scif_unregister_window(window); - if (err) - return err; - nr_pages -= loop_nr_pages; - offset += (loop_nr_pages << PAGE_SHIFT); - if (!nr_pages) - break; - } - return 0; -} - -/* - * scif_unmap_all_window: - * - * Traverse all the windows in the self registration list and: - * 1) Delete any DMA mappings created - */ -void scif_unmap_all_windows(scif_epd_t epd) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct list_head *head = &ep->rma_info.reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - list_for_each_safe(item, tmp, head) { - window = list_entry(item, struct scif_window, list); - scif_unmap_window(ep->remote_dev, window); - } - mutex_unlock(&ep->rma_info.rma_lock); -} - -/* - * scif_unregister_all_window: - * - * Traverse all the windows in the self registration list and: - * 1) Call scif_unregister_window(..) - * RMA lock must be held. - */ -int scif_unregister_all_windows(scif_epd_t epd) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct list_head *head = &ep->rma_info.reg_list; - int err = 0; - - mutex_lock(&ep->rma_info.rma_lock); -retry: - item = NULL; - tmp = NULL; - list_for_each_safe(item, tmp, head) { - window = list_entry(item, struct scif_window, list); - ep->rma_info.async_list_del = 0; - err = scif_unregister_window(window); - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", - __func__, __LINE__, err); - /* - * Need to restart list traversal if there has been - * an asynchronous list entry deletion. - */ - if (READ_ONCE(ep->rma_info.async_list_del)) - goto retry; - } - mutex_unlock(&ep->rma_info.rma_lock); - if (!list_empty(&ep->rma_info.mmn_list)) { - spin_lock(&scif_info.rmalock); - list_add_tail(&ep->mmu_list, &scif_info.mmu_notif_cleanup); - spin_unlock(&scif_info.rmalock); - schedule_work(&scif_info.mmu_notif_work); - } - return err; -} diff --git a/drivers/misc/mic/scif/scif_rma_list.h b/drivers/misc/mic/scif/scif_rma_list.h deleted file mode 100644 index 0f8e0ed65614..000000000000 --- a/drivers/misc/mic/scif/scif_rma_list.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_RMA_LIST_H -#define SCIF_RMA_LIST_H - -/* - * struct scif_rma_req - Self Registration list RMA Request query - * - * @out_window - Returns the window if found - * @offset: Starting offset - * @nr_bytes: number of bytes - * @prot: protection requested i.e. read or write or both - * @type: Specify single, partial or multiple windows - * @head: Head of list on which to search - * @va_for_temp: VA for searching temporary cached windows - */ -struct scif_rma_req { - struct scif_window **out_window; - union { - s64 offset; - unsigned long va_for_temp; - }; - size_t nr_bytes; - int prot; - enum scif_window_type type; - struct list_head *head; -}; - -/* Insert */ -void scif_insert_window(struct scif_window *window, struct list_head *head); -void scif_insert_tcw(struct scif_window *window, - struct list_head *head); -/* Query */ -int scif_query_window(struct scif_rma_req *request); -int scif_query_tcw(struct scif_endpt *ep, struct scif_rma_req *request); -/* Called from close to unregister all self windows */ -int scif_unregister_all_windows(scif_epd_t epd); -void scif_unmap_all_windows(scif_epd_t epd); -/* Traverse list and unregister */ -int scif_rma_list_unregister(struct scif_window *window, s64 offset, - int nr_pages); -#endif /* SCIF_RMA_LIST_H */ diff --git a/drivers/misc/mic/vop/Makefile b/drivers/misc/mic/vop/Makefile deleted file mode 100644 index 51b9b0022786..000000000000 --- a/drivers/misc/mic/vop/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2016, Intel Corporation. -# -obj-$(CONFIG_VOP) := vop.o - -vop-objs += vop_main.o -vop-objs += vop_debugfs.o -vop-objs += vop_vringh.o diff --git a/drivers/misc/mic/vop/vop_debugfs.c b/drivers/misc/mic/vop/vop_debugfs.c deleted file mode 100644 index 9d4f175f4dd1..000000000000 --- a/drivers/misc/mic/vop/vop_debugfs.c +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#include <linux/debugfs.h> -#include <linux/seq_file.h> - -#include "vop_main.h" - -static int vop_dp_show(struct seq_file *s, void *pos) -{ - struct mic_device_desc *d; - struct mic_device_ctrl *dc; - struct mic_vqconfig *vqconfig; - __u32 *features; - __u8 *config; - struct vop_info *vi = s->private; - struct vop_device *vpdev = vi->vpdev; - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - int j, k; - - seq_printf(s, "Bootparam: magic 0x%x\n", - bootparam->magic); - seq_printf(s, "Bootparam: h2c_config_db %d\n", - bootparam->h2c_config_db); - seq_printf(s, "Bootparam: node_id %d\n", - bootparam->node_id); - seq_printf(s, "Bootparam: c2h_scif_db %d\n", - bootparam->c2h_scif_db); - seq_printf(s, "Bootparam: h2c_scif_db %d\n", - bootparam->h2c_scif_db); - seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", - bootparam->scif_host_dma_addr); - seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", - bootparam->scif_card_dma_addr); - - for (j = sizeof(*bootparam); - j < MIC_DP_SIZE; j += mic_total_desc_size(d)) { - d = (void *)bootparam + j; - dc = (void *)d + mic_aligned_desc_size(d); - - /* end of list */ - if (d->type == 0) - break; - - if (d->type == -1) - continue; - - seq_printf(s, "Type %d ", d->type); - seq_printf(s, "Num VQ %d ", d->num_vq); - seq_printf(s, "Feature Len %d\n", d->feature_len); - seq_printf(s, "Config Len %d ", d->config_len); - seq_printf(s, "Shutdown Status %d\n", d->status); - - for (k = 0; k < d->num_vq; k++) { - vqconfig = mic_vq_config(d) + k; - seq_printf(s, "vqconfig[%d]: ", k); - seq_printf(s, "address 0x%llx ", - vqconfig->address); - seq_printf(s, "num %d ", vqconfig->num); - seq_printf(s, "used address 0x%llx\n", - vqconfig->used_address); - } - - features = (__u32 *)mic_vq_features(d); - seq_printf(s, "Features: Host 0x%x ", features[0]); - seq_printf(s, "Guest 0x%x\n", features[1]); - - config = mic_vq_configspace(d); - for (k = 0; k < d->config_len; k++) - seq_printf(s, "config[%d]=%d\n", k, config[k]); - - seq_puts(s, "Device control:\n"); - seq_printf(s, "Config Change %d ", dc->config_change); - seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); - seq_printf(s, "Guest Ack %d ", dc->guest_ack); - seq_printf(s, "Host ack %d\n", dc->host_ack); - seq_printf(s, "Used address updated %d ", - dc->used_address_updated); - seq_printf(s, "Vdev 0x%llx\n", dc->vdev); - seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); - seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); - } - schedule_work(&vi->hotplug_work); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(vop_dp); - -static int vop_vdev_info_show(struct seq_file *s, void *unused) -{ - struct vop_info *vi = s->private; - struct list_head *pos, *tmp; - struct vop_vdev *vdev; - int i, j; - - mutex_lock(&vi->vop_mutex); - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev = list_entry(pos, struct vop_vdev, list); - seq_printf(s, "VDEV type %d state %s in %ld out %ld in_dma %ld out_dma %ld\n", - vdev->virtio_id, - vop_vdevup(vdev) ? "UP" : "DOWN", - vdev->in_bytes, - vdev->out_bytes, - vdev->in_bytes_dma, - vdev->out_bytes_dma); - for (i = 0; i < MIC_MAX_VRINGS; i++) { - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; - struct vop_vringh *vvr = &vdev->vvr[i]; - struct vringh *vrh = &vvr->vrh; - int num = vrh->vring.num; - - if (!num) - continue; - desc = vrh->vring.desc; - seq_printf(s, "vring i %d avail_idx %d", - i, vvr->vring.info->avail_idx & (num - 1)); - seq_printf(s, " vring i %d avail_idx %d\n", - i, vvr->vring.info->avail_idx); - seq_printf(s, "vrh i %d weak_barriers %d", - i, vrh->weak_barriers); - seq_printf(s, " last_avail_idx %d last_used_idx %d", - vrh->last_avail_idx, vrh->last_used_idx); - seq_printf(s, " completed %d\n", vrh->completed); - for (j = 0; j < num; j++) { - seq_printf(s, "desc[%d] addr 0x%llx len %d", - j, desc->addr, desc->len); - seq_printf(s, " flags 0x%x next %d\n", - desc->flags, desc->next); - desc++; - } - avail = vrh->vring.avail; - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, - avail->idx) & (num - 1)); - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, avail->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "avail ring[%d] %d\n", - j, avail->ring[j]); - used = vrh->vring.used; - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx) & (num - 1)); - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "used ring[%d] id %d len %d\n", - j, vringh32_to_cpu(vrh, - used->ring[j].id), - vringh32_to_cpu(vrh, - used->ring[j].len)); - } - } - mutex_unlock(&vi->vop_mutex); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(vop_vdev_info); - -void vop_init_debugfs(struct vop_info *vi) -{ - char name[16]; - - snprintf(name, sizeof(name), "%s%d", KBUILD_MODNAME, vi->vpdev->dnode); - vi->dbg = debugfs_create_dir(name, NULL); - debugfs_create_file("dp", 0444, vi->dbg, vi, &vop_dp_fops); - debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vop_vdev_info_fops); -} - -void vop_exit_debugfs(struct vop_info *vi) -{ - debugfs_remove_recursive(vi->dbg); -} diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c deleted file mode 100644 index 714b94f42d38..000000000000 --- a/drivers/misc/mic/vop/vop_main.c +++ /dev/null @@ -1,784 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Adapted from: - * - * virtio for kvm on s390 - * - * Copyright IBM Corp. 2008 - * - * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/dma-mapping.h> -#include <linux/io-64-nonatomic-lo-hi.h> - -#include "vop_main.h" - -#define VOP_MAX_VRINGS 4 - -/* - * _vop_vdev - Allocated per virtio device instance injected by the peer. - * - * @vdev: Virtio device - * @desc: Virtio device page descriptor - * @dc: Virtio device control - * @vpdev: VOP device which is the parent for this virtio device - * @vr: Buffer for accessing the VRING - * @used_virt: Virtual address of used ring - * @used: DMA address of used ring - * @used_size: Size of the used buffer - * @reset_done: Track whether VOP reset is complete - * @virtio_cookie: Cookie returned upon requesting a interrupt - * @c2h_vdev_db: The doorbell used by the guest to interrupt the host - * @h2c_vdev_db: The doorbell used by the host to interrupt the guest - * @dnode: The destination node - */ -struct _vop_vdev { - struct virtio_device vdev; - struct mic_device_desc __iomem *desc; - struct mic_device_ctrl __iomem *dc; - struct vop_device *vpdev; - void __iomem *vr[VOP_MAX_VRINGS]; - void *used_virt[VOP_MAX_VRINGS]; - dma_addr_t used[VOP_MAX_VRINGS]; - int used_size[VOP_MAX_VRINGS]; - struct completion reset_done; - struct mic_irq *virtio_cookie; - int c2h_vdev_db; - int h2c_vdev_db; - int dnode; -}; - -#define to_vopvdev(vd) container_of(vd, struct _vop_vdev, vdev) - -#define _vop_aligned_desc_size(d) __mic_align(_vop_desc_size(d), 8) - -/* Helper API to obtain the parent of the virtio device */ -static inline struct device *_vop_dev(struct _vop_vdev *vdev) -{ - return vdev->vdev.dev.parent; -} - -static inline unsigned _vop_desc_size(struct mic_device_desc __iomem *desc) -{ - return sizeof(*desc) - + ioread8(&desc->num_vq) * sizeof(struct mic_vqconfig) - + ioread8(&desc->feature_len) * 2 - + ioread8(&desc->config_len); -} - -static inline struct mic_vqconfig __iomem * -_vop_vq_config(struct mic_device_desc __iomem *desc) -{ - return (struct mic_vqconfig __iomem *)(desc + 1); -} - -static inline u8 __iomem * -_vop_vq_features(struct mic_device_desc __iomem *desc) -{ - return (u8 __iomem *)(_vop_vq_config(desc) + ioread8(&desc->num_vq)); -} - -static inline u8 __iomem * -_vop_vq_configspace(struct mic_device_desc __iomem *desc) -{ - return _vop_vq_features(desc) + ioread8(&desc->feature_len) * 2; -} - -static inline unsigned -_vop_total_desc_size(struct mic_device_desc __iomem *desc) -{ - return _vop_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); -} - -/* This gets the device's feature bits. */ -static u64 vop_get_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - u64 features = 0; - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - u8 __iomem *in_features = _vop_vq_features(desc); - int feature_len = ioread8(&desc->feature_len); - - bits = min_t(unsigned, feature_len, sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) - if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) - features |= BIT_ULL(i); - - return features; -} - -static void vop_transport_features(struct virtio_device *vdev) -{ - /* - * Packed ring isn't enabled on virtio_vop for now, - * because virtio_vop uses vring_new_virtqueue() which - * creates virtio rings on preallocated memory. - */ - __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED); - __virtio_set_bit(vdev, VIRTIO_F_ACCESS_PLATFORM); -} - -static int vop_finalize_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - u8 feature_len = ioread8(&desc->feature_len); - /* Second half of bitmap is features we accept. */ - u8 __iomem *out_features = - _vop_vq_features(desc) + feature_len; - - /* Give virtio_ring a chance to accept features. */ - vring_transport_features(vdev); - - /* Give virtio_vop a chance to accept features. */ - vop_transport_features(vdev); - - memset_io(out_features, 0, feature_len); - bits = min_t(unsigned, feature_len, - sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) { - if (__virtio_test_bit(vdev, i)) - iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), - &out_features[i / 8]); - } - return 0; -} - -/* - * Reading and writing elements in config space - */ -static void vop_get(struct virtio_device *vdev, unsigned int offset, - void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_fromio(buf, _vop_vq_configspace(desc) + offset, len); -} - -static void vop_set(struct virtio_device *vdev, unsigned int offset, - const void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_toio(_vop_vq_configspace(desc) + offset, buf, len); -} - -/* - * The operations to get and set the status word just access the status - * field of the device descriptor. set_status also interrupts the host - * to tell about status changes. - */ -static u8 vop_get_status(struct virtio_device *vdev) -{ - return ioread8(&to_vopvdev(vdev)->desc->status); -} - -static void vop_set_status(struct virtio_device *dev, u8 status) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct vop_device *vpdev = vdev->vpdev; - - if (!status) - return; - iowrite8(status, &vdev->desc->status); - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); -} - -/* Inform host on a virtio device reset and wait for ack from host */ -static void vop_reset_inform_host(struct virtio_device *dev) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct mic_device_ctrl __iomem *dc = vdev->dc; - struct vop_device *vpdev = vdev->vpdev; - int retry; - - iowrite8(0, &dc->host_ack); - iowrite8(1, &dc->vdev_reset); - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); - - /* Wait till host completes all card accesses and acks the reset */ - for (retry = 100; retry--;) { - if (ioread8(&dc->host_ack)) - break; - msleep(100); - } - - dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); - - /* Reset status to 0 in case we timed out */ - iowrite8(0, &vdev->desc->status); -} - -static void vop_reset(struct virtio_device *dev) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - - dev_dbg(_vop_dev(vdev), "%s: virtio id %d\n", - __func__, dev->id.device); - - vop_reset_inform_host(dev); - complete_all(&vdev->reset_done); -} - -/* - * The virtio_ring code calls this API when it wants to notify the Host. - */ -static bool vop_notify(struct virtqueue *vq) -{ - struct _vop_vdev *vdev = vq->priv; - struct vop_device *vpdev = vdev->vpdev; - - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); - return true; -} - -static void vop_del_vq(struct virtqueue *vq, int n) -{ - struct _vop_vdev *vdev = to_vopvdev(vq->vdev); - struct vop_device *vpdev = vdev->vpdev; - - dma_unmap_single(&vpdev->dev, vdev->used[n], - vdev->used_size[n], DMA_BIDIRECTIONAL); - free_pages((unsigned long)vdev->used_virt[n], - get_order(vdev->used_size[n])); - vring_del_virtqueue(vq); - vpdev->hw_ops->unmap(vpdev, vdev->vr[n]); - vdev->vr[n] = NULL; -} - -static void vop_del_vqs(struct virtio_device *dev) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct virtqueue *vq, *n; - int idx = 0; - - dev_dbg(_vop_dev(vdev), "%s\n", __func__); - - list_for_each_entry_safe(vq, n, &dev->vqs, list) - vop_del_vq(vq, idx++); -} - -static struct virtqueue *vop_new_virtqueue(unsigned int index, - unsigned int num, - struct virtio_device *vdev, - bool context, - void *pages, - bool (*notify)(struct virtqueue *vq), - void (*callback)(struct virtqueue *vq), - const char *name, - void *used) -{ - bool weak_barriers = false; - struct vring vring; - - vring_init(&vring, num, pages, MIC_VIRTIO_RING_ALIGN); - vring.used = used; - - return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context, - notify, callback, name); -} - -/* - * This routine will assign vring's allocated in host/io memory. Code in - * virtio_ring.c however continues to access this io memory as if it were local - * memory without io accessors. - */ -static struct virtqueue *vop_find_vq(struct virtio_device *dev, - unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name, bool ctx) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct vop_device *vpdev = vdev->vpdev; - struct mic_vqconfig __iomem *vqconfig; - struct mic_vqconfig config; - struct virtqueue *vq; - void __iomem *va; - struct _mic_vring_info __iomem *info; - void *used; - int vr_size, _vr_size, err, magic; - u8 type = ioread8(&vdev->desc->type); - - if (index >= ioread8(&vdev->desc->num_vq)) - return ERR_PTR(-ENOENT); - - if (!name) - return ERR_PTR(-ENOENT); - - /* First assign the vring's allocated in host memory */ - vqconfig = _vop_vq_config(vdev->desc) + index; - memcpy_fromio(&config, vqconfig, sizeof(config)); - _vr_size = round_up(vring_size(le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN), 4); - vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); - va = vpdev->hw_ops->remap(vpdev, le64_to_cpu(config.address), vr_size); - if (!va) - return ERR_PTR(-ENOMEM); - vdev->vr[index] = va; - memset_io(va, 0x0, _vr_size); - - info = va + _vr_size; - magic = ioread32(&info->magic); - - if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { - err = -EIO; - goto unmap; - } - - vdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + - sizeof(struct vring_used_elem) * - le16_to_cpu(config.num)); - used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vdev->used_size[index])); - vdev->used_virt[index] = used; - if (!used) { - err = -ENOMEM; - dev_err(_vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto unmap; - } - - vq = vop_new_virtqueue(index, le16_to_cpu(config.num), dev, ctx, - (void __force *)va, vop_notify, callback, - name, used); - if (!vq) { - err = -ENOMEM; - goto free_used; - } - - vdev->used[index] = dma_map_single(&vpdev->dev, used, - vdev->used_size[index], - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&vpdev->dev, vdev->used[index])) { - err = -ENOMEM; - dev_err(_vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto del_vq; - } - writeq(vdev->used[index], &vqconfig->used_address); - - vq->priv = vdev; - return vq; -del_vq: - vring_del_virtqueue(vq); -free_used: - free_pages((unsigned long)used, - get_order(vdev->used_size[index])); -unmap: - vpdev->hw_ops->unmap(vpdev, vdev->vr[index]); - return ERR_PTR(err); -} - -static int vop_find_vqs(struct virtio_device *dev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, - struct irq_affinity *desc) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct vop_device *vpdev = vdev->vpdev; - struct mic_device_ctrl __iomem *dc = vdev->dc; - int i, err, retry, queue_idx = 0; - - /* We must have this many virtqueues. */ - if (nvqs > ioread8(&vdev->desc->num_vq)) - return -ENOENT; - - for (i = 0; i < nvqs; ++i) { - if (!names[i]) { - vqs[i] = NULL; - continue; - } - - dev_dbg(_vop_dev(vdev), "%s: %d: %s\n", - __func__, i, names[i]); - vqs[i] = vop_find_vq(dev, queue_idx++, callbacks[i], names[i], - ctx ? ctx[i] : false); - if (IS_ERR(vqs[i])) { - err = PTR_ERR(vqs[i]); - goto error; - } - } - - iowrite8(1, &dc->used_address_updated); - /* - * Send an interrupt to the host to inform it that used - * rings have been re-assigned. - */ - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); - for (retry = 100; --retry;) { - if (!ioread8(&dc->used_address_updated)) - break; - msleep(100); - } - - dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); - if (!retry) { - err = -ENODEV; - goto error; - } - - return 0; -error: - vop_del_vqs(dev); - return err; -} - -/* - * The config ops structure as defined by virtio config - */ -static const struct virtio_config_ops vop_vq_config_ops = { - .get_features = vop_get_features, - .finalize_features = vop_finalize_features, - .get = vop_get, - .set = vop_set, - .get_status = vop_get_status, - .set_status = vop_set_status, - .reset = vop_reset, - .find_vqs = vop_find_vqs, - .del_vqs = vop_del_vqs, -}; - -static irqreturn_t vop_virtio_intr_handler(int irq, void *data) -{ - struct _vop_vdev *vdev = data; - struct vop_device *vpdev = vdev->vpdev; - struct virtqueue *vq; - - vpdev->hw_ops->ack_interrupt(vpdev, vdev->h2c_vdev_db); - list_for_each_entry(vq, &vdev->vdev.vqs, list) - vring_interrupt(0, vq); - - return IRQ_HANDLED; -} - -static void vop_virtio_release_dev(struct device *_d) -{ - struct virtio_device *vdev = - container_of(_d, struct virtio_device, dev); - struct _vop_vdev *vop_vdev = - container_of(vdev, struct _vop_vdev, vdev); - - kfree(vop_vdev); -} - -/* - * adds a new device and register it with virtio - * appropriate drivers are loaded by the device model - */ -static int _vop_add_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct vop_device *vpdev, - int dnode) -{ - struct _vop_vdev *vdev, *reg_dev = NULL; - int ret; - u8 type = ioread8(&d->type); - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) - return -ENOMEM; - - vdev->vpdev = vpdev; - vdev->vdev.dev.parent = &vpdev->dev; - vdev->vdev.dev.release = vop_virtio_release_dev; - vdev->vdev.id.device = type; - vdev->vdev.config = &vop_vq_config_ops; - vdev->desc = d; - vdev->dc = (void __iomem *)d + _vop_aligned_desc_size(d); - vdev->dnode = dnode; - vdev->vdev.priv = (void *)(unsigned long)dnode; - init_completion(&vdev->reset_done); - - vdev->h2c_vdev_db = vpdev->hw_ops->next_db(vpdev); - vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, - vop_virtio_intr_handler, "virtio intr", - vdev, vdev->h2c_vdev_db); - if (IS_ERR(vdev->virtio_cookie)) { - ret = PTR_ERR(vdev->virtio_cookie); - goto kfree; - } - iowrite8((u8)vdev->h2c_vdev_db, &vdev->dc->h2c_vdev_db); - vdev->c2h_vdev_db = ioread8(&vdev->dc->c2h_vdev_db); - - ret = register_virtio_device(&vdev->vdev); - reg_dev = vdev; - if (ret) { - dev_err(_vop_dev(vdev), - "Failed to register vop device %u type %u\n", - offset, type); - goto free_irq; - } - writeq((unsigned long)vdev, &vdev->dc->vdev); - dev_dbg(_vop_dev(vdev), "%s: registered vop device %u type %u vdev %p\n", - __func__, offset, type, vdev); - - return 0; - -free_irq: - vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); -kfree: - if (reg_dev) - put_device(&vdev->vdev.dev); - else - kfree(vdev); - return ret; -} - -/* - * match for a vop device with a specific desc pointer - */ -static int vop_match_desc(struct device *dev, void *data) -{ - struct virtio_device *_dev = dev_to_virtio(dev); - struct _vop_vdev *vdev = to_vopvdev(_dev); - - return vdev->desc == (void __iomem *)data; -} - -static struct _vop_vdev *vop_dc_to_vdev(struct mic_device_ctrl __iomem *dc) -{ - return (struct _vop_vdev *)(unsigned long)readq(&dc->vdev); -} - -static void _vop_handle_config_change(struct mic_device_desc __iomem *d, - unsigned int offset, - struct vop_device *vpdev) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + _vop_aligned_desc_size(d); - struct _vop_vdev *vdev = vop_dc_to_vdev(dc); - - if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) - return; - - dev_dbg(&vpdev->dev, "%s %d\n", __func__, __LINE__); - virtio_config_changed(&vdev->vdev); - iowrite8(1, &dc->guest_ack); -} - -/* - * removes a virtio device if a hot remove event has been - * requested by the host. - */ -static int _vop_remove_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct vop_device *vpdev) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + _vop_aligned_desc_size(d); - struct _vop_vdev *vdev = vop_dc_to_vdev(dc); - u8 status; - int ret = -1; - - if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { - struct device *dev = get_device(&vdev->vdev.dev); - - dev_dbg(&vpdev->dev, - "%s %d config_change %d type %d vdev %p\n", - __func__, __LINE__, - ioread8(&dc->config_change), ioread8(&d->type), vdev); - status = ioread8(&d->status); - reinit_completion(&vdev->reset_done); - unregister_virtio_device(&vdev->vdev); - vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); - iowrite8(-1, &dc->h2c_vdev_db); - if (status & VIRTIO_CONFIG_S_DRIVER_OK) - wait_for_completion(&vdev->reset_done); - put_device(dev); - iowrite8(1, &dc->guest_ack); - dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n", - __func__, __LINE__, ioread8(&dc->guest_ack)); - iowrite8(-1, &d->type); - ret = 0; - } - return ret; -} - -#define REMOVE_DEVICES true - -static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, - bool remove, int dnode) -{ - s8 type; - unsigned int i; - struct mic_device_desc __iomem *d; - struct mic_device_ctrl __iomem *dc; - struct device *dev; - - for (i = sizeof(struct mic_bootparam); - i < MIC_DP_SIZE; i += _vop_total_desc_size(d)) { - d = dp + i; - dc = (void __iomem *)d + _vop_aligned_desc_size(d); - /* - * This read barrier is paired with the corresponding write - * barrier on the host which is inserted before adding or - * removing a virtio device descriptor, by updating the type. - */ - rmb(); - type = ioread8(&d->type); - - /* end of list */ - if (type == 0) - break; - - if (type == -1) - continue; - - /* device already exists */ - dev = device_find_child(&vpdev->dev, (void __force *)d, - vop_match_desc); - if (dev) { - if (remove) - iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, - &dc->config_change); - put_device(dev); - _vop_handle_config_change(d, i, vpdev); - _vop_remove_device(d, i, vpdev); - if (remove) { - iowrite8(0, &dc->config_change); - iowrite8(0, &dc->guest_ack); - } - continue; - } - - /* new device */ - dev_dbg(&vpdev->dev, "%s %d Adding new virtio device %p\n", - __func__, __LINE__, d); - if (!remove) - _vop_add_device(d, i, vpdev, dnode); - } -} - -static void vop_scan_devices(struct vop_info *vi, - struct vop_device *vpdev, bool remove) -{ - void __iomem *dp = vpdev->hw_ops->get_remote_dp(vpdev); - - if (!dp) - return; - mutex_lock(&vi->vop_mutex); - _vop_scan_devices(dp, vpdev, remove, vpdev->dnode); - mutex_unlock(&vi->vop_mutex); -} - -/* - * vop_hotplug_device tries to find changes in the device page. - */ -static void vop_hotplug_devices(struct work_struct *work) -{ - struct vop_info *vi = container_of(work, struct vop_info, - hotplug_work); - - vop_scan_devices(vi, vi->vpdev, !REMOVE_DEVICES); -} - -/* - * Interrupt handler for hot plug/config changes etc. - */ -static irqreturn_t vop_extint_handler(int irq, void *data) -{ - struct vop_info *vi = data; - struct mic_bootparam __iomem *bp; - struct vop_device *vpdev = vi->vpdev; - - bp = vpdev->hw_ops->get_remote_dp(vpdev); - dev_dbg(&vpdev->dev, "%s %d hotplug work\n", - __func__, __LINE__); - vpdev->hw_ops->ack_interrupt(vpdev, ioread8(&bp->h2c_config_db)); - schedule_work(&vi->hotplug_work); - return IRQ_HANDLED; -} - -static int vop_driver_probe(struct vop_device *vpdev) -{ - struct vop_info *vi; - int rc; - - vi = kzalloc(sizeof(*vi), GFP_KERNEL); - if (!vi) { - rc = -ENOMEM; - goto exit; - } - dev_set_drvdata(&vpdev->dev, vi); - vi->vpdev = vpdev; - - mutex_init(&vi->vop_mutex); - INIT_WORK(&vi->hotplug_work, vop_hotplug_devices); - if (vpdev->dnode) { - rc = vop_host_init(vi); - if (rc < 0) - goto free; - } else { - struct mic_bootparam __iomem *bootparam; - - vop_scan_devices(vi, vpdev, !REMOVE_DEVICES); - - vi->h2c_config_db = vpdev->hw_ops->next_db(vpdev); - vi->cookie = vpdev->hw_ops->request_irq(vpdev, - vop_extint_handler, - "virtio_config_intr", - vi, vi->h2c_config_db); - if (IS_ERR(vi->cookie)) { - rc = PTR_ERR(vi->cookie); - goto free; - } - bootparam = vpdev->hw_ops->get_remote_dp(vpdev); - iowrite8(vi->h2c_config_db, &bootparam->h2c_config_db); - } - vop_init_debugfs(vi); - return 0; -free: - kfree(vi); -exit: - return rc; -} - -static void vop_driver_remove(struct vop_device *vpdev) -{ - struct vop_info *vi = dev_get_drvdata(&vpdev->dev); - - if (vpdev->dnode) { - vop_host_uninit(vi); - } else { - struct mic_bootparam __iomem *bootparam = - vpdev->hw_ops->get_remote_dp(vpdev); - if (bootparam) - iowrite8(-1, &bootparam->h2c_config_db); - vpdev->hw_ops->free_irq(vpdev, vi->cookie, vi); - flush_work(&vi->hotplug_work); - vop_scan_devices(vi, vpdev, REMOVE_DEVICES); - } - vop_exit_debugfs(vi); - kfree(vi); -} - -static const struct vop_device_id id_table[] = { - { VOP_DEV_TRNSP, VOP_DEV_ANY_ID }, - { 0 }, -}; - -static struct vop_driver vop_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = vop_driver_probe, - .remove = vop_driver_remove, -}; - -module_vop_driver(vop_driver); - -MODULE_DEVICE_TABLE(mbus, id_table); -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) Virtio Over PCIe (VOP) driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/vop/vop_main.h b/drivers/misc/mic/vop/vop_main.h deleted file mode 100644 index 2451d9218137..000000000000 --- a/drivers/misc/mic/vop/vop_main.h +++ /dev/null @@ -1,158 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#ifndef _VOP_MAIN_H_ -#define _VOP_MAIN_H_ - -#include <linux/vringh.h> -#include <linux/virtio_config.h> -#include <linux/virtio.h> -#include <linux/miscdevice.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" - -#include "../bus/vop_bus.h" - -/* - * Note on endianness. - * 1. Host can be both BE or LE - * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail - * rings and ioreadXX/iowriteXX to access used ring. - * 3. Device page exposed by host to guest contains LE values. Guest - * accesses these using ioreadXX/iowriteXX etc. This way in general we - * obey the virtio spec according to which guest works with native - * endianness and host is aware of guest endianness and does all - * required endianness conversion. - * 4. Data provided from user space to guest (in ADD_DEVICE and - * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be - * in guest endianness. - */ - -/* - * vop_info - Allocated per invocation of VOP probe - * - * @vpdev: VOP device - * @hotplug_work: Handle virtio device creation, deletion and configuration - * @cookie: Cookie received upon requesting a virtio configuration interrupt - * @h2c_config_db: The doorbell used by the peer to indicate a config change - * @vdev_list: List of "active" virtio devices injected in the peer node - * @vop_mutex: Synchronize access to the device page as well as serialize - * creation/deletion of virtio devices on the peer node - * @dp: Peer device page information - * @dbg: Debugfs entry - * @dma_ch: The DMA channel used by this transport for data transfers. - * @name: Name for this transport used in misc device creation. - * @miscdev: The misc device registered. - */ -struct vop_info { - struct vop_device *vpdev; - struct work_struct hotplug_work; - struct mic_irq *cookie; - int h2c_config_db; - struct list_head vdev_list; - struct mutex vop_mutex; - void __iomem *dp; - struct dentry *dbg; - struct dma_chan *dma_ch; - char name[16]; - struct miscdevice miscdev; -}; - -/** - * struct vop_vringh - Virtio ring host information. - * - * @vring: The VOP vring used for setting up user space mappings. - * @vrh: The host VRINGH used for accessing the card vrings. - * @riov: The VRINGH read kernel IOV. - * @wiov: The VRINGH write kernel IOV. - * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). - * @vr_mutex: Mutex for synchronizing access to the VRING. - * @buf: Temporary kernel buffer used to copy in/out data - * from/to the card via DMA. - * @buf_da: dma address of buf. - * @vdev: Back pointer to VOP virtio device for vringh_notify(..). - */ -struct vop_vringh { - struct mic_vring vring; - struct vringh vrh; - struct vringh_kiov riov; - struct vringh_kiov wiov; - u16 head; - struct mutex vr_mutex; - void *buf; - dma_addr_t buf_da; - struct vop_vdev *vdev; -}; - -/** - * struct vop_vdev - Host information for a card Virtio device. - * - * @virtio_id - Virtio device id. - * @waitq - Waitqueue to allow ring3 apps to poll. - * @vpdev - pointer to VOP bus device. - * @poll_wake - Used for waking up threads blocked in poll. - * @out_bytes - Debug stats for number of bytes copied from host to card. - * @in_bytes - Debug stats for number of bytes copied from card to host. - * @out_bytes_dma - Debug stats for number of bytes copied from host to card - * using DMA. - * @in_bytes_dma - Debug stats for number of bytes copied from card to host - * using DMA. - * @tx_len_unaligned - Debug stats for number of bytes copied to the card where - * the transfer length did not have the required DMA alignment. - * @tx_dst_unaligned - Debug stats for number of bytes copied where the - * destination address on the card did not have the required DMA alignment. - * @vvr - Store per VRING data structures. - * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. - * @dd - Virtio device descriptor. - * @dc - Virtio device control fields. - * @list - List of Virtio devices. - * @virtio_db - The doorbell used by the card to interrupt the host. - * @virtio_cookie - The cookie returned while requesting interrupts. - * @vi: Transport information. - * @vdev_mutex: Mutex synchronizing virtio device injection, - * removal and data transfers. - * @destroy: Track if a virtio device is being destroyed. - * @deleted: The virtio device has been deleted. - */ -struct vop_vdev { - int virtio_id; - wait_queue_head_t waitq; - struct vop_device *vpdev; - int poll_wake; - unsigned long out_bytes; - unsigned long in_bytes; - unsigned long out_bytes_dma; - unsigned long in_bytes_dma; - unsigned long tx_len_unaligned; - unsigned long tx_dst_unaligned; - unsigned long rx_dst_unaligned; - struct vop_vringh vvr[MIC_MAX_VRINGS]; - struct work_struct virtio_bh_work; - struct mic_device_desc *dd; - struct mic_device_ctrl *dc; - struct list_head list; - int virtio_db; - struct mic_irq *virtio_cookie; - struct vop_info *vi; - struct mutex vdev_mutex; - struct completion destroy; - bool deleted; -}; - -/* Helper API to check if a virtio device is running */ -static inline bool vop_vdevup(struct vop_vdev *vdev) -{ - return !!vdev->dd->status; -} - -void vop_init_debugfs(struct vop_info *vi); -void vop_exit_debugfs(struct vop_info *vi); -int vop_host_init(struct vop_info *vi); -void vop_host_uninit(struct vop_info *vi); -#endif diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c deleted file mode 100644 index 7014ffe88632..000000000000 --- a/drivers/misc/mic/vop/vop_vringh.c +++ /dev/null @@ -1,1166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#include <linux/sched.h> -#include <linux/poll.h> -#include <linux/dma-mapping.h> - -#include <linux/mic_common.h> -#include "../common/mic_dev.h" - -#include <linux/mic_ioctl.h> -#include "vop_main.h" - -/* Helper API to obtain the VOP PCIe device */ -static inline struct device *vop_dev(struct vop_vdev *vdev) -{ - return vdev->vpdev->dev.parent; -} - -/* Helper API to check if a virtio device is initialized */ -static inline int vop_vdev_inited(struct vop_vdev *vdev) -{ - if (!vdev) - return -EINVAL; - /* Device has not been created yet */ - if (!vdev->dd || !vdev->dd->type) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - /* Device has been removed/deleted */ - if (vdev->dd->type == -1) { - dev_dbg(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, -ENODEV); - return -ENODEV; - } - return 0; -} - -static void _vop_notify(struct vringh *vrh) -{ - struct vop_vringh *vvrh = container_of(vrh, struct vop_vringh, vrh); - struct vop_vdev *vdev = vvrh->vdev; - struct vop_device *vpdev = vdev->vpdev; - s8 db = vdev->dc->h2c_vdev_db; - - if (db != -1) - vpdev->hw_ops->send_intr(vpdev, db); -} - -static void vop_virtio_init_post(struct vop_vdev *vdev) -{ - struct mic_vqconfig *vqconfig = mic_vq_config(vdev->dd); - struct vop_device *vpdev = vdev->vpdev; - int i, used_size; - - for (i = 0; i < vdev->dd->num_vq; i++) { - used_size = PAGE_ALIGN(sizeof(u16) * 3 + - sizeof(struct vring_used_elem) * - le16_to_cpu(vqconfig->num)); - if (!le64_to_cpu(vqconfig[i].used_address)) { - dev_warn(vop_dev(vdev), "used_address zero??\n"); - continue; - } - vdev->vvr[i].vrh.vring.used = - (void __force *)vpdev->hw_ops->remap( - vpdev, - le64_to_cpu(vqconfig[i].used_address), - used_size); - } - - vdev->dc->used_address_updated = 0; - - dev_info(vop_dev(vdev), "%s: device type %d LINKUP\n", - __func__, vdev->virtio_id); -} - -static inline void vop_virtio_device_reset(struct vop_vdev *vdev) -{ - int i; - - dev_dbg(vop_dev(vdev), "%s: status %d device type %d RESET\n", - __func__, vdev->dd->status, vdev->virtio_id); - - for (i = 0; i < vdev->dd->num_vq; i++) - /* - * Avoid lockdep false positive. The + 1 is for the vop - * mutex which is held in the reset devices code path. - */ - mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); - - /* 0 status means "reset" */ - vdev->dd->status = 0; - vdev->dc->vdev_reset = 0; - vdev->dc->host_ack = 1; - - for (i = 0; i < vdev->dd->num_vq; i++) { - struct vringh *vrh = &vdev->vvr[i].vrh; - - vdev->vvr[i].vring.info->avail_idx = 0; - vrh->completed = 0; - vrh->last_avail_idx = 0; - vrh->last_used_idx = 0; - } - - for (i = 0; i < vdev->dd->num_vq; i++) - mutex_unlock(&vdev->vvr[i].vr_mutex); -} - -static void vop_virtio_reset_devices(struct vop_info *vi) -{ - struct list_head *pos, *tmp; - struct vop_vdev *vdev; - - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev = list_entry(pos, struct vop_vdev, list); - vop_virtio_device_reset(vdev); - vdev->poll_wake = 1; - wake_up(&vdev->waitq); - } -} - -static void vop_bh_handler(struct work_struct *work) -{ - struct vop_vdev *vdev = container_of(work, struct vop_vdev, - virtio_bh_work); - - if (vdev->dc->used_address_updated) - vop_virtio_init_post(vdev); - - if (vdev->dc->vdev_reset) - vop_virtio_device_reset(vdev); - - vdev->poll_wake = 1; - wake_up(&vdev->waitq); -} - -static irqreturn_t _vop_virtio_intr_handler(int irq, void *data) -{ - struct vop_vdev *vdev = data; - struct vop_device *vpdev = vdev->vpdev; - - vpdev->hw_ops->ack_interrupt(vpdev, vdev->virtio_db); - schedule_work(&vdev->virtio_bh_work); - return IRQ_HANDLED; -} - -static int vop_virtio_config_change(struct vop_vdev *vdev, void *argp) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int ret = 0, retry, i; - struct vop_device *vpdev = vdev->vpdev; - struct vop_info *vi = dev_get_drvdata(&vpdev->dev); - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - s8 db = bootparam->h2c_config_db; - - mutex_lock(&vi->vop_mutex); - for (i = 0; i < vdev->dd->num_vq; i++) - mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); - - if (db == -1 || vdev->dd->type == -1) { - ret = -EIO; - goto exit; - } - - memcpy(mic_vq_configspace(vdev->dd), argp, vdev->dd->config_len); - vdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; - vpdev->hw_ops->send_intr(vpdev, db); - - for (retry = 100; retry--;) { - ret = wait_event_timeout(wake, vdev->dc->guest_ack, - msecs_to_jiffies(100)); - if (ret) - break; - } - - dev_dbg(vop_dev(vdev), - "%s %d retry: %d\n", __func__, __LINE__, retry); - vdev->dc->config_change = 0; - vdev->dc->guest_ack = 0; -exit: - for (i = 0; i < vdev->dd->num_vq; i++) - mutex_unlock(&vdev->vvr[i].vr_mutex); - mutex_unlock(&vi->vop_mutex); - return ret; -} - -static int vop_copy_dp_entry(struct vop_vdev *vdev, - struct mic_device_desc *argp, __u8 *type, - struct mic_device_desc **devpage) -{ - struct vop_device *vpdev = vdev->vpdev; - struct mic_device_desc *devp; - struct mic_vqconfig *vqconfig; - int ret = 0, i; - bool slot_found = false; - - vqconfig = mic_vq_config(argp); - for (i = 0; i < argp->num_vq; i++) { - if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { - ret = -EINVAL; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - } - - /* Find the first free device page entry */ - for (i = sizeof(struct mic_bootparam); - i < MIC_DP_SIZE - mic_total_desc_size(argp); - i += mic_total_desc_size(devp)) { - devp = vpdev->hw_ops->get_dp(vpdev) + i; - if (devp->type == 0 || devp->type == -1) { - slot_found = true; - break; - } - } - if (!slot_found) { - ret = -EINVAL; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - /* - * Save off the type before doing the memcpy. Type will be set in the - * end after completing all initialization for the new device. - */ - *type = argp->type; - argp->type = 0; - memcpy(devp, argp, mic_desc_size(argp)); - - *devpage = devp; -exit: - return ret; -} - -static void vop_init_device_ctrl(struct vop_vdev *vdev, - struct mic_device_desc *devpage) -{ - struct mic_device_ctrl *dc; - - dc = (void *)devpage + mic_aligned_desc_size(devpage); - - dc->config_change = 0; - dc->guest_ack = 0; - dc->vdev_reset = 0; - dc->host_ack = 0; - dc->used_address_updated = 0; - dc->c2h_vdev_db = -1; - dc->h2c_vdev_db = -1; - vdev->dc = dc; -} - -static int vop_virtio_add_device(struct vop_vdev *vdev, - struct mic_device_desc *argp) -{ - struct vop_info *vi = vdev->vi; - struct vop_device *vpdev = vi->vpdev; - struct mic_device_desc *dd = NULL; - struct mic_vqconfig *vqconfig; - int vr_size, i, j, ret; - u8 type = 0; - s8 db = -1; - char irqname[16]; - struct mic_bootparam *bootparam; - u16 num; - dma_addr_t vr_addr; - - bootparam = vpdev->hw_ops->get_dp(vpdev); - init_waitqueue_head(&vdev->waitq); - INIT_LIST_HEAD(&vdev->list); - vdev->vpdev = vpdev; - - ret = vop_copy_dp_entry(vdev, argp, &type, &dd); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - return ret; - } - - vop_init_device_ctrl(vdev, dd); - - vdev->dd = dd; - vdev->virtio_id = type; - vqconfig = mic_vq_config(dd); - INIT_WORK(&vdev->virtio_bh_work, vop_bh_handler); - - for (i = 0; i < dd->num_vq; i++) { - struct vop_vringh *vvr = &vdev->vvr[i]; - struct mic_vring *vr = &vdev->vvr[i].vring; - - num = le16_to_cpu(vqconfig[i].num); - mutex_init(&vvr->vr_mutex); - vr_size = PAGE_ALIGN(round_up(vring_size(num, MIC_VIRTIO_RING_ALIGN), 4) + - sizeof(struct _mic_vring_info)); - vr->va = (void *) - __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vr_size)); - if (!vr->va) { - ret = -ENOMEM; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vr->len = vr_size; - vr->info = vr->va + round_up(vring_size(num, MIC_VIRTIO_RING_ALIGN), 4); - vr->info->magic = cpu_to_le32(MIC_MAGIC + vdev->virtio_id + i); - vr_addr = dma_map_single(&vpdev->dev, vr->va, vr_size, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&vpdev->dev, vr_addr)) { - free_pages((unsigned long)vr->va, get_order(vr_size)); - ret = -ENOMEM; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vqconfig[i].address = cpu_to_le64(vr_addr); - - vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); - ret = vringh_init_kern(&vvr->vrh, - *(u32 *)mic_vq_features(vdev->dd), - num, false, vr->vr.desc, vr->vr.avail, - vr->vr.used); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vringh_kiov_init(&vvr->riov, NULL, 0); - vringh_kiov_init(&vvr->wiov, NULL, 0); - vvr->head = USHRT_MAX; - vvr->vdev = vdev; - vvr->vrh.notify = _vop_notify; - dev_dbg(&vpdev->dev, - "%s %d index %d va %p info %p vr_size 0x%x\n", - __func__, __LINE__, i, vr->va, vr->info, vr_size); - vvr->buf = (void *)__get_free_pages(GFP_KERNEL, - get_order(VOP_INT_DMA_BUF_SIZE)); - vvr->buf_da = dma_map_single(&vpdev->dev, - vvr->buf, VOP_INT_DMA_BUF_SIZE, - DMA_BIDIRECTIONAL); - } - - snprintf(irqname, sizeof(irqname), "vop%dvirtio%d", vpdev->index, - vdev->virtio_id); - vdev->virtio_db = vpdev->hw_ops->next_db(vpdev); - vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, - _vop_virtio_intr_handler, irqname, vdev, - vdev->virtio_db); - if (IS_ERR(vdev->virtio_cookie)) { - ret = PTR_ERR(vdev->virtio_cookie); - dev_dbg(&vpdev->dev, "request irq failed\n"); - goto err; - } - - vdev->dc->c2h_vdev_db = vdev->virtio_db; - - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - dd->type = type; - argp->type = type; - - if (bootparam) { - db = bootparam->h2c_config_db; - if (db != -1) - vpdev->hw_ops->send_intr(vpdev, db); - } - dev_dbg(&vpdev->dev, "Added virtio id %d db %d\n", dd->type, db); - return 0; -err: - vqconfig = mic_vq_config(dd); - for (j = 0; j < i; j++) { - struct vop_vringh *vvr = &vdev->vvr[j]; - - dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[j].address), - vvr->vring.len, DMA_BIDIRECTIONAL); - free_pages((unsigned long)vvr->vring.va, - get_order(vvr->vring.len)); - } - return ret; -} - -static void vop_dev_remove(struct vop_info *pvi, struct mic_device_ctrl *devp, - struct vop_device *vpdev) -{ - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - s8 db; - int ret, retry; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - - devp->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; - db = bootparam->h2c_config_db; - if (db != -1) - vpdev->hw_ops->send_intr(vpdev, db); - else - goto done; - for (retry = 15; retry--;) { - ret = wait_event_timeout(wake, devp->guest_ack, - msecs_to_jiffies(1000)); - if (ret) - break; - } -done: - devp->config_change = 0; - devp->guest_ack = 0; -} - -static void vop_virtio_del_device(struct vop_vdev *vdev) -{ - struct vop_info *vi = vdev->vi; - struct vop_device *vpdev = vdev->vpdev; - int i; - struct mic_vqconfig *vqconfig; - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - - if (!bootparam) - goto skip_hot_remove; - vop_dev_remove(vi, vdev->dc, vpdev); -skip_hot_remove: - vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); - flush_work(&vdev->virtio_bh_work); - vqconfig = mic_vq_config(vdev->dd); - for (i = 0; i < vdev->dd->num_vq; i++) { - struct vop_vringh *vvr = &vdev->vvr[i]; - - dma_unmap_single(&vpdev->dev, - vvr->buf_da, VOP_INT_DMA_BUF_SIZE, - DMA_BIDIRECTIONAL); - free_pages((unsigned long)vvr->buf, - get_order(VOP_INT_DMA_BUF_SIZE)); - vringh_kiov_cleanup(&vvr->riov); - vringh_kiov_cleanup(&vvr->wiov); - dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[i].address), - vvr->vring.len, DMA_BIDIRECTIONAL); - free_pages((unsigned long)vvr->vring.va, - get_order(vvr->vring.len)); - } - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - vdev->dd->type = -1; -} - -/* - * vop_sync_dma - Wrapper for synchronous DMAs. - * - * @dev - The address of the pointer to the device instance used - * for DMA registration. - * @dst - destination DMA address. - * @src - source DMA address. - * @len - size of the transfer. - * - * Return DMA_SUCCESS on success - */ -static int vop_sync_dma(struct vop_vdev *vdev, dma_addr_t dst, dma_addr_t src, - size_t len) -{ - int err = 0; - struct dma_device *ddev; - struct dma_async_tx_descriptor *tx; - struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); - struct dma_chan *vop_ch = vi->dma_ch; - - if (!vop_ch) { - err = -EBUSY; - goto error; - } - ddev = vop_ch->device; - tx = ddev->device_prep_dma_memcpy(vop_ch, dst, src, len, - DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - goto error; - } else { - dma_cookie_t cookie; - - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = -ENOMEM; - goto error; - } - dma_async_issue_pending(vop_ch); - err = dma_sync_wait(vop_ch, cookie); - } -error: - if (err) - dev_err(&vi->vpdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; -} - -#define VOP_USE_DMA true - -/* - * Initiates the copies across the PCIe bus from card memory to a user - * space buffer. When transfers are done using DMA, source/destination - * addresses and transfer length must follow the alignment requirements of - * the MIC DMA engine. - */ -static int vop_virtio_copy_to_user(struct vop_vdev *vdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct vop_device *vpdev = vdev->vpdev; - void __iomem *dbuf = vpdev->hw_ops->remap(vpdev, daddr, len); - struct vop_vringh *vvr = &vdev->vvr[vr_idx]; - struct vop_info *vi = dev_get_drvdata(&vpdev->dev); - size_t dma_alignment; - bool x200; - size_t dma_offset, partlen; - int err; - - if (!VOP_USE_DMA || !vi->dma_ch) { - if (copy_to_user(ubuf, (void __force *)dbuf, len)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - vdev->in_bytes += len; - err = 0; - goto err; - } - - dma_alignment = 1 << vi->dma_ch->device->copy_align; - x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); - - dma_offset = daddr - round_down(daddr, dma_alignment); - daddr -= dma_offset; - len += dma_offset; - /* - * X100 uses DMA addresses as seen by the card so adding - * the aperture base is not required for DMA. However x200 - * requires DMA addresses to be an offset into the bar so - * add the aperture base for x200. - */ - if (x200) - daddr += vpdev->aper->pa; - while (len) { - partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); - err = vop_sync_dma(vdev, vvr->buf_da, daddr, - ALIGN(partlen, dma_alignment)); - if (err) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - if (copy_to_user(ubuf, vvr->buf + dma_offset, - partlen - dma_offset)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - vdev->in_bytes_dma += partlen; - vdev->in_bytes += partlen; - len -= partlen; - dma_offset = 0; - } - err = 0; -err: - vpdev->hw_ops->unmap(vpdev, dbuf); - dev_dbg(vop_dev(vdev), - "%s: ubuf %p dbuf %p len 0x%zx vr_idx 0x%x\n", - __func__, ubuf, dbuf, len, vr_idx); - return err; -} - -/* - * Initiates copies across the PCIe bus from a user space buffer to card - * memory. When transfers are done using DMA, source/destination addresses - * and transfer length must follow the alignment requirements of the MIC - * DMA engine. - */ -static int vop_virtio_copy_from_user(struct vop_vdev *vdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct vop_device *vpdev = vdev->vpdev; - void __iomem *dbuf = vpdev->hw_ops->remap(vpdev, daddr, len); - struct vop_vringh *vvr = &vdev->vvr[vr_idx]; - struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); - size_t dma_alignment; - bool x200; - size_t partlen; - bool dma = VOP_USE_DMA && vi->dma_ch; - int err = 0; - size_t offset = 0; - - if (dma) { - dma_alignment = 1 << vi->dma_ch->device->copy_align; - x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); - - if (daddr & (dma_alignment - 1)) { - vdev->tx_dst_unaligned += len; - dma = false; - } else if (ALIGN(len, dma_alignment) > dlen) { - vdev->tx_len_unaligned += len; - dma = false; - } - } - - if (!dma) - goto memcpy; - - /* - * X100 uses DMA addresses as seen by the card so adding - * the aperture base is not required for DMA. However x200 - * requires DMA addresses to be an offset into the bar so - * add the aperture base for x200. - */ - if (x200) - daddr += vpdev->aper->pa; - while (len) { - partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); - - if (copy_from_user(vvr->buf, ubuf, partlen)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - err = vop_sync_dma(vdev, daddr, vvr->buf_da, - ALIGN(partlen, dma_alignment)); - if (err) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - vdev->out_bytes_dma += partlen; - vdev->out_bytes += partlen; - len -= partlen; - } -memcpy: - /* - * We are copying to IO below and should ideally use something - * like copy_from_user_toio(..) if it existed. - */ - while (len) { - partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); - - if (copy_from_user(vvr->buf, ubuf + offset, partlen)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - memcpy_toio(dbuf + offset, vvr->buf, partlen); - offset += partlen; - vdev->out_bytes += partlen; - len -= partlen; - } - err = 0; -err: - vpdev->hw_ops->unmap(vpdev, dbuf); - dev_dbg(vop_dev(vdev), - "%s: ubuf %p dbuf %p len 0x%zx vr_idx 0x%x\n", - __func__, ubuf, dbuf, len, vr_idx); - return err; -} - -#define MIC_VRINGH_READ true - -/* Determine the total number of bytes consumed in a VRINGH KIOV */ -static inline u32 vop_vringh_iov_consumed(struct vringh_kiov *iov) -{ - int i; - u32 total = iov->consumed; - - for (i = 0; i < iov->i; i++) - total += iov->iov[i].iov_len; - return total; -} - -/* - * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. - * This API is heavily based on the vringh_iov_xfer(..) implementation - * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) - * and vringh_iov_push_kern(..) directly is because there is no - * way to override the VRINGH xfer(..) routines as of v3.10. - */ -static int vop_vringh_copy(struct vop_vdev *vdev, struct vringh_kiov *iov, - void __user *ubuf, size_t len, bool read, int vr_idx, - size_t *out_len) -{ - int ret = 0; - size_t partlen, tot_len = 0; - - while (len && iov->i < iov->used) { - struct kvec *kiov = &iov->iov[iov->i]; - unsigned long daddr = (unsigned long)kiov->iov_base; - - partlen = min(kiov->iov_len, len); - if (read) - ret = vop_virtio_copy_to_user(vdev, ubuf, partlen, - daddr, - kiov->iov_len, - vr_idx); - else - ret = vop_virtio_copy_from_user(vdev, ubuf, partlen, - daddr, - kiov->iov_len, - vr_idx); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= partlen; - ubuf += partlen; - tot_len += partlen; - iov->consumed += partlen; - kiov->iov_len -= partlen; - kiov->iov_base += partlen; - if (!kiov->iov_len) { - /* Fix up old iov element then increment. */ - kiov->iov_len = iov->consumed; - kiov->iov_base -= iov->consumed; - - iov->consumed = 0; - iov->i++; - } - } - *out_len = tot_len; - return ret; -} - -/* - * Use the standard VRINGH infrastructure in the kernel to fetch new - * descriptors, initiate the copies and update the used ring. - */ -static int _vop_virtio_copy(struct vop_vdev *vdev, struct mic_copy_desc *copy) -{ - int ret = 0; - u32 iovcnt = copy->iovcnt; - struct iovec iov; - struct iovec __user *u_iov = copy->iov; - void __user *ubuf = NULL; - struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; - struct vringh_kiov *riov = &vvr->riov; - struct vringh_kiov *wiov = &vvr->wiov; - struct vringh *vrh = &vvr->vrh; - u16 *head = &vvr->head; - struct mic_vring *vr = &vvr->vring; - size_t len = 0, out_len; - - copy->out_len = 0; - /* Fetch a new IOVEC if all previous elements have been processed */ - if (riov->i == riov->used && wiov->i == wiov->used) { - ret = vringh_getdesc_kern(vrh, riov, wiov, - head, GFP_KERNEL); - /* Check if there are available descriptors */ - if (ret <= 0) - return ret; - } - while (iovcnt) { - if (!len) { - /* Copy over a new iovec from user space. */ - ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); - if (ret) { - ret = -EINVAL; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len = iov.iov_len; - ubuf = iov.iov_base; - } - /* Issue all the read descriptors first */ - ret = vop_vringh_copy(vdev, riov, ubuf, len, - MIC_VRINGH_READ, copy->vr_idx, &out_len); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - /* Issue the write descriptors next */ - ret = vop_vringh_copy(vdev, wiov, ubuf, len, - !MIC_VRINGH_READ, copy->vr_idx, &out_len); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - if (!len) { - /* One user space iovec is now completed */ - iovcnt--; - u_iov++; - } - /* Exit loop if all elements in KIOVs have been processed. */ - if (riov->i == riov->used && wiov->i == wiov->used) - break; - } - /* - * Update the used ring if a descriptor was available and some data was - * copied in/out and the user asked for a used ring update. - */ - if (*head != USHRT_MAX && copy->out_len && copy->update_used) { - u32 total = 0; - - /* Determine the total data consumed */ - total += vop_vringh_iov_consumed(riov); - total += vop_vringh_iov_consumed(wiov); - vringh_complete_kern(vrh, *head, total); - *head = USHRT_MAX; - if (vringh_need_notify_kern(vrh) > 0) - vringh_notify(vrh); - vringh_kiov_cleanup(riov); - vringh_kiov_cleanup(wiov); - /* Update avail idx for user space */ - vr->info->avail_idx = vrh->last_avail_idx; - } - return ret; -} - -static inline int vop_verify_copy_args(struct vop_vdev *vdev, - struct mic_copy_desc *copy) -{ - if (!vdev || copy->vr_idx >= vdev->dd->num_vq) - return -EINVAL; - return 0; -} - -/* Copy a specified number of virtio descriptors in a chain */ -static int vop_virtio_copy_desc(struct vop_vdev *vdev, - struct mic_copy_desc *copy) -{ - int err; - struct vop_vringh *vvr; - - err = vop_verify_copy_args(vdev, copy); - if (err) - return err; - - vvr = &vdev->vvr[copy->vr_idx]; - mutex_lock(&vvr->vr_mutex); - if (!vop_vdevup(vdev)) { - err = -ENODEV; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - err = _vop_virtio_copy(vdev, copy); - if (err) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - } -err: - mutex_unlock(&vvr->vr_mutex); - return err; -} - -static int vop_open(struct inode *inode, struct file *f) -{ - struct vop_vdev *vdev; - struct vop_info *vi = container_of(f->private_data, - struct vop_info, miscdev); - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) - return -ENOMEM; - vdev->vi = vi; - mutex_init(&vdev->vdev_mutex); - f->private_data = vdev; - init_completion(&vdev->destroy); - complete(&vdev->destroy); - return 0; -} - -static int vop_release(struct inode *inode, struct file *f) -{ - struct vop_vdev *vdev = f->private_data, *vdev_tmp; - struct vop_info *vi = vdev->vi; - struct list_head *pos, *tmp; - bool found = false; - - mutex_lock(&vdev->vdev_mutex); - if (vdev->deleted) - goto unlock; - mutex_lock(&vi->vop_mutex); - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev_tmp = list_entry(pos, struct vop_vdev, list); - if (vdev == vdev_tmp) { - vop_virtio_del_device(vdev); - list_del(pos); - found = true; - break; - } - } - mutex_unlock(&vi->vop_mutex); -unlock: - mutex_unlock(&vdev->vdev_mutex); - if (!found) - wait_for_completion(&vdev->destroy); - f->private_data = NULL; - kfree(vdev); - return 0; -} - -static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - struct vop_vdev *vdev = f->private_data; - struct vop_info *vi = vdev->vi; - void __user *argp = (void __user *)arg; - int ret; - - switch (cmd) { - case MIC_VIRTIO_ADD_DEVICE: - { - struct mic_device_desc dd, *dd_config; - - if (copy_from_user(&dd, argp, sizeof(dd))) - return -EFAULT; - - if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || - dd.num_vq > MIC_MAX_VRINGS) - return -EINVAL; - - dd_config = memdup_user(argp, mic_desc_size(&dd)); - if (IS_ERR(dd_config)) - return PTR_ERR(dd_config); - - /* Ensure desc has not changed between the two reads */ - if (memcmp(&dd, dd_config, sizeof(dd))) { - ret = -EINVAL; - goto free_ret; - } - mutex_lock(&vdev->vdev_mutex); - mutex_lock(&vi->vop_mutex); - ret = vop_virtio_add_device(vdev, dd_config); - if (ret) - goto unlock_ret; - list_add_tail(&vdev->list, &vi->vdev_list); -unlock_ret: - mutex_unlock(&vi->vop_mutex); - mutex_unlock(&vdev->vdev_mutex); -free_ret: - kfree(dd_config); - return ret; - } - case MIC_VIRTIO_COPY_DESC: - { - struct mic_copy_desc copy; - - mutex_lock(&vdev->vdev_mutex); - ret = vop_vdev_inited(vdev); - if (ret) - goto _unlock_ret; - - if (copy_from_user(©, argp, sizeof(copy))) { - ret = -EFAULT; - goto _unlock_ret; - } - - ret = vop_virtio_copy_desc(vdev, ©); - if (ret < 0) - goto _unlock_ret; - if (copy_to_user( - &((struct mic_copy_desc __user *)argp)->out_len, - ©.out_len, sizeof(copy.out_len))) - ret = -EFAULT; -_unlock_ret: - mutex_unlock(&vdev->vdev_mutex); - return ret; - } - case MIC_VIRTIO_CONFIG_CHANGE: - { - void *buf; - - mutex_lock(&vdev->vdev_mutex); - ret = vop_vdev_inited(vdev); - if (ret) - goto __unlock_ret; - buf = memdup_user(argp, vdev->dd->config_len); - if (IS_ERR(buf)) { - ret = PTR_ERR(buf); - goto __unlock_ret; - } - ret = vop_virtio_config_change(vdev, buf); - kfree(buf); -__unlock_ret: - mutex_unlock(&vdev->vdev_mutex); - return ret; - } - default: - return -ENOIOCTLCMD; - }; - return 0; -} - -/* - * We return EPOLLIN | EPOLLOUT from poll when new buffers are enqueued, and - * not when previously enqueued buffers may be available. This means that - * in the card->host (TX) path, when userspace is unblocked by poll it - * must drain all available descriptors or it can stall. - */ -static __poll_t vop_poll(struct file *f, poll_table *wait) -{ - struct vop_vdev *vdev = f->private_data; - __poll_t mask = 0; - - mutex_lock(&vdev->vdev_mutex); - if (vop_vdev_inited(vdev)) { - mask = EPOLLERR; - goto done; - } - poll_wait(f, &vdev->waitq, wait); - if (vop_vdev_inited(vdev)) { - mask = EPOLLERR; - } else if (vdev->poll_wake) { - vdev->poll_wake = 0; - mask = EPOLLIN | EPOLLOUT; - } -done: - mutex_unlock(&vdev->vdev_mutex); - return mask; -} - -static inline int -vop_query_offset(struct vop_vdev *vdev, unsigned long offset, - unsigned long *size, unsigned long *pa) -{ - struct vop_device *vpdev = vdev->vpdev; - unsigned long start = MIC_DP_SIZE; - int i; - - /* - * MMAP interface is as follows: - * offset region - * 0x0 virtio device_page - * 0x1000 first vring - * 0x1000 + size of 1st vring second vring - * .... - */ - if (!offset) { - *pa = virt_to_phys(vpdev->hw_ops->get_dp(vpdev)); - *size = MIC_DP_SIZE; - return 0; - } - - for (i = 0; i < vdev->dd->num_vq; i++) { - struct vop_vringh *vvr = &vdev->vvr[i]; - - if (offset == start) { - *pa = virt_to_phys(vvr->vring.va); - *size = vvr->vring.len; - return 0; - } - start += vvr->vring.len; - } - return -1; -} - -/* - * Maps the device page and virtio rings to user space for readonly access. - */ -static int vop_mmap(struct file *f, struct vm_area_struct *vma) -{ - struct vop_vdev *vdev = f->private_data; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; - int i, err; - - err = vop_vdev_inited(vdev); - if (err) - goto ret; - if (vma->vm_flags & VM_WRITE) { - err = -EACCES; - goto ret; - } - while (size_rem) { - i = vop_query_offset(vdev, offset, &size, &pa); - if (i < 0) { - err = -EINVAL; - goto ret; - } - err = remap_pfn_range(vma, vma->vm_start + offset, - pa >> PAGE_SHIFT, size, - vma->vm_page_prot); - if (err) - goto ret; - size_rem -= size; - offset += size; - } -ret: - return err; -} - -static const struct file_operations vop_fops = { - .open = vop_open, - .release = vop_release, - .unlocked_ioctl = vop_ioctl, - .poll = vop_poll, - .mmap = vop_mmap, - .owner = THIS_MODULE, -}; - -int vop_host_init(struct vop_info *vi) -{ - int rc; - struct miscdevice *mdev; - struct vop_device *vpdev = vi->vpdev; - - INIT_LIST_HEAD(&vi->vdev_list); - vi->dma_ch = vpdev->dma_ch; - mdev = &vi->miscdev; - mdev->minor = MISC_DYNAMIC_MINOR; - snprintf(vi->name, sizeof(vi->name), "vop_virtio%d", vpdev->index); - mdev->name = vi->name; - mdev->fops = &vop_fops; - mdev->parent = &vpdev->dev; - - rc = misc_register(mdev); - if (rc) - dev_err(&vpdev->dev, "%s failed rc %d\n", __func__, rc); - return rc; -} - -void vop_host_uninit(struct vop_info *vi) -{ - struct list_head *pos, *tmp; - struct vop_vdev *vdev; - - mutex_lock(&vi->vop_mutex); - vop_virtio_reset_devices(vi); - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev = list_entry(pos, struct vop_vdev, list); - list_del(pos); - reinit_completion(&vdev->destroy); - mutex_unlock(&vi->vop_mutex); - mutex_lock(&vdev->vdev_mutex); - vop_virtio_del_device(vdev); - vdev->deleted = true; - mutex_unlock(&vdev->vdev_mutex); - complete(&vdev->destroy); - mutex_lock(&vi->vop_mutex); - } - mutex_unlock(&vi->vop_mutex); - misc_deregister(&vi->miscdev); -} diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a30796e79b1c..6de02f09c322 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -5,6 +5,7 @@ * Copyright (c) 2007 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * Copyright (c) 2010 Pengutronix e.K. + * Copyright 2020 NXP * Author: Wolfram Sang <kernel@pengutronix.de> */ @@ -88,6 +89,7 @@ /* DLL Config 0 Register */ #define ESDHC_DLLCFG0 0x160 #define ESDHC_DLL_ENABLE 0x80000000 +#define ESDHC_DLL_RESET 0x40000000 #define ESDHC_DLL_FREQ_SEL 0x08000000 /* DLL Config 1 Register */ diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0b45eff6fed4..bb094459196a 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -4,6 +4,7 @@ * * Copyright (c) 2007, 2010, 2012 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. + * Copyright 2020 NXP * * Authors: Xiaobo Xie <X.Xie@freescale.com> * Anton Vorontsov <avorontsov@ru.mvista.com> @@ -19,6 +20,7 @@ #include <linux/clk.h> #include <linux/ktime.h> #include <linux/dma-mapping.h> +#include <linux/iopoll.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include "sdhci-pltfm.h" @@ -743,6 +745,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) if (host->mmc->actual_clock == MMC_HS200_MAX_DTR) temp |= ESDHC_DLL_FREQ_SEL; sdhci_writel(host, temp, ESDHC_DLLCFG0); + + temp |= ESDHC_DLL_RESET; + sdhci_writel(host, temp, ESDHC_DLLCFG0); + udelay(1); + temp &= ~ESDHC_DLL_RESET; + sdhci_writel(host, temp, ESDHC_DLLCFG0); + + /* Wait max 20 ms */ + if (read_poll_timeout(sdhci_readl, temp, + temp & ESDHC_DLL_STS_SLV_LOCK, + 10, 20000, false, + host, ESDHC_DLLSTAT0)) + pr_err("%s: timeout for delay chain lock.\n", + mmc_hostname(host->mmc)); + temp = sdhci_readl(host, ESDHC_TBCTL); sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL); @@ -1052,6 +1069,17 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) esdhc_tuning_block_enable(host, true); + /* + * The eSDHC controller takes the data timeout value into account + * during tuning. If the SD card is too slow sending the response, the + * timer will expire and a "Buffer Read Ready" interrupt without data + * is triggered. This leads to tuning errors. + * + * Just set the timeout to the maximum value because the core will + * already take care of it in sdhci_send_tuning(). + */ + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + hs400_tuning = host->flags & SDHCI_HS400_TUNING; do { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 592a55a34b58..3561ae8a481a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1384,9 +1384,11 @@ static inline void sdhci_auto_cmd_select(struct sdhci_host *host, /* * In case of Version 4.10 or later, use of 'Auto CMD Auto * Select' is recommended rather than use of 'Auto CMD12 - * Enable' or 'Auto CMD23 Enable'. + * Enable' or 'Auto CMD23 Enable'. We require Version 4 Mode + * here because some controllers (e.g sdhci-of-dwmshc) expect it. */ - if (host->version >= SDHCI_SPEC_410 && (use_cmd12 || use_cmd23)) { + if (host->version >= SDHCI_SPEC_410 && host->v4_mode && + (use_cmd12 || use_cmd23)) { *mode |= SDHCI_TRNS_AUTO_SEL; ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index fa147865e33f..7975f59735d6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1160,16 +1160,6 @@ static void bnxt_queue_sp_work(struct bnxt *bp) schedule_work(&bp->sp_task); } -static void bnxt_cancel_sp_work(struct bnxt *bp) -{ - if (BNXT_PF(bp)) { - flush_workqueue(bnxt_pf_wq); - } else { - cancel_work_sync(&bp->sp_task); - cancel_delayed_work_sync(&bp->fw_reset_task); - } -} - static void bnxt_sched_reset(struct bnxt *bp, struct bnxt_rx_ring_info *rxr) { if (!rxr->bnapi->in_reset) { @@ -4362,7 +4352,8 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len, u32 bar_offset = BNXT_GRCPF_REG_CHIMP_COMM; u16 dst = BNXT_HWRM_CHNL_CHIMP; - if (BNXT_NO_FW_ACCESS(bp)) + if (BNXT_NO_FW_ACCESS(bp) && + le16_to_cpu(req->req_type) != HWRM_FUNC_RESET) return -EBUSY; if (msg_len > BNXT_HWRM_MAX_REQ_LEN) { @@ -9789,7 +9780,10 @@ int bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) { int rc = 0; - rc = __bnxt_open_nic(bp, irq_re_init, link_re_init); + if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) + rc = -EIO; + if (!rc) + rc = __bnxt_open_nic(bp, irq_re_init, link_re_init); if (rc) { netdev_err(bp->dev, "nic open fail (rc: %x)\n", rc); dev_close(bp->dev); @@ -12108,15 +12102,17 @@ static void bnxt_remove_one(struct pci_dev *pdev) if (BNXT_PF(bp)) bnxt_sriov_disable(bp); - clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state); - bnxt_cancel_sp_work(bp); - bp->sp_event = 0; - - bnxt_dl_fw_reporters_destroy(bp, true); if (BNXT_PF(bp)) devlink_port_type_clear(&bp->dl_port); pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); + clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state); + /* Flush any pending tasks */ + cancel_work_sync(&bp->sp_task); + cancel_delayed_work_sync(&bp->fw_reset_task); + bp->sp_event = 0; + + bnxt_dl_fw_reporters_destroy(bp, true); bnxt_dl_unregister(bp); bnxt_shutdown_tc(bp); @@ -12860,6 +12856,9 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; } + if (state == pci_channel_io_frozen) + set_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state); + if (netif_running(netdev)) bnxt_close(netdev); @@ -12886,7 +12885,7 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct bnxt *bp = netdev_priv(netdev); - int err = 0; + int err = 0, off; pci_ers_result_t result = PCI_ERS_RESULT_DISCONNECT; netdev_info(bp->dev, "PCI Slot Reset\n"); @@ -12898,6 +12897,20 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) "Cannot re-enable PCI device after reset.\n"); } else { pci_set_master(pdev); + /* Upon fatal error, our device internal logic that latches to + * BAR value is getting reset and will restore only upon + * rewritting the BARs. + * + * As pci_restore_state() does not re-write the BARs if the + * value is same as saved value earlier, driver needs to + * write the BARs to 0 to force restore, in case of fatal error. + */ + if (test_and_clear_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, + &bp->state)) { + for (off = PCI_BASE_ADDRESS_0; + off <= PCI_BASE_ADDRESS_5; off += 4) + pci_write_config_dword(bp->pdev, off, 0); + } pci_restore_state(pdev); pci_save_state(pdev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 21ef1c21f602..47b3c3127879 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1781,6 +1781,7 @@ struct bnxt { #define BNXT_STATE_ABORT_ERR 5 #define BNXT_STATE_FW_FATAL_COND 6 #define BNXT_STATE_DRV_REGISTERED 7 +#define BNXT_STATE_PCI_CHANNEL_IO_FROZEN 8 #define BNXT_NO_FW_ACCESS(bp) \ (test_bit(BNXT_STATE_FW_FATAL_COND, &(bp)->state) || \ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c index 6ec5f2f26f05..4e55f7081644 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c @@ -145,13 +145,13 @@ static int configure_filter_smac(struct adapter *adap, struct filter_entry *f) int err; /* do a set-tcb for smac-sel and CWR bit.. */ - err = set_tcb_tflag(adap, f, f->tid, TF_CCTRL_CWR_S, 1, 1); - if (err) - goto smac_err; - err = set_tcb_field(adap, f, f->tid, TCB_SMAC_SEL_W, TCB_SMAC_SEL_V(TCB_SMAC_SEL_M), TCB_SMAC_SEL_V(f->smt->idx), 1); + if (err) + goto smac_err; + + err = set_tcb_tflag(adap, f, f->tid, TF_CCTRL_CWR_S, 1, 1); if (!err) return 0; @@ -862,6 +862,7 @@ int set_filter_wr(struct adapter *adapter, int fidx) FW_FILTER_WR_DIRSTEERHASH_V(f->fs.dirsteerhash) | FW_FILTER_WR_LPBK_V(f->fs.action == FILTER_SWITCH) | FW_FILTER_WR_DMAC_V(f->fs.newdmac) | + FW_FILTER_WR_SMAC_V(f->fs.newsmac) | FW_FILTER_WR_INSVLAN_V(f->fs.newvlan == VLAN_INSERT || f->fs.newvlan == VLAN_REWRITE) | FW_FILTER_WR_RMVLAN_V(f->fs.newvlan == VLAN_REMOVE || @@ -879,7 +880,7 @@ int set_filter_wr(struct adapter *adapter, int fidx) FW_FILTER_WR_OVLAN_VLD_V(f->fs.val.ovlan_vld) | FW_FILTER_WR_IVLAN_VLDM_V(f->fs.mask.ivlan_vld) | FW_FILTER_WR_OVLAN_VLDM_V(f->fs.mask.ovlan_vld)); - fwr->smac_sel = 0; + fwr->smac_sel = f->smt->idx; fwr->rx_chan_rx_rpl_iq = htons(FW_FILTER_WR_RX_CHAN_V(0) | FW_FILTER_WR_RX_RPL_IQ_V(adapter->sge.fw_evtq.abs_id)); @@ -1323,11 +1324,8 @@ static void mk_act_open_req6(struct filter_entry *f, struct sk_buff *skb, TX_QUEUE_V(f->fs.nat_mode) | T5_OPT_2_VALID_F | RX_CHANNEL_V(cxgb4_port_e2cchan(f->dev)) | - CONG_CNTRL_V((f->fs.action == FILTER_DROP) | - (f->fs.dirsteer << 1)) | PACE_V((f->fs.maskhash) | - ((f->fs.dirsteerhash) << 1)) | - CCTRL_ECN_V(f->fs.action == FILTER_SWITCH)); + ((f->fs.dirsteerhash) << 1))); } static void mk_act_open_req(struct filter_entry *f, struct sk_buff *skb, @@ -1363,11 +1361,8 @@ static void mk_act_open_req(struct filter_entry *f, struct sk_buff *skb, TX_QUEUE_V(f->fs.nat_mode) | T5_OPT_2_VALID_F | RX_CHANNEL_V(cxgb4_port_e2cchan(f->dev)) | - CONG_CNTRL_V((f->fs.action == FILTER_DROP) | - (f->fs.dirsteer << 1)) | PACE_V((f->fs.maskhash) | - ((f->fs.dirsteerhash) << 1)) | - CCTRL_ECN_V(f->fs.action == FILTER_SWITCH)); + ((f->fs.dirsteerhash) << 1))); } static int cxgb4_set_hash_filter(struct net_device *dev, @@ -2039,6 +2034,20 @@ void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl) } return; } + switch (f->fs.action) { + case FILTER_PASS: + if (f->fs.dirsteer) + set_tcb_tflag(adap, f, tid, + TF_DIRECT_STEER_S, 1, 1); + break; + case FILTER_DROP: + set_tcb_tflag(adap, f, tid, TF_DROP_S, 1, 1); + break; + case FILTER_SWITCH: + set_tcb_tflag(adap, f, tid, TF_LPBK_S, 1, 1); + break; + } + break; default: @@ -2106,22 +2115,11 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) if (ctx) ctx->result = 0; } else if (ret == FW_FILTER_WR_FLT_ADDED) { - int err = 0; - - if (f->fs.newsmac) - err = configure_filter_smac(adap, f); - - if (!err) { - f->pending = 0; /* async setup completed */ - f->valid = 1; - if (ctx) { - ctx->result = 0; - ctx->tid = idx; - } - } else { - clear_filter(adap, f); - if (ctx) - ctx->result = err; + f->pending = 0; /* async setup completed */ + f->valid = 1; + if (ctx) { + ctx->result = 0; + ctx->tid = idx; } } else { /* Something went wrong. Issue a warning about the diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h index 50232e063f49..92473dda55d9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h @@ -50,6 +50,10 @@ #define TCB_T_FLAGS_M 0xffffffffffffffffULL #define TCB_T_FLAGS_V(x) ((__u64)(x) << TCB_T_FLAGS_S) +#define TF_DROP_S 22 +#define TF_DIRECT_STEER_S 23 +#define TF_LPBK_S 59 + #define TF_CCTRL_ECE_S 60 #define TF_CCTRL_CWR_S 61 #define TF_CCTRL_RFR_S 62 diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c index ec4f79049a06..d581c4e623f8 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c @@ -772,14 +772,13 @@ static int chtls_pass_open_rpl(struct chtls_dev *cdev, struct sk_buff *skb) if (rpl->status != CPL_ERR_NONE) { pr_info("Unexpected PASS_OPEN_RPL status %u for STID %u\n", rpl->status, stid); - return CPL_RET_BUF_DONE; + } else { + cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); + sock_put(listen_ctx->lsk); + kfree(listen_ctx); + module_put(THIS_MODULE); } - cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); - sock_put(listen_ctx->lsk); - kfree(listen_ctx); - module_put(THIS_MODULE); - - return 0; + return CPL_RET_BUF_DONE; } static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb) @@ -796,15 +795,13 @@ static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb) if (rpl->status != CPL_ERR_NONE) { pr_info("Unexpected CLOSE_LISTSRV_RPL status %u for STID %u\n", rpl->status, stid); - return CPL_RET_BUF_DONE; + } else { + cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); + sock_put(listen_ctx->lsk); + kfree(listen_ctx); + module_put(THIS_MODULE); } - - cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); - sock_put(listen_ctx->lsk); - kfree(listen_ctx); - module_put(THIS_MODULE); - - return 0; + return CPL_RET_BUF_DONE; } static void chtls_purge_wr_queue(struct sock *sk) @@ -1514,7 +1511,6 @@ static void add_to_reap_list(struct sock *sk) struct chtls_sock *csk = sk->sk_user_data; local_bh_disable(); - bh_lock_sock(sk); release_tcp_port(sk); /* release the port immediately */ spin_lock(&reap_list_lock); @@ -1523,7 +1519,6 @@ static void add_to_reap_list(struct sock *sk) if (!csk->passive_reap_next) schedule_work(&reap_task); spin_unlock(&reap_list_lock); - bh_unlock_sock(sk); local_bh_enable(); } diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c index 9fb5ca6682ea..188d871f6b8c 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c @@ -1585,6 +1585,7 @@ skip_copy: tp->urg_data = 0; if ((avail + offset) >= skb->len) { + struct sk_buff *next_skb; if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_TLS_HDR) { tp->copied_seq += skb->len; hws->rcvpld = skb->hdr_len; @@ -1595,8 +1596,10 @@ skip_copy: chtls_free_skb(sk, skb); buffers_freed++; hws->copied_seq = 0; - if (copied >= target && - !skb_peek(&sk->sk_receive_queue)) + next_skb = skb_peek(&sk->sk_receive_queue); + if (copied >= target && !next_skb) + break; + if (ULP_SKB_CB(next_skb)->flags & ULPCB_FLAG_TLS_HDR) break; } } while (len > 0); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index c81be32bcedf..827f74e86d34 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -402,7 +402,7 @@ struct enetc_psfp_gate { u32 num_entries; refcount_t refcount; struct hlist_node node; - struct action_gate_entry entries[0]; + struct action_gate_entry entries[]; }; /* Only enable the green color frame now diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 714b501be7d0..ba8869c3d891 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -1358,7 +1358,7 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UCC_GETH_UPSMR_TBIM; } - if ((ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII)) + if (ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII) upsmr |= UCC_GETH_UPSMR_SGMM; out_be32(&uf_regs->upsmr, upsmr); diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h index 281de8326bc5..015796a20118 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.h +++ b/drivers/net/ethernet/google/gve/gve_adminq.h @@ -198,7 +198,7 @@ static_assert(sizeof(struct stats) == 16); struct gve_stats_report { __be64 written_count; - struct stats stats[0]; + struct stats stats[]; }; static_assert(sizeof(struct gve_stats_report) == 8); diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 48a433154ce0..02e7d74779f4 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -116,9 +116,8 @@ static int gve_alloc_stats_report(struct gve_priv *priv) priv->tx_cfg.num_queues; rx_stats_num = (GVE_RX_STATS_REPORT_NUM + NIC_RX_STATS_REPORT_NUM) * priv->rx_cfg.num_queues; - priv->stats_report_len = sizeof(struct gve_stats_report) + - (tx_stats_num + rx_stats_num) * - sizeof(struct stats); + priv->stats_report_len = struct_size(priv->stats_report, stats, + tx_stats_num + rx_stats_num); priv->stats_report = dma_alloc_coherent(&priv->pdev->dev, priv->stats_report_len, &priv->stats_report_bus, GFP_KERNEL); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index 15f69fa86323..e8495f58a1a8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -1373,7 +1373,7 @@ static int hclge_tm_bp_setup(struct hclge_dev *hdev) return ret; } - return ret; + return 0; } int hclge_pause_setup_hw(struct hclge_dev *hdev, bool init) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 50c84c5e65d2..c8e3fdd5999c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -3262,8 +3262,8 @@ static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev) hclgevf_uninit_msi(hdev); } - hclgevf_pci_uninit(hdev); hclgevf_cmd_uninit(hdev); + hclgevf_pci_uninit(hdev); hclgevf_uninit_mac_list(hdev); } diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 7ef3369953b6..c3ec9ceed833 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1031,12 +1031,6 @@ static int ibmveth_is_packet_unsupported(struct sk_buff *skb, ret = -EOPNOTSUPP; } - if (!ether_addr_equal(ether_header->h_source, netdev->dev_addr)) { - netdev_dbg(netdev, "source packet MAC address does not match veth device's, dropping packet.\n"); - netdev->stats.tx_dropped++; - ret = -EOPNOTSUPP; - } - return ret; } diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 8148f796a807..af4dfbe28d56 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1815,9 +1815,13 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) int rc; rc = 0; - ether_addr_copy(adapter->mac_addr, addr->sa_data); - if (adapter->state != VNIC_PROBED) + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (adapter->state != VNIC_PROBED) { + ether_addr_copy(adapter->mac_addr, addr->sa_data); rc = __ibmvnic_set_mac(netdev, addr->sa_data); + } return rc; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h index d046db7bb047..3a9fa629503f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h @@ -90,9 +90,4 @@ int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, u32 key_type, u32 *p_key_id); void mlx5_destroy_encryption_key(struct mlx5_core_dev *mdev, u32 key_id); -static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev) -{ - return devlink_net(priv_to_devlink(dev)); -} - #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 7f77c2a71d1c..937b8e46f8c7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -620,6 +620,9 @@ static void mlxsw_emad_transmit_retry(struct mlxsw_core *mlxsw_core, err = mlxsw_emad_transmit(trans->core, trans); if (err == 0) return; + + if (!atomic_dec_and_test(&trans->active)) + return; } else { err = -EIO; } @@ -2064,6 +2067,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, if (!reload) devlink_resources_unregister(devlink, NULL); mlxsw_core->bus->fini(mlxsw_core->bus_priv); + if (!reload) + devlink_free(devlink); return; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 16b47fce540b..b08853f71b2b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1174,11 +1174,14 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port) u32 eth_proto_cap, eth_proto_admin, eth_proto_oper; const struct mlxsw_sp_port_type_speed_ops *ops; char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 eth_proto_cap_masked; int err; ops = mlxsw_sp->port_type_speed_ops; - /* Set advertised speeds to supported speeds. */ + /* Set advertised speeds to speeds supported by both the driver + * and the device. + */ ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, 0, false); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); @@ -1187,8 +1190,10 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port) ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, ð_proto_cap, ð_proto_admin, ð_proto_oper); + eth_proto_cap_masked = ops->ptys_proto_cap_masked_get(eth_proto_cap); ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, - eth_proto_cap, mlxsw_sp_port->link.autoneg); + eth_proto_cap_masked, + mlxsw_sp_port->link.autoneg); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3e26eb6cb140..74b3959b36d4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -342,6 +342,7 @@ struct mlxsw_sp_port_type_speed_ops { u32 *p_eth_proto_cap, u32 *p_eth_proto_admin, u32 *p_eth_proto_oper); + u32 (*ptys_proto_cap_masked_get)(u32 eth_proto_cap); }; static inline struct net_device * diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 2096b6478958..540616469e28 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -1303,6 +1303,20 @@ mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload, p_eth_proto_oper); } +static u32 mlxsw_sp1_ptys_proto_cap_masked_get(u32 eth_proto_cap) +{ + u32 ptys_proto_cap_masked = 0; + int i; + + for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { + if (mlxsw_sp1_port_link_mode[i].mask & eth_proto_cap) + ptys_proto_cap_masked |= + mlxsw_sp1_port_link_mode[i].mask; + } + + return ptys_proto_cap_masked; +} + const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = { .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port, .from_ptys_link = mlxsw_sp1_from_ptys_link, @@ -1313,6 +1327,7 @@ const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = { .to_ptys_speed = mlxsw_sp1_to_ptys_speed, .reg_ptys_eth_pack = mlxsw_sp1_reg_ptys_eth_pack, .reg_ptys_eth_unpack = mlxsw_sp1_reg_ptys_eth_unpack, + .ptys_proto_cap_masked_get = mlxsw_sp1_ptys_proto_cap_masked_get, }; static const enum ethtool_link_mode_bit_indices @@ -1731,6 +1746,20 @@ mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload, p_eth_proto_admin, p_eth_proto_oper); } +static u32 mlxsw_sp2_ptys_proto_cap_masked_get(u32 eth_proto_cap) +{ + u32 ptys_proto_cap_masked = 0; + int i; + + for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { + if (mlxsw_sp2_port_link_mode[i].mask & eth_proto_cap) + ptys_proto_cap_masked |= + mlxsw_sp2_port_link_mode[i].mask; + } + + return ptys_proto_cap_masked; +} + const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = { .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port, .from_ptys_link = mlxsw_sp2_from_ptys_link, @@ -1741,4 +1770,5 @@ const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = { .to_ptys_speed = mlxsw_sp2_to_ptys_speed, .reg_ptys_eth_pack = mlxsw_sp2_reg_ptys_eth_pack, .reg_ptys_eth_unpack = mlxsw_sp2_reg_ptys_eth_unpack, + .ptys_proto_cap_masked_get = mlxsw_sp2_ptys_proto_cap_masked_get, }; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index 545c99b15df8..dc5fbc2704f3 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -333,7 +333,7 @@ int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data) union ionic_dev_cmd cmd = { .vf_setattr.opcode = IONIC_CMD_VF_SETATTR, .vf_setattr.attr = attr, - .vf_setattr.vf_index = vf, + .vf_setattr.vf_index = cpu_to_le16(vf), }; int err; @@ -391,7 +391,7 @@ void ionic_dev_cmd_queue_identify(struct ionic_dev *idev, { union ionic_dev_cmd cmd = { .q_identify.opcode = IONIC_CMD_Q_IDENTIFY, - .q_identify.lif_type = lif_type, + .q_identify.lif_type = cpu_to_le16(lif_type), .q_identify.type = qtype, .q_identify.ver = qver, }; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index c109cd5a0471..6c243b17312c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -29,6 +29,7 @@ struct ionic_dev_bar { int res_index; }; +#ifndef __CHECKER__ /* Registers */ static_assert(sizeof(struct ionic_intr) == 32); @@ -119,6 +120,7 @@ static_assert(sizeof(struct ionic_vf_setattr_cmd) == 64); static_assert(sizeof(struct ionic_vf_setattr_comp) == 16); static_assert(sizeof(struct ionic_vf_getattr_cmd) == 64); static_assert(sizeof(struct ionic_vf_getattr_comp) == 16); +#endif /* __CHECKER__ */ struct ionic_devinfo { u8 asic_type; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_fw.c b/drivers/net/ethernet/pensando/ionic/ionic_fw.c index f492ae406a60..d7bbf336c6f6 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_fw.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_fw.c @@ -27,9 +27,9 @@ static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr, { union ionic_dev_cmd cmd = { .fw_download.opcode = IONIC_CMD_FW_DOWNLOAD, - .fw_download.offset = offset, - .fw_download.addr = addr, - .fw_download.length = length + .fw_download.offset = cpu_to_le32(offset), + .fw_download.addr = cpu_to_le64(addr), + .fw_download.length = cpu_to_le32(length), }; ionic_dev_cmd_go(idev, &cmd); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index d655a7ae3058..a12df3946a07 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -1656,7 +1656,6 @@ static void ionic_txrx_deinit(struct ionic_lif *lif) if (lif->rxqcqs) { for (i = 0; i < lif->nxqs && lif->rxqcqs[i]; i++) { ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]); - ionic_rx_flush(&lif->rxqcqs[i]->cq); ionic_rx_empty(&lif->rxqcqs[i]->q); } } @@ -1915,11 +1914,11 @@ static int ionic_get_vf_config(struct net_device *netdev, ret = -EINVAL; } else { ivf->vf = vf; - ivf->vlan = ionic->vfs[vf].vlanid; + ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); ivf->qos = 0; ivf->spoofchk = ionic->vfs[vf].spoofchk; ivf->linkstate = ionic->vfs[vf].linkstate; - ivf->max_tx_rate = ionic->vfs[vf].maxrate; + ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); ivf->trusted = ionic->vfs[vf].trusted; ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); } @@ -2019,7 +2018,7 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_VLAN, (u8 *)&vlan); if (!ret) - ionic->vfs[vf].vlanid = vlan; + ionic->vfs[vf].vlanid = cpu_to_le16(vlan); } up_write(&ionic->vf_op_lock); @@ -2048,7 +2047,7 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf, ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_RATE, (u8 *)&tx_max); if (!ret) - lif->ionic->vfs[vf].maxrate = tx_max; + lif->ionic->vfs[vf].maxrate = cpu_to_le32(tx_max); } up_write(&ionic->vf_op_lock); @@ -2981,14 +2980,14 @@ void ionic_lif_unregister(struct ionic_lif *lif) static void ionic_lif_queue_identify(struct ionic_lif *lif) { + union ionic_q_identity __iomem *q_ident; struct ionic *ionic = lif->ionic; - union ionic_q_identity *q_ident; struct ionic_dev *idev; int qtype; int err; idev = &lif->ionic->idev; - q_ident = (union ionic_q_identity *)&idev->dev_cmd_regs->data; + q_ident = (union ionic_q_identity __iomem *)&idev->dev_cmd_regs->data; for (qtype = 0; qtype < ARRAY_SIZE(ionic_qtype_versions); qtype++) { struct ionic_qtype_info *qti = &lif->qtype_info[qtype]; @@ -3011,14 +3010,14 @@ static void ionic_lif_queue_identify(struct ionic_lif *lif) ionic_qtype_versions[qtype]); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); if (!err) { - qti->version = q_ident->version; - qti->supported = q_ident->supported; - qti->features = le64_to_cpu(q_ident->features); - qti->desc_sz = le16_to_cpu(q_ident->desc_sz); - qti->comp_sz = le16_to_cpu(q_ident->comp_sz); - qti->sg_desc_sz = le16_to_cpu(q_ident->sg_desc_sz); - qti->max_sg_elems = le16_to_cpu(q_ident->max_sg_elems); - qti->sg_desc_stride = le16_to_cpu(q_ident->sg_desc_stride); + qti->version = readb(&q_ident->version); + qti->supported = readb(&q_ident->supported); + qti->features = readq(&q_ident->features); + qti->desc_sz = readw(&q_ident->desc_sz); + qti->comp_sz = readw(&q_ident->comp_sz); + qti->sg_desc_sz = readw(&q_ident->sg_desc_sz); + qti->max_sg_elems = readw(&q_ident->max_sg_elems); + qti->sg_desc_stride = readw(&q_ident->sg_desc_stride); } mutex_unlock(&ionic->dev_cmd_lock); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index ee0740881af3..d355676f6c16 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -311,7 +311,7 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) static void ionic_dev_cmd_clean(struct ionic *ionic) { - union ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; + union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; iowrite32(0, ®s->doorbell); memset_io(®s->cmd, 0, sizeof(regs->cmd)); @@ -333,7 +333,7 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) */ max_wait = jiffies + (max_seconds * HZ); try_again: - opcode = idev->dev_cmd_regs->cmd.cmd.opcode; + opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode); start_time = jiffies; do { done = ionic_dev_cmd_done(idev); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.h b/drivers/net/ethernet/pensando/ionic/ionic_stats.h index 3f543512616e..2a725834f792 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.h @@ -49,7 +49,7 @@ extern const int ionic_num_stats_grps; (*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset))) #define IONIC_READ_STAT_LE64(base_ptr, desc_ptr) \ - __le64_to_cpu(*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset))) + __le64_to_cpu(*((__le64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset))) struct ionic_stat_desc { char name[ETH_GSTRING_LEN]; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 169ac4f54640..b3d2250c77d0 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -200,7 +200,7 @@ static void ionic_rx_clean(struct ionic_queue *q, if (likely(netdev->features & NETIF_F_RXCSUM)) { if (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_CALC) { skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum = (__wsum)le16_to_cpu(comp->csum); + skb->csum = (__force __wsum)le16_to_cpu(comp->csum); stats->csum_complete++; } } else { @@ -253,19 +253,6 @@ static bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) return true; } -void ionic_rx_flush(struct ionic_cq *cq) -{ - struct ionic_dev *idev = &cq->lif->ionic->idev; - u32 work_done; - - work_done = ionic_cq_service(cq, cq->num_descs, - ionic_rx_service, NULL, NULL); - - if (work_done) - ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index, - work_done, IONIC_INTR_CRED_RESET_COALESCE); -} - static int ionic_rx_page_alloc(struct ionic_queue *q, struct ionic_page_info *page_info) { @@ -413,22 +400,20 @@ static void ionic_rx_fill_cb(void *arg) void ionic_rx_empty(struct ionic_queue *q) { struct ionic_desc_info *desc_info; - struct ionic_rxq_desc *desc; - unsigned int i; - u16 idx; - - idx = q->tail_idx; - while (idx != q->head_idx) { - desc_info = &q->info[idx]; - desc = desc_info->desc; - desc->addr = 0; - desc->len = 0; + struct ionic_page_info *page_info; + unsigned int i, j; - for (i = 0; i < desc_info->npages; i++) - ionic_rx_page_free(q, &desc_info->pages[i]); + for (i = 0; i < q->num_descs; i++) { + desc_info = &q->info[i]; + for (j = 0; j < IONIC_RX_MAX_SG_ELEMS + 1; j++) { + page_info = &desc_info->pages[j]; + if (page_info->page) + ionic_rx_page_free(q, page_info); + } + desc_info->npages = 0; + desc_info->cb = NULL; desc_info->cb_arg = NULL; - idx = (idx + 1) & (q->num_descs - 1); } } @@ -812,6 +797,7 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) skb_frag_t *frag; bool start, done; bool outer_csum; + dma_addr_t addr; bool has_vlan; u16 desc_len; u8 desc_nsge; @@ -893,11 +879,10 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) if (frag_left > 0) { len = min(frag_left, left); frag_left -= len; - elem->addr = - cpu_to_le64(ionic_tx_map_frag(q, frag, - offset, len)); - if (dma_mapping_error(dev, elem->addr)) + addr = ionic_tx_map_frag(q, frag, offset, len); + if (dma_mapping_error(dev, addr)) goto err_out_abort; + elem->addr = cpu_to_le64(addr); elem->len = cpu_to_le16(len); elem++; desc_nsge++; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h index a5883be0413f..7667b72232b8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h @@ -4,7 +4,6 @@ #ifndef _IONIC_TXRX_H_ #define _IONIC_TXRX_H_ -void ionic_rx_flush(struct ionic_cq *cq); void ionic_tx_flush(struct ionic_cq *cq); void ionic_rx_fill(struct ionic_queue *q); diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 3b6ddc706e92..00f13805c6f7 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4573,7 +4573,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) } rtl_irq_disable(tp); - napi_schedule_irqoff(&tp->napi); + napi_schedule(&tp->napi); out: rtl_ack_events(tp, status); @@ -4746,7 +4746,7 @@ static int rtl_open(struct net_device *dev) rtl_request_firmware(tp); retval = request_irq(pci_irq_vector(pdev, 0), rtl8169_interrupt, - IRQF_NO_THREAD | IRQF_SHARED, dev->name, tp); + IRQF_SHARED, dev->name, tp); if (retval < 0) goto err_release_fw_2; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 9c4df4ede011..bd30505fbc57 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1744,12 +1744,16 @@ static int ravb_hwtstamp_get(struct net_device *ndev, struct ifreq *req) config.flags = 0; config.tx_type = priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; - if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_V2_L2_EVENT) + switch (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE) { + case RAVB_RXTSTAMP_TYPE_V2_L2_EVENT: config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; - else if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_ALL) + break; + case RAVB_RXTSTAMP_TYPE_ALL: config.rx_filter = HWTSTAMP_FILTER_ALL; - else + break; + default: config.rx_filter = HWTSTAMP_FILTER_NONE; + } return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 030a1a5afe05..dc668ed280b9 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -657,10 +657,6 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, gtp = netdev_priv(dev); - err = gtp_encap_enable(gtp, data); - if (err < 0) - return err; - if (!data[IFLA_GTP_PDP_HASHSIZE]) { hashsize = 1024; } else { @@ -671,12 +667,16 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, err = gtp_hashtable_new(gtp, hashsize); if (err < 0) - goto out_encap; + return err; + + err = gtp_encap_enable(gtp, data); + if (err < 0) + goto out_hashtable; err = register_netdevice(dev); if (err < 0) { netdev_dbg(dev, "failed to register new netdev %d\n", err); - goto out_hashtable; + goto out_encap; } gn = net_generic(dev_net(dev), gtp_net_id); @@ -687,11 +687,11 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, return 0; +out_encap: + gtp_encap_disable(gtp); out_hashtable: kfree(gtp->addr_hash); kfree(gtp->tid_hash); -out_encap: - gtp_encap_disable(gtp); return err; } diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c index 43f5f5d93cb0..92642030e735 100644 --- a/drivers/net/ipa/gsi_trans.c +++ b/drivers/net/ipa/gsi_trans.c @@ -397,15 +397,24 @@ void gsi_trans_cmd_add(struct gsi_trans *trans, void *buf, u32 size, /* assert(which < trans->tre_count); */ - /* Set the page information for the buffer. We also need to fill in - * the DMA address and length for the buffer (something dma_map_sg() - * normally does). + /* Commands are quite different from data transfer requests. + * Their payloads come from a pool whose memory is allocated + * using dma_alloc_coherent(). We therefore do *not* map them + * for DMA (unlike what we do for pages and skbs). + * + * When a transaction completes, the SGL is normally unmapped. + * A command transaction has direction DMA_NONE, which tells + * gsi_trans_complete() to skip the unmapping step. + * + * The only things we use directly in a command scatter/gather + * entry are the DMA address and length. We still need the SG + * table flags to be maintained though, so assign a NULL page + * pointer for that purpose. */ sg = &trans->sgl[which]; - - sg_set_buf(sg, buf, size); + sg_assign_page(sg, NULL); sg_dma_address(sg) = addr; - sg_dma_len(sg) = sg->length; + sg_dma_len(sg) = size; info = &trans->info[which]; info->opcode = opcode; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 95ef4943d8bd..376096bfc54a 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2125,7 +2125,7 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_id_ns *id) if (blk_queue_is_zoned(ns->queue)) { ret = nvme_revalidate_zones(ns); - if (ret) + if (ret && !nvme_first_scan(ns->disk)) return ret; } diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 3c002bdcace3..f4c246462658 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -146,7 +146,8 @@ struct nvme_fc_rport { /* fc_ctrl flags values - specified as bit positions */ #define ASSOC_ACTIVE 0 -#define FCCTRL_TERMIO 1 +#define ASSOC_FAILED 1 +#define FCCTRL_TERMIO 2 struct nvme_fc_ctrl { spinlock_t lock; @@ -157,7 +158,6 @@ struct nvme_fc_ctrl { u32 cnum; bool ioq_live; - atomic_t err_work_active; u64 association_id; struct nvmefc_ls_rcv_op *rcv_disconn; @@ -167,7 +167,6 @@ struct nvme_fc_ctrl { struct blk_mq_tag_set tag_set; struct delayed_work connect_work; - struct work_struct err_work; struct kref ref; unsigned long flags; @@ -2414,24 +2413,97 @@ nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl) nvme_fc_ctrl_put(ctrl); } +/* + * This routine is used by the transport when it needs to find active + * io on a queue that is to be terminated. The transport uses + * blk_mq_tagset_busy_itr() to find the busy requests, which then invoke + * this routine to kill them on a 1 by 1 basis. + * + * As FC allocates FC exchange for each io, the transport must contact + * the LLDD to terminate the exchange, thus releasing the FC exchange. + * After terminating the exchange the LLDD will call the transport's + * normal io done path for the request, but it will have an aborted + * status. The done path will return the io request back to the block + * layer with an error status. + */ +static bool +nvme_fc_terminate_exchange(struct request *req, void *data, bool reserved) +{ + struct nvme_ctrl *nctrl = data; + struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); + struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req); + + __nvme_fc_abort_op(ctrl, op); + return true; +} + +/* + * This routine runs through all outstanding commands on the association + * and aborts them. This routine is typically be called by the + * delete_association routine. It is also called due to an error during + * reconnect. In that scenario, it is most likely a command that initializes + * the controller, including fabric Connect commands on io queues, that + * may have timed out or failed thus the io must be killed for the connect + * thread to see the error. + */ static void -nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg) +__nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) { - int active; + /* + * If io queues are present, stop them and terminate all outstanding + * ios on them. As FC allocates FC exchange for each io, the + * transport must contact the LLDD to terminate the exchange, + * thus releasing the FC exchange. We use blk_mq_tagset_busy_itr() + * to tell us what io's are busy and invoke a transport routine + * to kill them with the LLDD. After terminating the exchange + * the LLDD will call the transport's normal io done path, but it + * will have an aborted status. The done path will return the + * io requests back to the block layer as part of normal completions + * (but with error status). + */ + if (ctrl->ctrl.queue_count > 1) { + nvme_stop_queues(&ctrl->ctrl); + blk_mq_tagset_busy_iter(&ctrl->tag_set, + nvme_fc_terminate_exchange, &ctrl->ctrl); + blk_mq_tagset_wait_completed_request(&ctrl->tag_set); + if (start_queues) + nvme_start_queues(&ctrl->ctrl); + } + + /* + * Other transports, which don't have link-level contexts bound + * to sqe's, would try to gracefully shutdown the controller by + * writing the registers for shutdown and polling (call + * nvme_shutdown_ctrl()). Given a bunch of i/o was potentially + * just aborted and we will wait on those contexts, and given + * there was no indication of how live the controlelr is on the + * link, don't send more io to create more contexts for the + * shutdown. Let the controller fail via keepalive failure if + * its still present. + */ /* - * if an error (io timeout, etc) while (re)connecting, - * it's an error on creating the new association. - * Start the error recovery thread if it hasn't already - * been started. It is expected there could be multiple - * ios hitting this path before things are cleaned up. + * clean up the admin queue. Same thing as above. + */ + blk_mq_quiesce_queue(ctrl->ctrl.admin_q); + blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, + nvme_fc_terminate_exchange, &ctrl->ctrl); + blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); +} + +static void +nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg) +{ + /* + * if an error (io timeout, etc) while (re)connecting, the remote + * port requested terminating of the association (disconnect_ls) + * or an error (timeout or abort) occurred on an io while creating + * the controller. Abort any ios on the association and let the + * create_association error path resolve things. */ if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { - active = atomic_xchg(&ctrl->err_work_active, 1); - if (!active && !queue_work(nvme_fc_wq, &ctrl->err_work)) { - atomic_set(&ctrl->err_work_active, 0); - WARN_ON(1); - } + __nvme_fc_abort_outstanding_ios(ctrl, true); + set_bit(ASSOC_FAILED, &ctrl->flags); return; } @@ -2745,30 +2817,6 @@ nvme_fc_complete_rq(struct request *rq) nvme_fc_ctrl_put(ctrl); } -/* - * This routine is used by the transport when it needs to find active - * io on a queue that is to be terminated. The transport uses - * blk_mq_tagset_busy_itr() to find the busy requests, which then invoke - * this routine to kill them on a 1 by 1 basis. - * - * As FC allocates FC exchange for each io, the transport must contact - * the LLDD to terminate the exchange, thus releasing the FC exchange. - * After terminating the exchange the LLDD will call the transport's - * normal io done path for the request, but it will have an aborted - * status. The done path will return the io request back to the block - * layer with an error status. - */ -static bool -nvme_fc_terminate_exchange(struct request *req, void *data, bool reserved) -{ - struct nvme_ctrl *nctrl = data; - struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); - struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req); - - __nvme_fc_abort_op(ctrl, op); - return true; -} - static const struct blk_mq_ops nvme_fc_mq_ops = { .queue_rq = nvme_fc_queue_rq, @@ -2988,6 +3036,8 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) ctrl->cnum, ctrl->lport->localport.port_name, ctrl->rport->remoteport.port_name, ctrl->ctrl.opts->subsysnqn); + clear_bit(ASSOC_FAILED, &ctrl->flags); + /* * Create the admin queue */ @@ -3016,7 +3066,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) */ ret = nvme_enable_ctrl(&ctrl->ctrl); - if (ret) + if (ret || test_bit(ASSOC_FAILED, &ctrl->flags)) goto out_disconnect_admin_queue; ctrl->ctrl.max_segments = ctrl->lport->ops->max_sgl_segments; @@ -3026,7 +3076,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) blk_mq_unquiesce_queue(ctrl->ctrl.admin_q); ret = nvme_init_identify(&ctrl->ctrl); - if (ret) + if (ret || test_bit(ASSOC_FAILED, &ctrl->flags)) goto out_disconnect_admin_queue; /* sanity checks */ @@ -3071,9 +3121,9 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) ret = nvme_fc_create_io_queues(ctrl); else ret = nvme_fc_recreate_io_queues(ctrl); - if (ret) - goto out_term_aen_ops; } + if (ret || test_bit(ASSOC_FAILED, &ctrl->flags)) + goto out_term_aen_ops; changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE); @@ -3108,60 +3158,6 @@ out_free_queue: /* - * This routine runs through all outstanding commands on the association - * and aborts them. This routine is typically be called by the - * delete_association routine. It is also called due to an error during - * reconnect. In that scenario, it is most likely a command that initializes - * the controller, including fabric Connect commands on io queues, that - * may have timed out or failed thus the io must be killed for the connect - * thread to see the error. - */ -static void -__nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) -{ - /* - * If io queues are present, stop them and terminate all outstanding - * ios on them. As FC allocates FC exchange for each io, the - * transport must contact the LLDD to terminate the exchange, - * thus releasing the FC exchange. We use blk_mq_tagset_busy_itr() - * to tell us what io's are busy and invoke a transport routine - * to kill them with the LLDD. After terminating the exchange - * the LLDD will call the transport's normal io done path, but it - * will have an aborted status. The done path will return the - * io requests back to the block layer as part of normal completions - * (but with error status). - */ - if (ctrl->ctrl.queue_count > 1) { - nvme_stop_queues(&ctrl->ctrl); - blk_mq_tagset_busy_iter(&ctrl->tag_set, - nvme_fc_terminate_exchange, &ctrl->ctrl); - blk_mq_tagset_wait_completed_request(&ctrl->tag_set); - if (start_queues) - nvme_start_queues(&ctrl->ctrl); - } - - /* - * Other transports, which don't have link-level contexts bound - * to sqe's, would try to gracefully shutdown the controller by - * writing the registers for shutdown and polling (call - * nvme_shutdown_ctrl()). Given a bunch of i/o was potentially - * just aborted and we will wait on those contexts, and given - * there was no indication of how live the controlelr is on the - * link, don't send more io to create more contexts for the - * shutdown. Let the controller fail via keepalive failure if - * its still present. - */ - - /* - * clean up the admin queue. Same thing as above. - */ - blk_mq_quiesce_queue(ctrl->ctrl.admin_q); - blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, - nvme_fc_terminate_exchange, &ctrl->ctrl); - blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); -} - -/* * This routine stops operation of the controller on the host side. * On the host os stack side: Admin and IO queues are stopped, * outstanding ios on them terminated via FC ABTS. @@ -3237,7 +3233,6 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl) { struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); - cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->connect_work); /* * kill the association on the link side. this will block @@ -3292,78 +3287,34 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status) } static void -__nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl) +nvme_fc_reset_ctrl_work(struct work_struct *work) { - /* - * if state is CONNECTING - the error occurred as part of a - * reconnect attempt. Abort any ios on the association and - * let the create_association error paths resolve things. - */ - if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { - __nvme_fc_abort_outstanding_ios(ctrl, true); - return; - } - - /* - * For any other state, kill the association. As this routine - * is a common io abort routine for resetting and such, after - * the association is terminated, ensure that the state is set - * to CONNECTING. - */ + struct nvme_fc_ctrl *ctrl = + container_of(work, struct nvme_fc_ctrl, ctrl.reset_work); - nvme_stop_keep_alive(&ctrl->ctrl); + nvme_stop_ctrl(&ctrl->ctrl); /* will block will waiting for io to terminate */ nvme_fc_delete_association(ctrl); - if (ctrl->ctrl.state != NVME_CTRL_CONNECTING && - !nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) + if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) dev_err(ctrl->ctrl.device, "NVME-FC{%d}: error_recovery: Couldn't change state " "to CONNECTING\n", ctrl->cnum); -} - -static void -nvme_fc_reset_ctrl_work(struct work_struct *work) -{ - struct nvme_fc_ctrl *ctrl = - container_of(work, struct nvme_fc_ctrl, ctrl.reset_work); - int ret; - __nvme_fc_terminate_io(ctrl); - - nvme_stop_ctrl(&ctrl->ctrl); - - if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) - ret = nvme_fc_create_association(ctrl); - else - ret = -ENOTCONN; - - if (ret) - nvme_fc_reconnect_or_delete(ctrl, ret); - else - dev_info(ctrl->ctrl.device, - "NVME-FC{%d}: controller reset complete\n", - ctrl->cnum); + if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) { + if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) { + dev_err(ctrl->ctrl.device, + "NVME-FC{%d}: failed to schedule connect " + "after reset\n", ctrl->cnum); + } else { + flush_delayed_work(&ctrl->connect_work); + } + } else { + nvme_fc_reconnect_or_delete(ctrl, -ENOTCONN); + } } -static void -nvme_fc_connect_err_work(struct work_struct *work) -{ - struct nvme_fc_ctrl *ctrl = - container_of(work, struct nvme_fc_ctrl, err_work); - - __nvme_fc_terminate_io(ctrl); - - atomic_set(&ctrl->err_work_active, 0); - - /* - * Rescheduling the connection after recovering - * from the io error is left to the reconnect work - * item, which is what should have stalled waiting on - * the io that had the error that scheduled this work. - */ -} static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = { .name = "fc", @@ -3491,7 +3442,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, ctrl->dev = lport->dev; ctrl->cnum = idx; ctrl->ioq_live = false; - atomic_set(&ctrl->err_work_active, 0); init_waitqueue_head(&ctrl->ioabort_wait); get_device(ctrl->dev); @@ -3499,7 +3449,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work); INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work); - INIT_WORK(&ctrl->err_work, nvme_fc_connect_err_work); spin_lock_init(&ctrl->lock); /* io queue count */ @@ -3592,7 +3541,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, fail_ctrl: nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING); cancel_work_sync(&ctrl->ctrl.reset_work); - cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->connect_work); ctrl->ctrl.opts = NULL; diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index aad829a2b50d..541b0cba6d80 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1768,6 +1768,14 @@ static void nvme_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc) return; } + /* sanity checking for received data length */ + if (unlikely(wc->byte_len < len)) { + dev_err(queue->ctrl->ctrl.device, + "Unexpected nvme completion length(%d)\n", wc->byte_len); + nvme_rdma_error_recovery(queue->ctrl); + return; + } + ib_dma_sync_single_for_cpu(ibdev, qe->dma, len, DMA_FROM_DEVICE); /* * AEN requests are special as they don't time out and can @@ -1890,10 +1898,10 @@ static int nvme_rdma_route_resolved(struct nvme_rdma_queue *queue) priv.hsqsize = cpu_to_le16(queue->ctrl->ctrl.sqsize); } - ret = rdma_connect(queue->cm_id, ¶m); + ret = rdma_connect_locked(queue->cm_id, ¶m); if (ret) { dev_err(ctrl->ctrl.device, - "rdma_connect failed (%d).\n", ret); + "rdma_connect_locked failed (%d).\n", ret); goto out_destroy_queue_ib; } diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index aafcbc424b7a..957b39a82431 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -907,8 +907,6 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, req->error_loc = NVMET_NO_ERROR_LOC; req->error_slba = 0; - trace_nvmet_req_init(req, req->cmd); - /* no support for fused commands yet */ if (unlikely(flags & (NVME_CMD_FUSE_FIRST | NVME_CMD_FUSE_SECOND))) { req->error_loc = offsetof(struct nvme_common_command, flags); @@ -938,6 +936,8 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, if (status) goto fail; + trace_nvmet_req_init(req, req->cmd); + if (unlikely(!percpu_ref_tryget_live(&sq->ref))) { status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; goto fail; diff --git a/drivers/nvme/target/trace.h b/drivers/nvme/target/trace.h index 0458046d6501..c14e3249a14d 100644 --- a/drivers/nvme/target/trace.h +++ b/drivers/nvme/target/trace.h @@ -46,19 +46,12 @@ static inline struct nvmet_ctrl *nvmet_req_to_ctrl(struct nvmet_req *req) return req->sq->ctrl; } -static inline void __assign_disk_name(char *name, struct nvmet_req *req, - bool init) +static inline void __assign_req_name(char *name, struct nvmet_req *req) { - struct nvmet_ctrl *ctrl = nvmet_req_to_ctrl(req); - struct nvmet_ns *ns; - - if ((init && req->sq->qid) || (!init && req->cq->qid)) { - ns = nvmet_find_namespace(ctrl, req->cmd->rw.nsid); - strncpy(name, ns->device_path, DISK_NAME_LEN); - return; - } - - memset(name, 0, DISK_NAME_LEN); + if (req->ns) + strncpy(name, req->ns->device_path, DISK_NAME_LEN); + else + memset(name, 0, DISK_NAME_LEN); } #endif @@ -81,7 +74,7 @@ TRACE_EVENT(nvmet_req_init, TP_fast_assign( __entry->cmd = cmd; __entry->ctrl = nvmet_req_to_ctrl(req); - __assign_disk_name(__entry->disk, req, true); + __assign_req_name(__entry->disk, req); __entry->qid = req->sq->qid; __entry->cid = cmd->common.command_id; __entry->opcode = cmd->common.opcode; @@ -121,7 +114,7 @@ TRACE_EVENT(nvmet_req_complete, __entry->cid = req->cqe->command_id; __entry->result = le64_to_cpu(req->cqe->result.u64); __entry->status = le16_to_cpu(req->cqe->status) >> 1; - __assign_disk_name(__entry->disk, req, false); + __assign_req_name(__entry->disk, req); ), TP_printk("nvmet%s: %sqid=%d, cmdid=%u, res=%#llx, status=%#x", __print_ctrl_name(__entry->ctrl), diff --git a/drivers/of/device.c b/drivers/of/device.c index 655dee422563..3a469c79e6b0 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -93,7 +93,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, { const struct iommu_ops *iommu; const struct bus_dma_region *map = NULL; - dma_addr_t dma_start = 0; + u64 dma_start = 0; u64 mask, end, size = 0; bool coherent; int ret; @@ -109,10 +109,10 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, return ret == -ENODEV ? 0 : ret; } else { const struct bus_dma_region *r = map; - dma_addr_t dma_end = 0; + u64 dma_end = 0; /* Determine the overall bounds of all DMA regions */ - for (dma_start = ~(dma_addr_t)0; r->size; r++) { + for (dma_start = ~0ULL; r->size; r++) { /* Take lower and upper limits */ if (r->dma_start < dma_start) dma_start = r->dma_start; diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index bcd154485972..a7fbc5e37e19 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -200,6 +200,16 @@ static int __init __rmem_cmp(const void *a, const void *b) if (ra->base > rb->base) return 1; + /* + * Put the dynamic allocations (address == 0, size == 0) before static + * allocations at address 0x0 so that overlap detection works + * correctly. + */ + if (ra->size < rb->size) + return -1; + if (ra->size > rb->size) + return 1; + return 0; } @@ -217,8 +227,7 @@ static void __init __rmem_check_for_overlap(void) this = &reserved_mem[i]; next = &reserved_mem[i + 1]; - if (!(this->base && next->base)) - continue; + if (this->base + this->size > next->base) { phys_addr_t this_end, next_end; diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 3bf18d718975..a50ab002e9e4 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -51,7 +51,7 @@ static void pnp_remove_protocol(struct pnp_protocol *protocol) } /** - * pnp_protocol_register - adds a pnp protocol to the pnp layer + * pnp_register_protocol - adds a pnp protocol to the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure * * Ex protocols: ISAPNP, PNPBIOS, etc @@ -91,7 +91,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) } /** - * pnp_protocol_unregister - removes a pnp protocol from the pnp layer + * pnp_unregister_protocol - removes a pnp protocol from the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure */ void pnp_unregister_protocol(struct pnp_protocol *protocol) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 481c7b10133b..53fa84f4d1e1 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP and Wolfson Microelectronic Arizona codecs devices. +config REGULATOR_ARM_SCMI + tristate "SCMI based regulator driver" + depends on ARM_SCMI_PROTOCOL && OF + help + This adds the regulator driver support for ARM platforms using SCMI + protocol for device voltage management. + This driver uses SCMI Message Protocol driver to interact with the + firmware providing the device Voltage functionality. + config REGULATOR_AS3711 tristate "AS3711 PMIC" depends on MFD_AS3711 @@ -303,6 +312,26 @@ config REGULATOR_DA9063 This driver can also be built as a module. If so, the module will be called da9063-regulator. +config REGULATOR_DA9121 + tristate "Dialog Semiconductor DA9121/DA9122/DA9220/DA9217/DA9130/DA9131/DA9132 regulator" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor DA9121. The + DA9121 is a single channel dual-phase buck converter controlled + through an I2C interface. + + DA9121 Single-channel dual-phase 10A buck converter + DA9130 Single-channel dual-phase 10A buck converter (Automotive) + DA9217 Single-channel dual-phase 6A buck converter + DA9122 Dual-channel single-phase 5A buck converter + DA9131 Dual-channel single-phase 5A buck converter (Automotive) + DA9220 Dual-channel single-phase 3A buck converter + DA9132 Dual-channel single-phase 3A buck converter (Automotive) + + This driver can also be built as a module. If so, the module + will be called da9121-regulator. + config REGULATOR_DA9210 tristate "Dialog Semiconductor DA9210 regulator" depends on I2C @@ -791,9 +820,17 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and converters on PCF50633 +config REGULATOR_PF8X00 + tristate "NXP PF8100/PF8121A/PF8200 regulator driver" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support the regulators found on the NXP + PF8100/PF8121A/PF8200 PMIC. + config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" - depends on I2C + depends on I2C && OF select REGMAP_I2C help Say y here to support the regulators found on the Freescale diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6ebae516258e..680e539f6579 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o +obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o @@ -38,6 +39,7 @@ obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o +obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o @@ -100,6 +102,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o +obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 33ca197860b3..7bebf9ce6271 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -455,7 +455,8 @@ static int as3722_sd_set_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_FAST: val = as3722_reg_lookup[id].mode_mask; - case REGULATOR_MODE_NORMAL: /* fall down */ + fallthrough; + case REGULATOR_MODE_NORMAL: break; default: return -EINVAL; diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 0774467994fb..e6d5d98c3cea 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1323,13 +1323,142 @@ static void mark_hw_controlled(struct device *dev, struct device_node *np, dev_warn(dev, "Bad regulator node\n"); } -static int get_hw_controlled_regulators(struct device *dev, - struct bd718xx_regulator_data *reg_data, - unsigned int num_reg_data, int *info) +/* + * Setups where regulator (especially the buck8) output voltage is scaled + * by adding external connection where some other regulator output is connected + * to feedback-pin (over suitable resistors) is getting popular amongst users + * of BD71837. (This allows for example scaling down the buck8 voltages to suit + * lover GPU voltages for projects where buck8 is (ab)used to supply power + * for GPU. Additionally some setups do allow DVS for buck8 but as this do + * produce voltage spikes the HW must be evaluated to be able to survive this + * - hence I keep the DVS disabled for non DVS bucks by default. I don't want + * to help you burn your proto board) + * + * So we allow describing this external connection from DT and scale the + * voltages accordingly. This is what the connection should look like: + * + * |------------| + * | buck 8 |-------+----->Vout + * | | | + * |------------| | + * | FB pin | + * | | + * +-------+--R2---+ + * | + * R1 + * | + * V FB-pull-up + * + * Here the buck output is sifted according to formula: + * + * Vout_o = Vo - (Vpu - Vo)*R2/R1 + * Linear_step = step_orig*(R1+R2)/R1 + * + * where: + * Vout_o is adjusted voltage output at vsel reg value 0 + * Vo is original voltage output at vsel reg value 0 + * Vpu is the pull-up voltage V FB-pull-up in the picture + * R1 and R2 are resistor values. + * + * As a real world example for buck8 and a specific GPU: + * VLDO = 1.6V (used as FB-pull-up) + * R1 = 1000ohms + * R2 = 150ohms + * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V + * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV + */ +static int setup_feedback_loop(struct device *dev, struct device_node *np, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int fb_uv) { + int i, r1, r2, ret; + + /* + * We do adjust the values in the global desc based on DT settings. + * This may not be best approach as it can cause problems if more than + * one PMIC is controlled from same processor. I don't see such use-case + * for BD718x7 now - so we spare some bits. + * + * If this will point out to be a problem - then we can allocate new + * bd718xx_regulator_data array at probe and just use the global + * array as a template where we copy initial values. Then we can + * use allocated descs for regultor registration and do IC specific + * modifications to this copy while leaving other PMICs untouched. But + * that means allocating new array for each PMIC - and currently I see + * no need for that. + */ + + for (i = 0; i < num_reg_data; i++) { + struct regulator_desc *desc = ®_data[i].desc; + int j; + + if (!of_node_name_eq(np, desc->of_match)) + continue; + + pr_info("Looking at node '%s'\n", desc->of_match); + + /* The feedback loop connection does not make sense for LDOs */ + if (desc->id >= BD718XX_LDO1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms", + &r1); + if (ret) + return ret; + + if (!r1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms", + &r2); + if (ret) + return ret; + + if (desc->n_linear_ranges && desc->linear_ranges) { + struct linear_range *new; + + new = devm_kzalloc(dev, desc->n_linear_ranges * + sizeof(struct linear_range), + GFP_KERNEL); + if (!new) + return -ENOMEM; + + for (j = 0; j < desc->n_linear_ranges; j++) { + int min = desc->linear_ranges[j].min; + int step = desc->linear_ranges[j].step; + + min -= (fb_uv - min)*r2/r1; + step = step * (r1 + r2); + step /= r1; + + new[j].min = min; + new[j].step = step; + + dev_dbg(dev, "%s: old range min %d, step %d\n", + desc->name, desc->linear_ranges[j].min, + desc->linear_ranges[j].step); + dev_dbg(dev, "new range min %d, step %d\n", min, + step); + } + desc->linear_ranges = new; + } + dev_dbg(dev, "regulator '%s' has FB pull-up configured\n", + desc->name); + + return 0; + } + + return -ENODEV; +} + +static int get_special_regulators(struct device *dev, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int *info) +{ + int ret; struct device_node *np; struct device_node *nproot = dev->of_node; - const char *prop = "rohm,no-regulator-enable-control"; + int uv; *info = 0; @@ -1338,13 +1467,32 @@ static int get_hw_controlled_regulators(struct device *dev, dev_err(dev, "failed to find regulators node\n"); return -ENODEV; } - for_each_child_of_node(nproot, np) - if (of_property_read_bool(np, prop)) + for_each_child_of_node(nproot, np) { + if (of_property_read_bool(np, "rohm,no-regulator-enable-control")) mark_hw_controlled(dev, np, reg_data, num_reg_data, info); + ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt", + &uv); + if (ret) { + if (ret == -EINVAL) + continue; + else + goto err_out; + } + + ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv); + if (ret) + goto err_out; + } of_node_put(nproot); return 0; + +err_out: + of_node_put(np); + of_node_put(nproot); + + return ret; } static int bd718xx_probe(struct platform_device *pdev) @@ -1432,8 +1580,10 @@ static int bd718xx_probe(struct platform_device *pdev) * be affected by PMIC state machine - Eg. regulator is likely to stay * on even in SUSPEND */ - get_hw_controlled_regulators(pdev->dev.parent, reg_data, num_reg_data, + err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data, &omit_enable); + if (err) + return err; for (i = 0; i < num_reg_data; i++) { diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 42bbd99a36ac..ca03d8e70bd1 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2958,6 +2958,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, if (ops->list_voltage) { if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (lock) regulator_lock(rdev); ret = ops->list_voltage(rdev, selector); @@ -3109,6 +3111,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator, if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; @@ -4030,6 +4034,12 @@ int regulator_set_voltage_time(struct regulator *regulator, for (i = 0; i < rdev->desc->n_voltages; i++) { /* We only look for exact voltage matches here */ + if (i < rdev->desc->linear_min_sel) + continue; + + if (old_sel >= 0 && new_sel >= 0) + break; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; @@ -5305,6 +5315,8 @@ regulator_register(const struct regulator_desc *regulator_desc, /* FIXME: this currently triggers a chicken-and-egg problem * when creating -SUPPLY symlink in sysfs to a regulator * that is just being created */ + rdev_dbg(rdev, "will resolve supply early: %s\n", + rdev->supply_name); ret = regulator_resolve_supply(rdev); if (!ret) ret = set_machine_constraints(rdev); @@ -5537,7 +5549,7 @@ void regulator_set_drvdata(struct regulator *regulator, void *data) EXPORT_SYMBOL_GPL(regulator_set_drvdata); /** - * regulator_get_id - get regulator ID + * rdev_get_id - get regulator ID * @rdev: regulator */ int rdev_get_id(struct regulator_dev *rdev) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c new file mode 100644 index 000000000000..a2ede7d7897e --- /dev/null +++ b/drivers/regulator/da9121-regulator.c @@ -0,0 +1,1075 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// DA9121 Single-channel dual-phase 10A buck converter +// +// Copyright (C) 2020 Axis Communications AB +// +// DA9130 Single-channel dual-phase 10A buck converter (Automotive) +// DA9217 Single-channel dual-phase 6A buck converter +// DA9122 Dual-channel single-phase 5A buck converter +// DA9131 Dual-channel single-phase 5A buck converter (Automotive) +// DA9220 Dual-channel single-phase 3A buck converter +// DA9132 Dual-channel single-phase 3A buck converter (Automotive) +// +// Copyright (C) 2020 Dialog Semiconductor + +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/driver.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/regulator/da9121.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> + +#include "da9121-regulator.h" + +/* Chip data */ +struct da9121 { + struct device *dev; + struct delayed_work work; + struct da9121_pdata *pdata; + struct regmap *regmap; + struct regulator_dev *rdev[DA9121_IDX_MAX]; + unsigned int persistent[2]; + unsigned int passive_delay; + int chip_irq; + int variant_id; +}; + +/* Define ranges for different variants, enabling translation to/from + * registers. Maximums give scope to allow for transients. + */ +struct da9121_range { + int val_min; + int val_max; + int val_stp; + int reg_min; + int reg_max; +}; + +static struct da9121_range da9121_10A_2phase_current = { + .val_min = 7000000, + .val_max = 20000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 14, +}; + +static struct da9121_range da9121_6A_2phase_current = { + .val_min = 7000000, + .val_max = 12000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 6, +}; + +static struct da9121_range da9121_5A_1phase_current = { + .val_min = 3500000, + .val_max = 10000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 14, +}; + +static struct da9121_range da9121_3A_1phase_current = { + .val_min = 3500000, + .val_max = 6000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 6, +}; + +struct da9121_variant_info { + int num_bucks; + int num_phases; + struct da9121_range *current_range; +}; + +static const struct da9121_variant_info variant_parameters[] = { + { 1, 2, &da9121_10A_2phase_current }, //DA9121_TYPE_DA9121_DA9130 + { 2, 1, &da9121_3A_1phase_current }, //DA9121_TYPE_DA9220_DA9132 + { 2, 1, &da9121_5A_1phase_current }, //DA9121_TYPE_DA9122_DA9131 + { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 +}; + +struct da9121_field { + unsigned int reg; + unsigned int msk; +}; + +static const struct da9121_field da9121_current_field[2] = { + { DA9121_REG_BUCK_BUCK1_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, + { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, +}; + +static const struct da9121_field da9121_mode_field[2] = { + { DA9121_REG_BUCK_BUCK1_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, + { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, +}; + +struct status_event_data { + int buck_id; /* 0=core, 1/2-buck */ + int reg_index; /* index for status/event/mask register selection */ + int status_bit; /* bit masks... */ + int event_bit; + int mask_bit; + unsigned long notification; /* Notification for status inception */ + char *warn; /* if NULL, notify - otherwise dev_warn this string */ +}; + +#define DA9121_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9121_MASK_SYS_STATUS_##bank##_##name, \ + DA9121_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9121_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* For second buck related event bits that are specific to DA9122, DA9220 variants */ +#define DA9xxx_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9xxx_MASK_SYS_STATUS_##bank##_##name, \ + DA9xxx_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9xxx_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* The status signals that may need servicing, depending on device variant. + * After assertion, they persist; so event is notified, the IRQ disabled, + * and status polled until clear again and IRQ is reenabled. + * + * SG/PG1/PG2 should be set when device first powers up and should never + * re-occur. When this driver starts, it is expected that these will have + * self-cleared for when the IRQs are enabled, so these should never be seen. + * If seen, the implication is that the device has reset. + * + * GPIO0/1/2 are not configured for use by default, so should not be seen. + */ +static const struct status_event_data status_event_handling[] = { + DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"), + DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL), + DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL), + DA9121_STATUS(1, 1, PG1, 0, "Handled E_PG1\n"), + DA9121_STATUS(1, 1, OV1, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9121_STATUS(1, 1, UV1, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9121_STATUS(1, 1, OC1, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9xxx_STATUS(2, 1, PG2, 0, "Handled E_PG2\n"), + DA9xxx_STATUS(2, 1, OV2, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9xxx_STATUS(2, 1, UV2, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9xxx_STATUS(2, 1, OC2, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9121_STATUS(0, 2, GPIO0, 0, "Handled E_GPIO0\n"), + DA9121_STATUS(0, 2, GPIO1, 0, "Handled E_GPIO1\n"), + DA9121_STATUS(0, 2, GPIO2, 0, "Handled E_GPIO2\n"), +}; + +static int da9121_get_current_limit(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int val = 0; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_current_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + goto error; + } + + if (val < range->reg_min) { + ret = -EACCES; + goto error; + } + + if (val > range->reg_max) { + ret = -EINVAL; + goto error; + } + + return range->val_min + (range->val_stp * (val - range->reg_min)); +error: + return ret; +} + +static int da9121_ceiling_selector(struct regulator_dev *rdev, + int min, int max, + unsigned int *selector) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int level; + unsigned int i = 0; + unsigned int sel = 0; + int ret = 0; + + if (range->val_min > max || range->val_max < min) { + dev_err(chip->dev, + "Requested current out of regulator capability\n"); + ret = -EINVAL; + goto error; + } + + level = range->val_max; + for (i = range->reg_max; i >= range->reg_min; i--) { + if (level <= max) { + sel = i; + break; + } + level -= range->val_stp; + } + + if (level < min) { + dev_err(chip->dev, + "Best match falls below minimum requested current\n"); + ret = -EINVAL; + goto error; + } + + *selector = sel; +error: + return ret; +} + +static int da9121_set_current_limit(struct regulator_dev *rdev, + int min_ua, int max_ua) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int sel = 0; + int ret = 0; + + if (min_ua < range->val_min || + max_ua > range->val_max) { + ret = -EINVAL; + goto error; + } + + ret = da9121_ceiling_selector(rdev, min_ua, max_ua, &sel); + if (ret < 0) + goto error; + + ret = regmap_update_bits(chip->regmap, + da9121_current_field[id].reg, + da9121_current_field[id].msk, + (unsigned int)sel); + if (ret < 0) + dev_err(chip->dev, "Cannot update BUCK current limit, err: %d\n", ret); + +error: + return ret; +} + +static unsigned int da9121_map_mode(unsigned int mode) +{ + switch (mode) { + case DA9121_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + case DA9121_BUCK_MODE_FORCE_PWM_SHEDDING: + return REGULATOR_MODE_NORMAL; + case DA9121_BUCK_MODE_AUTO: + return REGULATOR_MODE_IDLE; + case DA9121_BUCK_MODE_FORCE_PFM: + return REGULATOR_MODE_STANDBY; + default: + return -EINVAL; + } +} + +static int da9121_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = DA9121_BUCK_MODE_FORCE_PWM; + break; + case REGULATOR_MODE_NORMAL: + val = DA9121_BUCK_MODE_FORCE_PWM_SHEDDING; + break; + case REGULATOR_MODE_IDLE: + val = DA9121_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = DA9121_BUCK_MODE_FORCE_PFM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(chip->regmap, + da9121_mode_field[id].reg, + da9121_mode_field[id].msk, + val); +} + +static unsigned int da9121_buck_get_mode(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_mode_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + return -EINVAL; + } + + return da9121_map_mode(val & da9121_mode_field[id].msk); +} + +static const struct regulator_ops da9121_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .get_current_limit = da9121_get_current_limit, + .set_current_limit = da9121_set_current_limit, + .set_mode = da9121_buck_set_mode, + .get_mode = da9121_buck_get_mode, +}; + +static struct of_regulator_match da9121_matches[] = { + [DA9121_IDX_BUCK1] = { .name = "buck1" }, + [DA9121_IDX_BUCK2] = { .name = "buck2" }, +}; + +static int da9121_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct da9121 *chip = config->driver_data; + struct da9121_pdata *pdata; + struct gpio_desc *ena_gpiod; + + if (chip->pdata == NULL) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else { + pdata = chip->pdata; + } + + pdata->num_buck++; + + if (pdata->num_buck > variant_parameters[chip->variant_id].num_bucks) { + dev_err(chip->dev, "Error: excessive regulators for device\n"); + return -ENODEV; + } + + ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "da9121-enable"); + if (!IS_ERR(ena_gpiod)) + config->ena_gpiod = ena_gpiod; + + if (variant_parameters[chip->variant_id].num_bucks == 2) { + uint32_t ripple_cancel; + uint32_t ripple_reg; + int ret; + + if (of_property_read_u32(da9121_matches[pdata->num_buck-1].of_node, + "dlg,ripple-cancel", &ripple_cancel)) { + if (pdata->num_buck > 1) + ripple_reg = DA9xxx_REG_BUCK_BUCK2_7; + else + ripple_reg = DA9121_REG_BUCK_BUCK1_7; + + ret = regmap_update_bits(chip->regmap, ripple_reg, + DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL, + ripple_cancel); + if (ret < 0) + dev_err(chip->dev, "Cannot set ripple mode, err: %d\n", ret); + } + } + + return 0; +} + +#define DA9121_MIN_MV 300 +#define DA9121_MAX_MV 1900 +#define DA9121_STEP_MV 10 +#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) +#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ + + 1 + DA9121_MIN_SEL) + +static const struct regulator_desc da9121_reg = { + .id = DA9121_IDX_BUCK1, + .name = "da9121", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ + .ramp_delay = 20000, + /* tBUCK_EN */ + .enable_time = 20, +}; + +static const struct regulator_desc da9220_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9220/DA9132 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9220/DA9132 BUCK2", + .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9122_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9122/DA9131 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9122/DA9131 BUCK2", + .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9217_reg = { + .id = DA9121_IDX_BUCK1, + .name = "DA9217 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, +}; + +static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = { + [DA9121_TYPE_DA9121_DA9130] = { &da9121_reg, NULL }, + [DA9121_TYPE_DA9220_DA9132] = { &da9220_reg[0], &da9220_reg[1] }, + [DA9121_TYPE_DA9122_DA9131] = { &da9122_reg[0], &da9122_reg[1] }, + [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, +}; + +static void da9121_status_poll_on(struct work_struct *work) +{ + struct da9121 *chip = container_of(work, struct da9121, work.work); + int status[3] = {0}; + int clear[3] = {0}; + unsigned long delay; + int i; + int ret; + + ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, status, 2); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read STATUS registers: %d\n", ret); + goto error; + } + + /* Possible events are tested to be within range for the variant, potentially + * masked by the IRQ handler (not just warned about), as having been masked, + * and the respective state cleared - then flagged to unmask for next IRQ. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool supported = (item->warn == NULL); + bool persisting = (chip->persistent[reg_idx] & item->event_bit); + bool now_cleared = !(status[reg_idx] & item->status_bit); + + if (relevant && supported && persisting && now_cleared) { + clear[reg_idx] |= item->mask_bit; + chip->persistent[reg_idx] &= ~item->event_bit; + } + } + + for (i = 0; i < 2; i++) { + if (clear[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = clear[i]; + + ret = regmap_update_bits(chip->regmap, reg, mbit, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to unmask 0x%02x %d\n", + reg, ret); + goto error; + } + } + } + + if (chip->persistent[0] | chip->persistent[1]) { + delay = msecs_to_jiffies(chip->passive_delay); + queue_delayed_work(system_freezable_wq, &chip->work, delay); + } + +error: + return; +} + +static irqreturn_t da9121_irq_handler(int irq, void *data) +{ + struct da9121 *chip = data; + struct regulator_dev *rdev; + int event[3] = {0}; + int handled[3] = {0}; + int mask[3] = {0}; + int ret = IRQ_NONE; + int i; + int err; + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, event, 3); + if (err < 0) { + dev_err(chip->dev, "Failed to read EVENT registers %d\n", err); + ret = IRQ_NONE; + goto error; + } + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0, mask, 3); + if (err < 0) { + dev_err(chip->dev, + "Failed to read MASK registers: %d\n", ret); + ret = IRQ_NONE; + goto error; + } + + rdev = chip->rdev[DA9121_IDX_BUCK1]; + + /* Possible events are tested to be within range for the variant, currently + * enabled, and having triggered this IRQ. The event may then be notified, + * or a warning given for unexpected events - those from device POR, and + * currently unsupported GPIO configurations. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool enabled = !(mask[reg_idx] & item->mask_bit); + bool active = (event[reg_idx] & item->event_bit); + bool notify = (item->warn == NULL); + + if (relevant && enabled && active) { + if (notify) { + chip->persistent[reg_idx] |= item->event_bit; + regulator_notifier_call_chain(rdev, item->notification, NULL); + } else { + dev_warn(chip->dev, item->warn); + handled[reg_idx] |= item->event_bit; + ret = IRQ_HANDLED; + } + } + } + + for (i = 0; i < 3; i++) { + if (event[i] != handled[i]) { + dev_warn(chip->dev, + "Unhandled event(s) in bank%d 0x%02x\n", i, + event[i] ^ handled[i]); + } + } + + /* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */ + for (i = 0; i < 2; i++) { + if (handled[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = handled[i]; + + err = regmap_update_bits(chip->regmap, reg, mbit, mbit); + if (err < 0) { + dev_err(chip->dev, + "Failed to mask 0x%02x interrupt %d\n", + reg, err); + ret = IRQ_NONE; + goto error; + } + } + } + + /* clear the events */ + if (handled[0] | handled[1] | handled[2]) { + err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, handled, 3); + if (err < 0) { + dev_err(chip->dev, "Fail to write EVENTs %d\n", err); + ret = IRQ_NONE; + goto error; + } + } + + queue_delayed_work(system_freezable_wq, &chip->work, 0); +error: + return ret; +} + +static int da9121_set_regulator_config(struct da9121 *chip) +{ + struct regulator_config config = { }; + unsigned int max_matches = variant_parameters[chip->variant_id].num_bucks; + int ret = 0; + int i; + + for (i = 0; i < max_matches; i++) { + const struct regulator_desc *regl_desc = + local_da9121_regulators[chip->variant_id][i]; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + chip->rdev[i] = devm_regulator_register(chip->dev, + regl_desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, "Failed to register regulator %s, %d/%d\n", + regl_desc->name, (i+1), max_matches); + ret = PTR_ERR(chip->rdev[i]); + goto error; + } + } + +error: + return ret; +} + +/* DA9121 chip register model */ +static const struct regmap_range da9121_1ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_1ch_readable_table = { + .yes_ranges = da9121_1ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_readable_ranges), +}; + +static const struct regmap_range da9121_2ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_7), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_2ch_readable_table = { + .yes_ranges = da9121_2ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_readable_ranges), +}; + +static const struct regmap_range da9121_1ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_1ch_writeable_table = { + .yes_ranges = da9121_1ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_writeable_ranges), +}; + +static const struct regmap_range da9121_2ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_2), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_4, DA9xxx_REG_BUCK_BUCK2_7), +}; + +static const struct regmap_access_table da9121_2ch_writeable_table = { + .yes_ranges = da9121_2ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_writeable_ranges), +}; + + +static const struct regmap_range da9121_volatile_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_EVENT_2), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_volatile_table = { + .yes_ranges = da9121_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_volatile_ranges), +}; + +/* DA9121 regmap config for 1 channel variants */ +static struct regmap_config da9121_1ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_1ch_readable_table, + .wr_table = &da9121_1ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +/* DA9121 regmap config for 2 channel variants */ +static struct regmap_config da9121_2ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_2ch_readable_table, + .wr_table = &da9121_2ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) +{ + u32 device_id; + u8 chip_id = chip->variant_id; + u32 variant_id; + u8 variant_mrc, variant_vrc; + char *type; + bool config_match = false; + int ret = 0; + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_DEVICE_ID, &device_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read device ID: %d\n", ret); + goto error; + } + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_VARIANT_ID, &variant_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read variant ID: %d\n", ret); + goto error; + } + + if (device_id != DA9121_DEVICE_ID) { + dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id); + ret = -ENODEV; + goto error; + } + + variant_vrc = variant_id & DA9121_MASK_OTP_VARIANT_ID_VRC; + + switch (variant_vrc) { + case DA9121_VARIANT_VRC: + type = "DA9121/DA9130"; + config_match = (chip_id == DA9121_TYPE_DA9121_DA9130); + break; + case DA9220_VARIANT_VRC: + type = "DA9220/DA9132"; + config_match = (chip_id == DA9121_TYPE_DA9220_DA9132); + break; + case DA9122_VARIANT_VRC: + type = "DA9122/DA9131"; + config_match = (chip_id == DA9121_TYPE_DA9122_DA9131); + break; + case DA9217_VARIANT_VRC: + type = "DA9217"; + config_match = (chip_id == DA9121_TYPE_DA9217); + break; + default: + type = "Unknown"; + break; + } + + dev_info(chip->dev, + "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n", + device_id, variant_id, type); + + if (!config_match) { + dev_err(chip->dev, "Device tree configuration does not match detected device.\n"); + ret = -EINVAL; + goto error; + } + + variant_mrc = (variant_id & DA9121_MASK_OTP_VARIANT_ID_MRC) + >> DA9121_SHIFT_OTP_VARIANT_ID_MRC; + + if ((device_id == DA9121_DEVICE_ID) && + (variant_mrc < DA9121_VARIANT_MRC_BASE)) { + dev_err(chip->dev, + "Cannot support variant MRC: 0x%02X\n", variant_mrc); + ret = -EINVAL; + } +error: + return ret; +} + +static int da9121_assign_chip_model(struct i2c_client *i2c, + struct da9121 *chip) +{ + struct regmap_config *regmap; + int ret = 0; + + chip->dev = &i2c->dev; + + switch (chip->variant_id) { + case DA9121_TYPE_DA9121_DA9130: + fallthrough; + case DA9121_TYPE_DA9217: + regmap = &da9121_1ch_regmap_config; + break; + case DA9121_TYPE_DA9122_DA9131: + fallthrough; + case DA9121_TYPE_DA9220_DA9132: + regmap = &da9121_2ch_regmap_config; + break; + } + + /* Set these up for of_regulator_match call which may want .of_map_modes */ + da9121_matches[0].desc = local_da9121_regulators[chip->variant_id][0]; + da9121_matches[1].desc = local_da9121_regulators[chip->variant_id][1]; + + chip->regmap = devm_regmap_init_i2c(i2c, regmap); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to configure a register map: %d\n", + ret); + return ret; + } + + ret = da9121_check_device_type(i2c, chip); + + return ret; +} + +static int da9121_config_irq(struct i2c_client *i2c, + struct da9121 *chip) +{ + unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + const int mask_all[4] = { 0, 0, 0xFF, 0xFF }; + int ret = 0; + + chip->chip_irq = i2c->irq; + + if (chip->chip_irq != 0) { + if (!of_property_read_u32(chip->dev->of_node, + "dlg,irq-polling-delay-passive-ms", + &p_delay)) { + if (p_delay < DA9121_MIN_POLLING_PERIOD_MS || + p_delay > DA9121_MAX_POLLING_PERIOD_MS) { + dev_warn(chip->dev, + "Out-of-range polling period %d ms\n", + p_delay); + p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + } + } + + chip->passive_delay = p_delay; + + ret = request_threaded_irq(chip->chip_irq, NULL, + da9121_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "da9121", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed IRQ request: %d\n", + chip->chip_irq); + goto error; + } + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", + ret); + goto regmap_error; + } + + INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); + dev_info(chip->dev, "Interrupt polling period set at %d ms\n", + chip->passive_delay); + } +error: + return ret; +regmap_error: + free_irq(chip->chip_irq, chip); + return ret; +} + +static const struct of_device_id da9121_dt_ids[] = { + { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9217", .data = (void *) DA9121_TYPE_DA9217 }, + { .compatible = "dlg,da9122", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9131", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9220", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, + { .compatible = "dlg,da9132", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, + { } +}; +MODULE_DEVICE_TABLE(of, da9121_dt_ids); + +static inline int da9121_of_get_id(struct device *dev) +{ + const struct of_device_id *id = of_match_device(da9121_dt_ids, dev); + + if (!id) { + dev_err(dev, "%s: Failed\n", __func__); + return -EINVAL; + } + return (uintptr_t)id->data; +} + +static int da9121_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da9121 *chip; + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto error; + } + + chip->pdata = i2c->dev.platform_data; + chip->variant_id = da9121_of_get_id(&i2c->dev); + + ret = da9121_assign_chip_model(i2c, chip); + if (ret < 0) + goto error; + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + goto error; + } + + ret = da9121_set_regulator_config(chip); + if (ret < 0) + goto error; + + ret = da9121_config_irq(i2c, chip); + +error: + return ret; +} + +static int da9121_i2c_remove(struct i2c_client *i2c) +{ + struct da9121 *chip = i2c_get_clientdata(i2c); + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + free_irq(chip->chip_irq, chip); + cancel_delayed_work_sync(&chip->work); + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + return ret; +} + +static const struct i2c_device_id da9121_i2c_id[] = { + {"da9121", DA9121_TYPE_DA9121_DA9130}, + {"da9130", DA9121_TYPE_DA9121_DA9130}, + {"da9217", DA9121_TYPE_DA9217}, + {"da9122", DA9121_TYPE_DA9122_DA9131}, + {"da9131", DA9121_TYPE_DA9122_DA9131}, + {"da9220", DA9121_TYPE_DA9220_DA9132}, + {"da9132", DA9121_TYPE_DA9220_DA9132}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); + +static struct i2c_driver da9121_regulator_driver = { + .driver = { + .name = "da9121", + .of_match_table = of_match_ptr(da9121_dt_ids), + }, + .probe = da9121_i2c_probe, + .remove = da9121_i2c_remove, + .id_table = da9121_i2c_id, +}; + +module_i2c_driver(da9121_regulator_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/da9121-regulator.h b/drivers/regulator/da9121-regulator.h new file mode 100644 index 000000000000..3c34cb889ca8 --- /dev/null +++ b/drivers/regulator/da9121-regulator.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DA9121 Single-channel dual-phase 10A buck converter + * DA9130 Single-channel dual-phase 10A buck converter (Automotive) + * DA9217 Single-channel dual-phase 6A buck converter + * DA9122 Dual-channel single-phase 5A buck converter + * DA9131 Dual-channel single-phase 5A buck converter (Automotive) + * DA9220 Dual-channel single-phase 3A buck converter + * DA9132 Dual-channel single-phase 3A buck converter (Automotive) + * + * Copyright (C) 2020 Dialog Semiconductor + * + * Authors: Steve Twiss, Dialog Semiconductor + * Adam Ward, Dialog Semiconductor + */ + +#ifndef __DA9121_REGISTERS_H__ +#define __DA9121_REGISTERS_H__ + +/* Values for: DA9121_REG_BUCK_BUCKx_4 registers, fields CHx_y_MODE + * DA9121_REG_BUCK_BUCKx_7 registers, fields CHx_RIPPLE_CANCEL + */ +#include <dt-bindings/regulator/dlg,da9121-regulator.h> + +enum da9121_variant { + DA9121_TYPE_DA9121_DA9130, + DA9121_TYPE_DA9220_DA9132, + DA9121_TYPE_DA9122_DA9131, + DA9121_TYPE_DA9217 +}; + +/* Minimum, maximum and default polling millisecond periods are provided + * here as an example. It is expected that any final implementation will + * include a modification of these settings to match the required + * application. + */ +#define DA9121_DEFAULT_POLLING_PERIOD_MS 3000 +#define DA9121_MAX_POLLING_PERIOD_MS 10000 +#define DA9121_MIN_POLLING_PERIOD_MS 1000 + +/* Registers */ + +#define DA9121_REG_SYS_STATUS_0 0x01 +#define DA9121_REG_SYS_STATUS_1 0x02 +#define DA9121_REG_SYS_STATUS_2 0x03 +#define DA9121_REG_SYS_EVENT_0 0x04 +#define DA9121_REG_SYS_EVENT_1 0x05 +#define DA9121_REG_SYS_EVENT_2 0x06 +#define DA9121_REG_SYS_MASK_0 0x07 +#define DA9121_REG_SYS_MASK_1 0x08 +#define DA9121_REG_SYS_MASK_2 0x09 +#define DA9121_REG_SYS_MASK_3 0x0A +#define DA9121_REG_SYS_CONFIG_0 0x0B +#define DA9121_REG_SYS_CONFIG_1 0x0C +#define DA9121_REG_SYS_CONFIG_2 0x0D +#define DA9121_REG_SYS_CONFIG_3 0x0E +#define DA9121_REG_SYS_GPIO0_0 0x10 +#define DA9121_REG_SYS_GPIO0_1 0x11 +#define DA9121_REG_SYS_GPIO1_0 0x12 +#define DA9121_REG_SYS_GPIO1_1 0x13 +#define DA9121_REG_SYS_GPIO2_0 0x14 +#define DA9121_REG_SYS_GPIO2_1 0x15 +#define DA9121_REG_BUCK_BUCK1_0 0x20 +#define DA9121_REG_BUCK_BUCK1_1 0x21 +#define DA9121_REG_BUCK_BUCK1_2 0x22 +#define DA9121_REG_BUCK_BUCK1_3 0x23 +#define DA9121_REG_BUCK_BUCK1_4 0x24 +#define DA9121_REG_BUCK_BUCK1_5 0x25 +#define DA9121_REG_BUCK_BUCK1_6 0x26 +#define DA9121_REG_BUCK_BUCK1_7 0x27 +#define DA9xxx_REG_BUCK_BUCK2_0 0x28 +#define DA9xxx_REG_BUCK_BUCK2_1 0x29 +#define DA9xxx_REG_BUCK_BUCK2_2 0x2A +#define DA9xxx_REG_BUCK_BUCK2_3 0x2B +#define DA9xxx_REG_BUCK_BUCK2_4 0x2C +#define DA9xxx_REG_BUCK_BUCK2_5 0x2D +#define DA9xxx_REG_BUCK_BUCK2_6 0x2E +#define DA9xxx_REG_BUCK_BUCK2_7 0x2F +#define DA9121_REG_OTP_DEVICE_ID 0x48 +#define DA9121_REG_OTP_VARIANT_ID 0x49 +#define DA9121_REG_OTP_CUSTOMER_ID 0x4A +#define DA9121_REG_OTP_CONFIG_ID 0x4B + +/* Register bits */ + +/* DA9121_REG_SYS_STATUS_0 */ + +#define DA9xxx_MASK_SYS_STATUS_0_SG BIT(2) +#define DA9121_MASK_SYS_STATUS_0_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_STATUS_0_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_STATUS_1 */ + +#define DA9xxx_MASK_SYS_STATUS_1_PG2 BIT(7) +#define DA9xxx_MASK_SYS_STATUS_1_OV2 BIT(6) +#define DA9xxx_MASK_SYS_STATUS_1_UV2 BIT(5) +#define DA9xxx_MASK_SYS_STATUS_1_OC2 BIT(4) +#define DA9121_MASK_SYS_STATUS_1_PG1 BIT(3) +#define DA9121_MASK_SYS_STATUS_1_OV1 BIT(2) +#define DA9121_MASK_SYS_STATUS_1_UV1 BIT(1) +#define DA9121_MASK_SYS_STATUS_1_OC1 BIT(0) + +/* DA9121_REG_SYS_STATUS_2 */ + +#define DA9121_MASK_SYS_STATUS_2_GPIO2 BIT(2) +#define DA9121_MASK_SYS_STATUS_2_GPIO1 BIT(1) +#define DA9121_MASK_SYS_STATUS_2_GPIO0 BIT(0) + +/* DA9121_REG_SYS_EVENT_0 */ + +#define DA9xxx_MASK_SYS_EVENT_0_E_SG BIT(2) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_EVENT_1 */ + +#define DA9xxx_MASK_SYS_EVENT_1_E_PG2 BIT(7) +#define DA9xxx_MASK_SYS_EVENT_1_E_OV2 BIT(6) +#define DA9xxx_MASK_SYS_EVENT_1_E_UV2 BIT(5) +#define DA9xxx_MASK_SYS_EVENT_1_E_OC2 BIT(4) +#define DA9121_MASK_SYS_EVENT_1_E_PG1 BIT(3) +#define DA9121_MASK_SYS_EVENT_1_E_OV1 BIT(2) +#define DA9121_MASK_SYS_EVENT_1_E_UV1 BIT(1) +#define DA9121_MASK_SYS_EVENT_1_E_OC1 BIT(0) + +/* DA9121_REG_SYS_EVENT_2 */ + +#define DA9121_MASK_SYS_EVENT_2_E_GPIO2 BIT(2) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO1 BIT(1) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO0 BIT(0) + +/* DA9121_REG_SYS_MASK_0 */ + +#define DA9xxx_MASK_SYS_MASK_0_M_SG BIT(2) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_MASK_1 */ + +#define DA9xxx_MASK_SYS_MASK_1_M_PG2 BIT(7) +#define DA9xxx_MASK_SYS_MASK_1_M_OV2 BIT(6) +#define DA9xxx_MASK_SYS_MASK_1_M_UV2 BIT(5) +#define DA9xxx_MASK_SYS_MASK_1_M_OC2 BIT(4) +#define DA9121_MASK_SYS_MASK_1_M_PG1 BIT(3) +#define DA9121_MASK_SYS_MASK_1_M_OV1 BIT(2) +#define DA9121_MASK_SYS_MASK_1_M_UV1 BIT(1) +#define DA9121_MASK_SYS_MASK_1_M_OC1 BIT(0) + +/* DA9121_REG_SYS_MASK_2 */ + +#define DA9121_MASK_SYS_MASK_2_M_GPIO2 BIT(2) +#define DA9121_MASK_SYS_MASK_2_M_GPIO1 BIT(1) +#define DA9121_MASK_SYS_MASK_2_M_GPIO0 BIT(0) + +/* DA9122_REG_SYS_MASK_3 */ + +#define DA9121_MASK_SYS_MASK_3_M_VR_HOT BIT(3) +#define DA9xxx_MASK_SYS_MASK_3_M_SG_STAT BIT(2) +#define DA9xxx_MASK_SYS_MASK_3_M_PG2_STAT BIT(1) +#define DA9121_MASK_SYS_MASK_3_M_PG1_STAT BIT(0) + +/* DA9121_REG_SYS_CONFIG_0 */ + +#define DA9121_MASK_SYS_CONFIG_0_CH1_DIS_DLY 0xF0 +#define DA9121_MASK_SYS_CONFIG_0_CH1_EN_DLY 0x0F + +/* DA9xxx_REG_SYS_CONFIG_1 */ + +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_DIS_DLY 0xF0 +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_EN_DLY 0x0F + +/* DA9121_REG_SYS_CONFIG_2 */ + +#define DA9121_MASK_SYS_CONFIG_2_OC_LATCHOFF 0x60 +#define DA9121_MASK_SYS_CONFIG_2_OC_DVC_MASK BIT(4) +#define DA9121_MASK_SYS_CONFIG_2_PG_DVC_MASK 0x0C + +/* DA9121_REG_SYS_CONFIG_3 */ + +#define DA9121_MASK_SYS_CONFIG_3_OSC_TUNE 0X70 +#define DA9121_MASK_SYS_CONFIG_3_I2C_TIMEOUT BIT(1) + +/* DA9121_REG_SYS_GPIO0_0 */ + +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_MODE 0X1E +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO0_1 */ + +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB 0x30 +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_POL BIT(2) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO1_0 */ + +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_MODE 0x1E +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO1_1 */ + +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB 0x30 +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_POL BIT(2) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO2_0 */ + +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_MODE 0x1E +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO2_1 */ + +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB 0x30 +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_POL BIT(2) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_TRIG 0x03 + +/* DA9121_REG_BUCK_BUCK1_0 / DA9xxx_REG_BUCK_BUCK2_0 */ + +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_DWN 0x70 +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_UP 0x0E +#define DA9121_MASK_BUCK_BUCKx_0_CHx_EN BIT(0) + +/* DA9121_REG_BUCK_BUCK1_1 / DA9xxx_REG_BUCK_BUCK2_1 */ + +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_SHDN 0x70 +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_STARTUP 0x0E +#define DA9121_MASK_BUCK_BUCKx_1_CHx_PD_DIS BIT(0) + +/* DA9121_REG_BUCK_BUCK1_2 / DA9xxx_REG_BUCK_BUCK2_2 */ + +#define DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM 0x0F + +/* DA9121_REG_BUCK_BUCK1_3 / DA9xxx_REG_BUCK_BUCK2_3 */ + +#define DA9121_MASK_BUCK_BUCKx_3_CHx_VMAX 0xFF + +/* DA9121_REG_BUCK_BUCK1_4 / DA9xxx_REG_BUCK_BUCK2_4 */ + +#define DA9121_MASK_BUCK_BUCKx_4_CHx_VSEL BIT(4) +#define DA9121_MASK_BUCK_BUCKx_4_CHx_B_MODE 0x0C +#define DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE 0x03 + +/* DA9121_REG_BUCK_BUCK1_5 / DA9xxx_REG_BUCK_BUCK2_5 */ + +#define DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_6 / DA9xxx_REG_BUCK_BUCK2_6 */ + +#define DA9121_MASK_BUCK_BUCKx_6_CHx_B_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_7 / DA9xxx_REG_BUCK_BUCK2_7 */ + +#define DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL 0x03 + + +/* DA9121_REG_OTP_DEVICE_ID */ + +#define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF + +#define DA9121_DEVICE_ID 0x05 + +/* DA9121_REG_OTP_VARIANT_ID */ + +#define DA9121_SHIFT_OTP_VARIANT_ID_MRC 4 +#define DA9121_MASK_OTP_VARIANT_ID_MRC 0xF0 +#define DA9121_SHIFT_OTP_VARIANT_ID_VRC 0 +#define DA9121_MASK_OTP_VARIANT_ID_VRC 0x0F + +#define DA9121_VARIANT_MRC_BASE 0x2 +#define DA9121_VARIANT_VRC 0x1 +#define DA9220_VARIANT_VRC 0x0 +#define DA9122_VARIANT_VRC 0x2 +#define DA9217_VARIANT_VRC 0x7 + +/* DA9121_REG_OTP_CUSTOMER_ID */ + +#define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF + +/* DA9121_REG_OTP_CONFIG_ID */ + +#define DA9121_MASK_OTP_CONFIG_ID_CONFIG_REV 0xFF + +#endif /* __DA9121_REGISTERS_H__ */ diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 3de7709bdcd4..02ad83153e19 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -18,6 +18,8 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_opp.h> #include <linux/regulator/driver.h> #include <linux/regulator/fixed.h> #include <linux/gpio/consumer.h> @@ -34,11 +36,13 @@ struct fixed_voltage_data { struct regulator_dev *dev; struct clk *enable_clock; - unsigned int clk_enable_counter; + unsigned int enable_counter; + int performance_state; }; struct fixed_dev_type { bool has_enable_clock; + bool has_performance_state; }; static int reg_clock_enable(struct regulator_dev *rdev) @@ -50,7 +54,7 @@ static int reg_clock_enable(struct regulator_dev *rdev) if (ret) return ret; - priv->clk_enable_counter++; + priv->enable_counter++; return ret; } @@ -60,16 +64,41 @@ static int reg_clock_disable(struct regulator_dev *rdev) struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); clk_disable_unprepare(priv->enable_clock); - priv->clk_enable_counter--; + priv->enable_counter--; return 0; } -static int reg_clock_is_enabled(struct regulator_dev *rdev) +static int reg_domain_enable(struct regulator_dev *rdev) { struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + int ret; + + ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state); + if (ret) + return ret; - return priv->clk_enable_counter > 0; + priv->enable_counter++; + + return ret; +} + +static int reg_domain_disable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + + priv->enable_counter--; + + return dev_pm_genpd_set_performance_state(dev, 0); +} + +static int reg_is_enabled(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + + return priv->enable_counter > 0; } @@ -129,7 +158,13 @@ static const struct regulator_ops fixed_voltage_ops = { static const struct regulator_ops fixed_voltage_clkenabled_ops = { .enable = reg_clock_enable, .disable = reg_clock_disable, - .is_enabled = reg_clock_is_enabled, + .is_enabled = reg_is_enabled, +}; + +static const struct regulator_ops fixed_voltage_domain_ops = { + .enable = reg_domain_enable, + .disable = reg_domain_disable, + .is_enabled = reg_is_enabled, }; static int reg_fixed_voltage_probe(struct platform_device *pdev) @@ -177,6 +212,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) dev_err(dev, "Can't get enable-clock from devicetree\n"); return -ENOENT; } + } else if (drvtype && drvtype->has_performance_state) { + drvdata->desc.ops = &fixed_voltage_domain_ops; + + drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0); + if (drvdata->performance_state < 0) { + dev_err(dev, "Can't get performance state from devicetree\n"); + return drvdata->performance_state; + } } else { drvdata->desc.ops = &fixed_voltage_ops; } @@ -260,6 +303,10 @@ static const struct fixed_dev_type fixed_clkenable_data = { .has_enable_clock = true, }; +static const struct fixed_dev_type fixed_domain_data = { + .has_performance_state = true, +}; + static const struct of_device_id fixed_of_match[] = { { .compatible = "regulator-fixed", @@ -270,6 +317,10 @@ static const struct of_device_id fixed_of_match[] = { .data = &fixed_clkenable_data, }, { + .compatible = "regulator-fixed-domain", + .data = &fixed_domain_data, + }, + { }, }; MODULE_DEVICE_TABLE(of, fixed_of_match); diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index e4bb09bbd3fa..f42b394a0c46 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -649,6 +649,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; return rdev->desc->volt_table[selector]; } diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 303d7e2dc838..e84be29533f4 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -892,7 +892,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) struct lp872x *lp; struct lp872x_platform_data *pdata; int ret; - const int lp872x_num_regulators[] = { + static const int lp872x_num_regulators[] = { [LP8720] = LP8720_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS, }; diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index e34face736f4..1d78b455cc48 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -269,3 +269,5 @@ module_exit(max14577_regulator_exit); MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max14577-regulator"); +MODULE_ALIAS("platform:max77836-regulator"); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index a731e826a037..5221f7a9df91 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -582,8 +582,8 @@ static int mc13892_regulator_probe(struct platform_device *pdev) /* update mc13892_vcam ops */ memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops, sizeof(struct regulator_ops)); - mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode, - mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode, + mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode; + mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode; mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops; mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators, diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 6d0ad74935b3..74ad92dc664a 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -22,8 +22,9 @@ #define VDD_LOW_SEL 0x0D #define VDD_HIGH_SEL 0x3F -#define MCP16502_FLT BIT(7) -#define MCP16502_ENS BIT(0) +#define MCP16502_FLT BIT(7) +#define MCP16502_DVSR GENMASK(3, 2) +#define MCP16502_ENS BIT(0) /* * The PMIC has four sets of registers corresponding to four power modes: @@ -54,13 +55,9 @@ * This function is useful for iterating over all regulators and accessing their * registers in a generic way or accessing a regulator device by its id. */ -#define MCP16502_BASE(i) (((i) + 1) << 4) +#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r) #define MCP16502_STAT_BASE(i) ((i) + 5) -#define MCP16502_OFFSET_MODE_A 0 -#define MCP16502_OFFSET_MODE_LPM 1 -#define MCP16502_OFFSET_MODE_HIB 2 - #define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL #define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE #define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY @@ -75,6 +72,29 @@ #define MCP16502_MIN_REG 0x0 #define MCP16502_MAX_REG 0x65 +/** + * enum mcp16502_reg - MCP16502 regulators's registers + * @MCP16502_REG_A: active state register + * @MCP16502_REG_LPM: low power mode state register + * @MCP16502_REG_HIB: hibernate state register + * @MCP16502_REG_SEQ: startup sequence register + * @MCP16502_REG_CFG: configuration register + */ +enum mcp16502_reg { + MCP16502_REG_A, + MCP16502_REG_LPM, + MCP16502_REG_HIB, + MCP16502_REG_HPM, + MCP16502_REG_SEQ, + MCP16502_REG_CFG, +}; + +/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */ +static const int mcp16502_ramp_b1l12[] = { 6250, 3125, 2083, 1563 }; + +/* Ramp delay (uV/us) for buck2, buck3, buck4. */ +static const int mcp16502_ramp_b234[] = { 3125, 1563, 1042, 781 }; + static unsigned int mcp16502_of_map_mode(unsigned int mode) { if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) @@ -93,6 +113,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode) .owner = THIS_MODULE, \ .n_voltages = MCP16502_VSEL + 1, \ .linear_ranges = _ranges, \ + .linear_min_sel = VDD_LOW_SEL, \ .n_linear_ranges = ARRAY_SIZE(_ranges), \ .of_match = of_match_ptr(_name), \ .of_map_mode = mcp16502_of_map_mode, \ @@ -114,8 +135,6 @@ enum { /* * struct mcp16502 - PMIC representation - * @rdev: the regulators belonging to this chip - * @rmap: regmap to be used for I2C communication * @lpm: LPM GPIO descriptor */ struct mcp16502 { @@ -143,22 +162,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) } /* - * mcp16502_get_reg() - get the PMIC's configuration register for opmode + * mcp16502_get_reg() - get the PMIC's state configuration register for opmode * * @rdev: the regulator whose register we are searching * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate */ -static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) +static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode) { - int reg = MCP16502_BASE(rdev_get_id(rdev)); - switch (opmode) { case MCP16502_OPMODE_ACTIVE: - return reg + MCP16502_OFFSET_MODE_A; + return MCP16502_REG_BASE(rdev_get_id(rdev), A); case MCP16502_OPMODE_LPM: - return reg + MCP16502_OFFSET_MODE_LPM; + return MCP16502_REG_BASE(rdev_get_id(rdev), LPM); case MCP16502_OPMODE_HIB: - return reg + MCP16502_OFFSET_MODE_HIB; + return MCP16502_REG_BASE(rdev_get_id(rdev), HIB); default: return -EINVAL; } @@ -178,7 +195,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev) unsigned int val; int ret, reg; - reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); + reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE); if (reg < 0) return reg; @@ -209,7 +226,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, int val; int reg; - reg = mcp16502_get_reg(rdev, op_mode); + reg = mcp16502_get_state_reg(rdev, op_mode); if (reg < 0) return reg; @@ -259,6 +276,80 @@ static int mcp16502_get_status(struct regulator_dev *rdev) return REGULATOR_STATUS_UNDEFINED; } +static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + static const u8 us_ramp[] = { 8, 16, 24, 32 }; + int id = rdev_get_id(rdev); + unsigned int uV_delta, val; + int ret; + + ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val); + if (ret) + return ret; + + val = (val & MCP16502_DVSR) >> 2; + uV_delta = abs(new_sel * rdev->desc->linear_ranges->step - + old_sel * rdev->desc->linear_ranges->step); + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b1l12[val]); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b234[val]); + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int mcp16502_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + const int *ramp; + int id = rdev_get_id(rdev); + unsigned int i, size; + + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ramp = mcp16502_ramp_b1l12; + size = ARRAY_SIZE(mcp16502_ramp_b1l12); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ramp = mcp16502_ramp_b234; + size = ARRAY_SIZE(mcp16502_ramp_b234); + break; + + default: + return -EINVAL; + } + + for (i = 0; i < size; i++) { + if (ramp[i] == ramp_delay) + break; + } + if (i == size) + return -EINVAL; + + return regmap_update_bits(rdev->regmap, MCP16502_REG_BASE(id, CFG), + MCP16502_DVSR, (i << 2)); +} + #ifdef CONFIG_SUSPEND /* * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC @@ -268,10 +359,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) { switch (pm_suspend_target_state) { case PM_SUSPEND_STANDBY: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM); case PM_SUSPEND_ON: case PM_SUSPEND_MEM: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB); default: dev_err(&rdev->dev, "invalid suspend target: %d\n", pm_suspend_target_state); @@ -353,6 +444,8 @@ static const struct regulator_ops mcp16502_buck_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, .set_mode = mcp16502_set_mode, .get_mode = mcp16502_get_mode, @@ -377,6 +470,8 @@ static const struct regulator_ops mcp16502_ldo_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, #ifdef CONFIG_SUSPEND .set_suspend_voltage = mcp16502_set_suspend_voltage, diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 06c0b15fe4c0..564f928eb1db 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -413,8 +413,12 @@ device_node *regulator_of_get_init_node(struct device *dev, for_each_available_child_of_node(search, child) { name = of_get_property(child, "regulator-compatible", NULL); - if (!name) - name = child->name; + if (!name) { + if (!desc->of_match_full_name) + name = child->name; + else + name = child->full_name; + } if (!strcmp(desc->of_match, name)) { of_node_put(search); diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c new file mode 100644 index 000000000000..308c27fa6ea8 --- /dev/null +++ b/drivers/regulator/pf8x00-regulator.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 NXP + * Copyright (C) 2019 Boundary Devices + * Copyright (C) 2020 Amarula Solutions(India) + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +/* registers */ +#define PF8X00_DEVICEID 0x00 +#define PF8X00_REVID 0x01 +#define PF8X00_EMREV 0x02 +#define PF8X00_PROGID 0x03 +#define PF8X00_IMS_INT 0x04 +#define PF8X00_IMS_THERM 0x07 +#define PF8X00_SW_MODE_INT 0x0a +#define PF8X00_SW_MODE_MASK 0x0b +#define PF8X00_IMS_SW_ILIM 0x12 +#define PF8X00_IMS_LDO_ILIM 0x15 +#define PF8X00_IMS_SW_UV 0x18 +#define PF8X00_IMS_SW_OV 0x1b +#define PF8X00_IMS_LDO_UV 0x1e +#define PF8X00_IMS_LDO_OV 0x21 +#define PF8X00_IMS_PWRON 0x24 +#define PF8X00_SYS_INT 0x27 +#define PF8X00_HARD_FAULT 0x29 +#define PF8X00_FSOB_FLAGS 0x2a +#define PF8X00_FSOB_SELECT 0x2b +#define PF8X00_ABIST_OV1 0x2c +#define PF8X00_ABIST_OV2 0x2d +#define PF8X00_ABIST_UV1 0x2e +#define PF8X00_ABIST_UV2 0x2f +#define PF8X00_TEST_FLAGS 0x30 +#define PF8X00_ABIST_RUN 0x31 +#define PF8X00_RANDOM_GEN 0x33 +#define PF8X00_RANDOM_CHK 0x34 +#define PF8X00_VMONEN1 0x35 +#define PF8X00_VMONEN2 0x36 +#define PF8X00_CTRL1 0x37 +#define PF8X00_CTRL2 0x38 +#define PF8X00_CTRL3 0x39 +#define PF8X00_PWRUP_CTRL 0x3a +#define PF8X00_RESETBMCU 0x3c +#define PF8X00_PGOOD 0x3d +#define PF8X00_PWRDN_DLY1 0x3e +#define PF8X00_PWRDN_DLY2 0x3f +#define PF8X00_FREQ_CTRL 0x40 +#define PF8X00_COINCELL_CTRL 0x41 +#define PF8X00_PWRON 0x42 +#define PF8X00_WD_CONFIG 0x43 +#define PF8X00_WD_CLEAR 0x44 +#define PF8X00_WD_EXPIRE 0x45 +#define PF8X00_WD_COUNTER 0x46 +#define PF8X00_FAULT_COUNTER 0x47 +#define PF8X00_FSAFE_COUNTER 0x48 +#define PF8X00_FAULT_TIMER 0x49 +#define PF8X00_AMUX 0x4a +#define PF8X00_SW1_CONFIG1 0x4d +#define PF8X00_LDO1_CONFIG1 0x85 +#define PF8X00_VSNVS_CONFIG1 0x9d +#define PF8X00_PAGE_SELECT 0x9f + +/* regulators */ +enum pf8x00_regulators { + PF8X00_LDO1, + PF8X00_LDO2, + PF8X00_LDO3, + PF8X00_LDO4, + PF8X00_BUCK1, + PF8X00_BUCK2, + PF8X00_BUCK3, + PF8X00_BUCK4, + PF8X00_BUCK5, + PF8X00_BUCK6, + PF8X00_BUCK7, + PF8X00_VSNVS, + + PF8X00_MAX_REGULATORS, +}; + +enum pf8x00_buck_states { + SW_CONFIG1, + SW_CONFIG2, + SW_PWRUP, + SW_MODE1, + SW_RUN_VOLT, + SW_STBY_VOLT, +}; +#define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1) + +enum pf8x00_ldo_states { + LDO_CONFIG1, + LDO_CONFIG2, + LDO_PWRUP, + LDO_RUN_VOLT, + LDO_STBY_VOLT, +}; +#define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1) + +enum swxilim_bits { + SWXILIM_2100_MA, + SWXILIM_2600_MA, + SWXILIM_3000_MA, + SWXILIM_4500_MA, +}; +#define PF8X00_SWXILIM_SHIFT 3 +#define PF8X00_SWXILIM_MASK GENMASK(4, 3) +#define PF8X00_SWXPHASE_MASK GENMASK(2, 0) +#define PF8X00_SWXPHASE_DEFAULT 0 +#define PF8X00_SWXPHASE_SHIFT 7 + +enum pf8x00_devid { + PF8100 = 0x0, + PF8121A = BIT(1), + PF8200 = BIT(3), +}; +#define PF8X00_FAM BIT(6) +#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4) +#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0) + +struct pf8x00_regulator { + struct regulator_desc desc; + u8 ilim; + u8 phase_shift; +}; + +struct pf8x00_chip { + struct regmap *regmap; + struct device *dev; +}; + +static const struct regmap_config pf8x00_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PF8X00_PAGE_SELECT, + .cache_type = REGCACHE_RBTREE, +}; + +/* VLDOx output: 1.5V to 5.0V */ +static const int pf8x00_ldo_voltages[] = { + 1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000, + 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000, +}; + +#define SWV(i) (6250 * i + 400000) +#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \ + SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7) + +/* Output: 0.4V to 1.8V */ +static const int pf8x00_sw1_to_6_voltages[] = { + SWV_LINE(0), + SWV_LINE(1), + SWV_LINE(2), + SWV_LINE(3), + SWV_LINE(4), + SWV_LINE(5), + SWV_LINE(6), + SWV_LINE(7), + SWV_LINE(8), + SWV_LINE(9), + SWV_LINE(10), + SWV_LINE(11), + SWV_LINE(12), + SWV_LINE(13), + SWV_LINE(14), + SWV_LINE(15), + SWV_LINE(16), + SWV_LINE(17), + SWV_LINE(18), + SWV_LINE(19), + SWV_LINE(20), + SWV_LINE(21), + 1500000, 1800000, +}; + +/* Output: 1.0V to 4.1V */ +static const int pf8x00_sw7_voltages[] = { + 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000, + 1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000, + 2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000, + 3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000, +}; + +/* Output: 1.8V, 3.0V, or 3.3V */ +static const int pf8x00_vsnvs_voltages[] = { + 0, 1800000, 3000000, 3300000, +}; + +static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc) +{ + return container_of(desc, struct pf8x00_regulator, desc); +} + +static void swxilim_select(const struct regulator_desc *desc, int ilim) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + u8 ilim_sel; + + switch (ilim) { + case 2100: + ilim_sel = SWXILIM_2100_MA; + break; + case 2600: + ilim_sel = SWXILIM_2600_MA; + break; + case 3000: + ilim_sel = SWXILIM_3000_MA; + break; + case 4500: + ilim_sel = SWXILIM_4500_MA; + break; + default: + ilim_sel = SWXILIM_2100_MA; + break; + } + + data->ilim = ilim_sel; +} + +static int pf8x00_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + struct pf8x00_chip *chip = config->driver_data; + int phase; + int val; + int ret; + + ret = of_property_read_u32(np, "nxp,ilim-ma", &val); + if (ret) + dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n", + desc->id - PF8X00_LDO4); + + swxilim_select(desc, val); + + ret = of_property_read_u32(np, "nxp,phase-shift", &val); + if (ret) { + dev_dbg(chip->dev, + "unspecified phase-shift for BUCK%d, use 0 degrees\n", + desc->id - PF8X00_LDO4); + val = PF8X00_SWXPHASE_DEFAULT; + } + + phase = val / 45; + if ((phase * 45) != val) { + dev_warn(config->dev, + "invalid phase_shift %d for BUCK%d, use 0 degrees\n", + (phase * 45), desc->id - PF8X00_LDO4); + phase = PF8X00_SWXPHASE_SHIFT; + } + + data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT; + + return 0; +} + +static const struct regulator_ops pf8x00_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_vsnvs_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +#define PF8X00LDO(_id, _name, base, voltages) \ + [PF8X00_LDO ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_LDO ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + LDO_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + LDO_CONFIG2, \ + .enable_val = 0x2, \ + .disable_val = 0x0, \ + .enable_mask = 2, \ + }, \ + } + +#define PF8X00BUCK(_id, _name, base, voltages) \ + [PF8X00_BUCK ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .of_parse_cb = pf8x00_of_parse_cb, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_BUCK ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + SW_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + SW_MODE1, \ + .enable_val = 0x3, \ + .disable_val = 0x0, \ + .enable_mask = 0x3, \ + .enable_time = 500, \ + }, \ + } + +#define PF8X00VSNVS(_name, base, voltages) \ + [PF8X00_VSNVS] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_vsnvs_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_VSNVS, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + }, \ + } + +static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = { + PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages), + PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages), + PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages), + PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages), + PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages), + PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages), +}; + +static int pf8x00_identify(struct pf8x00_chip *chip) +{ + unsigned int value; + u8 dev_fam, dev_id; + const char *name = NULL; + int ret; + + ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value); + if (ret) { + dev_err(chip->dev, "failed to read chip family\n"); + return ret; + } + + dev_fam = value & PF8X00_DEVICE_FAM_MASK; + switch (dev_fam) { + case PF8X00_FAM: + break; + default: + dev_err(chip->dev, + "Chip 0x%x is not from PF8X00 family\n", dev_fam); + return ret; + } + + dev_id = value & PF8X00_DEVICE_ID_MASK; + switch (dev_id) { + case PF8100: + name = "PF8100"; + break; + case PF8121A: + name = "PF8121A"; + break; + case PF8200: + name = "PF8100"; + break; + default: + dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id); + return -ENODEV; + } + + dev_info(chip->dev, "%s PMIC found.\n", name); + + return 0; +} + +static int pf8x00_i2c_probe(struct i2c_client *client) +{ + struct regulator_config config = { NULL, }; + struct pf8x00_chip *chip; + int id; + int ret; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->dev = &client->dev; + + chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "regmap allocation failed with err %d\n", ret); + return ret; + } + + ret = pf8x00_identify(chip); + if (ret) + return ret; + + for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) { + struct pf8x00_regulator *data = &pf8x00_regulators_data[id]; + struct regulator_dev *rdev; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + rdev = devm_regulator_register(&client->dev, &data->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&client->dev, + "failed to register %s regulator\n", data->desc.name); + return PTR_ERR(rdev); + } + + if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) { + u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2; + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXPHASE_MASK, + data->phase_shift); + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXILIM_MASK, + data->ilim << PF8X00_SWXILIM_SHIFT); + } + } + + return 0; +} + +static const struct of_device_id pf8x00_dt_ids[] = { + { .compatible = "nxp,pf8x00",}, + { } +}; +MODULE_DEVICE_TABLE(of, pf8x00_dt_ids); + +static const struct i2c_device_id pf8x00_i2c_id[] = { + { "pf8x00", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id); + +static struct i2c_driver pf8x00_regulator_driver = { + .id_table = pf8x00_i2c_id, + .driver = { + .name = "pf8x00", + .of_match_table = pf8x00_dt_ids, + }, + .probe_new = pf8x00_i2c_probe, +}; +module_i2c_driver(pf8x00_regulator_driver); + +MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); +MODULE_AUTHOR("Troy Kisky <troy.kisky@boundarydevices.com>"); +MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 01a12cfcea7c..d60d7d1b7fa2 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -105,15 +105,6 @@ static const int pfuze3000_sw2hi[] = { 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000, }; -static const struct i2c_device_id pfuze_device_id[] = { - {.name = "pfuze100", .driver_data = PFUZE100}, - {.name = "pfuze200", .driver_data = PFUZE200}, - {.name = "pfuze3000", .driver_data = PFUZE3000}, - {.name = "pfuze3001", .driver_data = PFUZE3001}, - { } -}; -MODULE_DEVICE_TABLE(i2c, pfuze_device_id); - static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, @@ -440,7 +431,6 @@ static struct pfuze_regulator pfuze3001_regulators[] = { PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; -#ifdef CONFIG_OF /* PFUZE100 */ static struct of_regulator_match pfuze100_matches[] = { { .name = "sw1ab", }, @@ -578,22 +568,6 @@ static inline struct device_node *match_of_node(int index) { return pfuze_matches[index].of_node; } -#else -static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) -{ - return 0; -} - -static inline struct regulator_init_data *match_init_data(int index) -{ - return NULL; -} - -static inline struct device_node *match_of_node(int index) -{ - return NULL; -} -#endif static struct pfuze_chip *syspm_pfuze_chip; @@ -708,8 +682,6 @@ static int pfuze100_regulator_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pfuze_chip *pfuze_chip; - struct pfuze_regulator_platform_data *pdata = - dev_get_platdata(&client->dev); struct regulator_config config = { }; int i, ret; const struct of_device_id *match; @@ -802,10 +774,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, desc = &pfuze_chip->regulator_descs[i].desc; - if (pdata) - init_data = pdata->init_data[i]; - else - init_data = match_init_data(i); + init_data = match_init_data(i); /* SW2~SW4 high bit check and modify the voltage value table */ if (i >= sw_check_start && i <= sw_check_end) { @@ -879,7 +848,6 @@ static int pfuze100_regulator_remove(struct i2c_client *client) } static struct i2c_driver pfuze_driver = { - .id_table = pfuze_device_id, .driver = { .name = "pfuze100-regulator", .of_match_table = pfuze_dt_ids, diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index d488325499a9..fe030ec4b7db 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -865,6 +865,60 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {}, +}; + static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), @@ -930,6 +984,33 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"), + {}, +}; + static int rpmh_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -985,6 +1066,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .data = pm8150l_vreg_data, }, { + .compatible = "qcom,pm8350-rpmh-regulators", + .data = pm8350_vreg_data, + }, + { + .compatible = "qcom,pm8350c-rpmh-regulators", + .data = pm8350c_vreg_data, + }, + { .compatible = "qcom,pm8998-rpmh-regulators", .data = pm8998_vreg_data, }, @@ -1000,6 +1089,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm6150l-rpmh-regulators", .data = pm6150l_vreg_data, }, + { + .compatible = "qcom,pmx55-rpmh-regulators", + .data = pmx55_vreg_data, + }, {} }; MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c new file mode 100644 index 000000000000..0e8b3caa8146 --- /dev/null +++ b/drivers/regulator/scmi-regulator.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// System Control and Management Interface (SCMI) based regulator driver +// +// Copyright (C) 2020 ARM Ltd. +// +// Implements a regulator driver on top of the SCMI Voltage Protocol. +// +// The ARM SCMI Protocol aims in general to hide as much as possible all the +// underlying operational details while providing an abstracted interface for +// its users to operate upon: as a consequence the resulting operational +// capabilities and configurability of this regulator device are much more +// limited than the ones usually available on a standard physical regulator. +// +// The supported SCMI regulator ops are restricted to the bare minimum: +// +// - 'status_ops': enable/disable/is_enabled +// - 'voltage_ops': get_voltage_sel/set_voltage_sel +// list_voltage/map_voltage +// +// Each SCMI regulator instance is associated, through the means of a proper DT +// entry description, to a specific SCMI Voltage Domain. + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/linear_range.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/scmi_protocol.h> +#include <linux/slab.h> +#include <linux/types.h> + +struct scmi_regulator { + u32 id; + struct scmi_device *sdev; + struct regulator_dev *rdev; + struct device_node *of_node; + struct regulator_desc desc; + struct regulator_config conf; +}; + +struct scmi_regulator_info { + int num_doms; + struct scmi_regulator **sregv; +}; + +static int scmi_reg_enable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); +} + +static int scmi_reg_disable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); +} + +static int scmi_reg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u32 config; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->config_get(handle, sreg->id, + &config); + if (ret) { + dev_err(&sreg->sdev->dev, + "Error %d reading regulator %s status.\n", + ret, sreg->desc.name); + return ret; + } + + return config & SCMI_VOLTAGE_ARCH_STATE_ON; +} + +static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); + if (ret) + return ret; + + return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); +} + +static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + volt_uV = sreg->desc.ops->list_voltage(rdev, selector); + if (volt_uV <= 0) + return -EINVAL; + + return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); +} + +static const struct regulator_ops scmi_reg_fixed_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, +}; + +static const struct regulator_ops scmi_reg_linear_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static const struct regulator_ops scmi_reg_discrete_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int +scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + s32 delta_uV; + + /* + * Note that SCMI voltage domains describable by linear ranges + * (segments) {low, high, step} are guaranteed to come in one single + * triplet by the SCMI Voltage Domain protocol support itself. + */ + + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); + + /* Rule out buggy negative-intervals answers from fw */ + if (delta_uV < 0) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + + if (!delta_uV) { + /* Just one fixed voltage exposed by SCMI */ + sreg->desc.fixed_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.n_voltages = 1; + sreg->desc.ops = &scmi_reg_fixed_ops; + } else { + /* One simple linear mapping. */ + sreg->desc.min_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.uV_step = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; + sreg->desc.linear_min_sel = 0; + sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step; + sreg->desc.ops = &scmi_reg_linear_ops; + } + + return 0; +} + +static int +scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + /* Discrete non linear levels are mapped to volt_table */ + sreg->desc.n_voltages = vinfo->num_levels; + + if (sreg->desc.n_voltages > 1) { + sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; + sreg->desc.ops = &scmi_reg_discrete_ops; + } else { + sreg->desc.fixed_uV = vinfo->levels_uv[0]; + sreg->desc.ops = &scmi_reg_fixed_ops; + } + + return 0; +} + +static int scmi_regulator_common_init(struct scmi_regulator *sreg) +{ + int ret; + const struct scmi_handle *handle = sreg->sdev->handle; + struct device *dev = &sreg->sdev->dev; + const struct scmi_voltage_info *vinfo; + + vinfo = handle->voltage_ops->info_get(handle, sreg->id); + if (!vinfo) { + dev_warn(dev, "Failure to get voltage domain %d\n", + sreg->id); + return -ENODEV; + } + + /* + * Regulator framework does not fully support negative voltages + * so we discard any voltage domain reported as supporting negative + * voltages: as a consequence each levels_uv entry is guaranteed to + * be non-negative from here on. + */ + if (vinfo->negative_volts_allowed) { + dev_warn(dev, "Negative voltages NOT supported...skip %s\n", + sreg->of_node->full_name); + return -EOPNOTSUPP; + } + + sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); + if (!sreg->desc.name) + return -ENOMEM; + + sreg->desc.id = sreg->id; + sreg->desc.type = REGULATOR_VOLTAGE; + sreg->desc.owner = THIS_MODULE; + sreg->desc.of_match_full_name = true; + sreg->desc.of_match = sreg->of_node->full_name; + sreg->desc.regulators_node = "regulators"; + if (vinfo->segmented) + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); + else + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); + if (ret) + return ret; + + /* + * Using the scmi device here to have DT searched from Voltage + * protocol node down. + */ + sreg->conf.dev = dev; + + /* Store for later retrieval via rdev_get_drvdata() */ + sreg->conf.driver_data = sreg; + + return 0; +} + +static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct device_node *np, + struct scmi_regulator_info *rinfo) +{ + u32 dom, ret; + + ret = of_property_read_u32(np, "reg", &dom); + if (ret) + return ret; + + if (dom >= rinfo->num_doms) + return -ENODEV; + + if (rinfo->sregv[dom]) { + dev_err(&sdev->dev, + "SCMI Voltage Domain %d already in use. Skipping: %s\n", + dom, np->full_name); + return -EINVAL; + } + + rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, + sizeof(struct scmi_regulator), + GFP_KERNEL); + if (!rinfo->sregv[dom]) + return -ENOMEM; + + rinfo->sregv[dom]->id = dom; + rinfo->sregv[dom]->sdev = sdev; + + /* get hold of good nodes */ + of_node_get(np); + rinfo->sregv[dom]->of_node = np; + + dev_dbg(&sdev->dev, + "Found SCMI Regulator entry -- OF node [%d] -> %s\n", + dom, np->full_name); + + return 0; +} + +static int scmi_regulator_probe(struct scmi_device *sdev) +{ + int d, ret, num_doms; + struct device_node *np, *child; + const struct scmi_handle *handle = sdev->handle; + struct scmi_regulator_info *rinfo; + + if (!handle || !handle->voltage_ops) + return -ENODEV; + + num_doms = handle->voltage_ops->num_domains_get(handle); + if (num_doms <= 0) { + if (!num_doms) { + dev_err(&sdev->dev, + "number of voltage domains invalid\n"); + num_doms = -EINVAL; + } else { + dev_err(&sdev->dev, + "failed to get voltage domains - err:%d\n", + num_doms); + } + + return num_doms; + } + + rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + /* Allocate pointers array for all possible domains */ + rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, + sizeof(void *), GFP_KERNEL); + if (!rinfo->sregv) + return -ENOMEM; + + rinfo->num_doms = num_doms; + + /* + * Start collecting into rinfo->sregv possibly good SCMI Regulators as + * described by a well-formed DT entry and associated with an existing + * plausible SCMI Voltage Domain number, all belonging to this SCMI + * platform instance node (handle->dev->of_node). + */ + np = of_find_node_by_name(handle->dev->of_node, "regulators"); + for_each_child_of_node(np, child) { + ret = process_scmi_regulator_of_node(sdev, child, rinfo); + /* abort on any mem issue */ + if (ret == -ENOMEM) + return ret; + } + + /* + * Register a regulator for each valid regulator-DT-entry that we + * can successfully reach via SCMI and has a valid associated voltage + * domain. + */ + for (d = 0; d < num_doms; d++) { + struct scmi_regulator *sreg = rinfo->sregv[d]; + + /* Skip empty slots */ + if (!sreg) + continue; + + ret = scmi_regulator_common_init(sreg); + /* Skip invalid voltage domains */ + if (ret) + continue; + + sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, + &sreg->conf); + if (IS_ERR(sreg->rdev)) { + sreg->rdev = NULL; + continue; + } + + dev_info(&sdev->dev, + "Regulator %s registered for domain [%d]\n", + sreg->desc.name, sreg->id); + } + + dev_set_drvdata(&sdev->dev, rinfo); + + return 0; +} + +static void scmi_regulator_remove(struct scmi_device *sdev) +{ + int d; + struct scmi_regulator_info *rinfo; + + rinfo = dev_get_drvdata(&sdev->dev); + if (!rinfo) + return; + + for (d = 0; d < rinfo->num_doms; d++) { + if (!rinfo->sregv[d]) + continue; + of_node_put(rinfo->sregv[d]->of_node); + } +} + +static const struct scmi_device_id scmi_regulator_id_table[] = { + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); + +static struct scmi_driver scmi_drv = { + .name = "scmi-regulator", + .probe = scmi_regulator_probe, + .remove = scmi_regulator_remove, + .id_table = scmi_regulator_id_table, +}; + +module_scmi_driver(scmi_drv); + +MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>"); +MODULE_DESCRIPTION("ARM SCMI regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index fe96ca3c88a5..26cc943d2034 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -390,7 +390,7 @@ static int ism_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx, } static struct ism_systemeid SYSTEM_EID = { - .seid_string = "IBM-SYSZ-IBMSEID00000000", + .seid_string = "IBM-SYSZ-ISMSEID00000000", .serial_number = "0000", .type = "0000", }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 128583dfccf2..c8dd8588f800 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -445,7 +445,7 @@ static int hisi_sas_task_prep(struct sas_task *task, } } - if (scmd) { + if (scmd && hisi_hba->shost->nr_hw_queues) { unsigned int dq_index; u32 blk_tag; diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index b1f3017b6547..29fcc44be2d5 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -807,13 +807,29 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code) } /** + * ibmvscsi_set_request_limit - Set the adapter request_limit in response to + * an adapter failure, reset, or SRP Login. Done under host lock to prevent + * race with SCSI command submission. + * @hostdata: adapter to adjust + * @limit: new request limit + */ +static void ibmvscsi_set_request_limit(struct ibmvscsi_host_data *hostdata, int limit) +{ + unsigned long flags; + + spin_lock_irqsave(hostdata->host->host_lock, flags); + atomic_set(&hostdata->request_limit, limit); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); +} + +/** * ibmvscsi_reset_host - Reset the connection to the server * @hostdata: struct ibmvscsi_host_data to reset */ static void ibmvscsi_reset_host(struct ibmvscsi_host_data *hostdata) { scsi_block_requests(hostdata->host); - atomic_set(&hostdata->request_limit, 0); + ibmvscsi_set_request_limit(hostdata, 0); purge_requests(hostdata, DID_ERROR); hostdata->action = IBMVSCSI_HOST_ACTION_RESET; @@ -1146,13 +1162,13 @@ static void login_rsp(struct srp_event_struct *evt_struct) dev_info(hostdata->dev, "SRP_LOGIN_REJ reason %u\n", evt_struct->xfer_iu->srp.login_rej.reason); /* Login failed. */ - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); return; default: dev_err(hostdata->dev, "Invalid login response typecode 0x%02x!\n", evt_struct->xfer_iu->srp.login_rsp.opcode); /* Login failed. */ - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); return; } @@ -1163,7 +1179,7 @@ static void login_rsp(struct srp_event_struct *evt_struct) * This value is set rather than added to request_limit because * request_limit could have been set to -1 by this client. */ - atomic_set(&hostdata->request_limit, + ibmvscsi_set_request_limit(hostdata, be32_to_cpu(evt_struct->xfer_iu->srp.login_rsp.req_lim_delta)); /* If we had any pending I/Os, kick them */ @@ -1195,13 +1211,13 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata) login->req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); - spin_lock_irqsave(hostdata->host->host_lock, flags); /* Start out with a request limit of 0, since this is negotiated in * the login request we are just sending and login requests always * get sent by the driver regardless of request_limit. */ - atomic_set(&hostdata->request_limit, 0); + ibmvscsi_set_request_limit(hostdata, 0); + spin_lock_irqsave(hostdata->host->host_lock, flags); rc = ibmvscsi_send_srp_event(evt_struct, hostdata, login_timeout * 2); spin_unlock_irqrestore(hostdata->host->host_lock, flags); dev_info(hostdata->dev, "sent SRP login\n"); @@ -1781,7 +1797,7 @@ static void ibmvscsi_handle_crq(struct viosrp_crq *crq, return; case VIOSRP_CRQ_XPORT_EVENT: /* Hypervisor telling us the connection is closed */ scsi_block_requests(hostdata->host); - atomic_set(&hostdata->request_limit, 0); + ibmvscsi_set_request_limit(hostdata, 0); if (crq->format == 0x06) { /* We need to re-setup the interpartition connection */ dev_info(hostdata->dev, "Re-enabling adapter!\n"); @@ -2137,12 +2153,12 @@ static void ibmvscsi_do_work(struct ibmvscsi_host_data *hostdata) } hostdata->action = IBMVSCSI_HOST_ACTION_NONE; + spin_unlock_irqrestore(hostdata->host->host_lock, flags); if (rc) { - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); dev_err(hostdata->dev, "error after %s\n", action); } - spin_unlock_irqrestore(hostdata->host->host_lock, flags); scsi_unblock_requests(hostdata->host); } @@ -2226,7 +2242,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) init_waitqueue_head(&hostdata->work_wait_q); hostdata->host = host; hostdata->dev = dev; - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT; if (map_persist_bufs(hostdata)) { diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 1f9005125313..b7a1dc24db38 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -554,10 +554,12 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport, fcport = qla_rport->fcport; - if (!qpair || !fcport || (qpair && !qpair->fw_started) || - (fcport && fcport->deleted)) + if (!qpair || !fcport) return -ENODEV; + if (!qpair->fw_started || fcport->deleted) + return -EBUSY; + vha = fcport->vha; if (!(fcport->nvme_flag & NVME_FLAG_REGISTERED)) diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c index 84f4416d366f..bd8623ee156a 100644 --- a/drivers/scsi/qla2xxx/qla_tmpl.c +++ b/drivers/scsi/qla2xxx/qla_tmpl.c @@ -1001,10 +1001,8 @@ qla27xx_mpi_fwdump(scsi_qla_host_t *vha, int hardware_locked) { ulong flags = 0; -#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&vha->hw->hardware_lock, flags); -#endif if (!vha->hw->mpi_fw_dump) { ql_log(ql_log_warn, vha, 0x02f3, "-> mpi_fwdump no buffer\n"); } else { @@ -1050,10 +1048,8 @@ qla27xx_mpi_fwdump(scsi_qla_host_t *vha, int hardware_locked) } bailout: -#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); -#endif } void diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index f2437a7570ce..9af50e6f94c4 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1714,15 +1714,16 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost) */ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) { - struct async_scan_data *data; + struct async_scan_data *data = NULL; unsigned long flags; if (strncmp(scsi_scan_type, "sync", 4) == 0) return NULL; + mutex_lock(&shost->scan_mutex); if (shost->async_scan) { shost_printk(KERN_DEBUG, shost, "%s called twice\n", __func__); - return NULL; + goto err; } data = kmalloc(sizeof(*data), GFP_KERNEL); @@ -1733,7 +1734,6 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) goto err; init_completion(&data->prev_finished); - mutex_lock(&shost->scan_mutex); spin_lock_irqsave(shost->host_lock, flags); shost->async_scan = 1; spin_unlock_irqrestore(shost->host_lock, flags); @@ -1748,6 +1748,7 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) return data; err: + mutex_unlock(&shost->scan_mutex); kfree(data); return NULL; } diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index af2126d2b2ff..8afb3f45d263 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -91,7 +91,7 @@ static struct generic_pm_domain *ti_sci_pd_xlate( struct genpd_onecell_data *genpd_data = data; unsigned int idx = genpdspec->args[0]; - if (genpdspec->args_count < 2) + if (genpdspec->args_count != 1 && genpdspec->args_count != 2) return ERR_PTR(-EINVAL); if (idx >= genpd_data->num_domains) { diff --git a/drivers/staging/comedi/drivers/cb_pcidas.c b/drivers/staging/comedi/drivers/cb_pcidas.c index 48ec2ee953dc..d740c4782775 100644 --- a/drivers/staging/comedi/drivers/cb_pcidas.c +++ b/drivers/staging/comedi/drivers/cb_pcidas.c @@ -1342,6 +1342,7 @@ static int cb_pcidas_auto_attach(struct comedi_device *dev, if (dev->irq && board->has_ao_fifo) { dev->write_subdev = s; s->subdev_flags |= SDF_CMD_WRITE; + s->len_chanlist = s->n_chan; s->do_cmdtest = cb_pcidas_ao_cmdtest; s->do_cmd = cb_pcidas_ao_cmd; s->cancel = cb_pcidas_ao_cancel; diff --git a/drivers/staging/fieldbus/anybuss/arcx-anybus.c b/drivers/staging/fieldbus/anybuss/arcx-anybus.c index 5b8d0bae9ff3..b5fded15e8a6 100644 --- a/drivers/staging/fieldbus/anybuss/arcx-anybus.c +++ b/drivers/staging/fieldbus/anybuss/arcx-anybus.c @@ -293,7 +293,7 @@ static int controller_probe(struct platform_device *pdev) regulator = devm_regulator_register(dev, &can_power_desc, &config); if (IS_ERR(regulator)) { err = PTR_ERR(regulator); - goto out_reset; + goto out_ida; } /* make controller info visible to userspace */ cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL); diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index cfb673a52b25..0bf545849b11 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -147,12 +147,6 @@ int cvm_oct_phy_setup_device(struct net_device *dev) phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); if (!phy_node && of_phy_is_fixed_link(priv->of_node)) { - int rc; - - rc = of_phy_register_fixed_link(priv->of_node); - if (rc) - return rc; - phy_node = of_node_get(priv->of_node); } if (!phy_node) diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 2c16230f993c..9ebd665e5d42 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -69,15 +69,17 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) else port = work->word1.cn38xx.ipprt; - if ((work->word2.snoip.err_code == 10) && (work->word1.len <= 64)) { + if ((work->word2.snoip.err_code == 10) && (work->word1.len <= 64)) /* * Ignore length errors on min size packets. Some * equipment incorrectly pads packets to 64+4FCS * instead of 60+4FCS. Note these packets still get * counted as frame errors. */ - } else if (work->word2.snoip.err_code == 5 || - work->word2.snoip.err_code == 7) { + return 0; + + if (work->word2.snoip.err_code == 5 || + work->word2.snoip.err_code == 7) { /* * We received a packet with either an alignment error * or a FCS error. This may be signalling that we are @@ -108,7 +110,10 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) /* Port received 0xd5 preamble */ work->packet_ptr.s.addr += i + 1; work->word1.len -= i + 5; - } else if ((*ptr & 0xf) == 0xd) { + return 0; + } + + if ((*ptr & 0xf) == 0xd) { /* Port received 0xd preamble */ work->packet_ptr.s.addr += i; work->word1.len -= i + 4; @@ -118,21 +123,20 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) ((*(ptr + 1) & 0xf) << 4); ptr++; } - } else { - printk_ratelimited("Port %d unknown preamble, packet dropped\n", - port); - cvm_oct_free_work(work); - return 1; + return 0; } + + printk_ratelimited("Port %d unknown preamble, packet dropped\n", + port); + cvm_oct_free_work(work); + return 1; } - } else { - printk_ratelimited("Port %d receive error code %d, packet dropped\n", - port, work->word2.snoip.err_code); - cvm_oct_free_work(work); - return 1; } - return 0; + printk_ratelimited("Port %d receive error code %d, packet dropped\n", + port, work->word2.snoip.err_code); + cvm_oct_free_work(work); + return 1; } static void copy_segments_to_skb(struct cvmx_wqe *work, struct sk_buff *skb) diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 204f0b1e2739..5dea6e96ec90 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -13,6 +13,7 @@ #include <linux/phy.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/of_mdio.h> #include <linux/of_net.h> #include <linux/if_ether.h> #include <linux/if_vlan.h> @@ -892,6 +893,14 @@ static int cvm_oct_probe(struct platform_device *pdev) break; } + if (priv->of_node && of_phy_is_fixed_link(priv->of_node)) { + if (of_phy_register_fixed_link(priv->of_node)) { + netdev_err(dev, "Failed to register fixed link for interface %d, port %d\n", + interface, priv->port); + dev->netdev_ops = NULL; + } + } + if (!dev->netdev_ops) { free_netdev(dev); } else if (register_netdev(dev) < 0) { diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 3a4202551cfc..9097bcbd67d8 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -179,6 +179,9 @@ struct vchiq_mmal_instance { /* ordered workqueue to process all bulk operations */ struct workqueue_struct *bulk_wq; + + /* handle for a vchiq instance */ + struct vchiq_instance *vchiq_instance; }; static struct mmal_msg_context * @@ -1840,6 +1843,7 @@ int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) mutex_unlock(&instance->vchiq_mutex); + vchiq_shutdown(instance->vchiq_instance); flush_workqueue(instance->bulk_wq); destroy_workqueue(instance->bulk_wq); @@ -1856,6 +1860,7 @@ EXPORT_SYMBOL_GPL(vchiq_mmal_finalise); int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) { int status; + int err = -ENODEV; struct vchiq_mmal_instance *instance; static struct vchiq_instance *vchiq_instance; struct vchiq_service_params_kernel params = { @@ -1890,17 +1895,21 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) status = vchiq_connect(vchiq_instance); if (status) { pr_err("Failed to connect VCHI instance (status=%d)\n", status); - return -EIO; + err = -EIO; + goto err_shutdown_vchiq; } instance = kzalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) - return -ENOMEM; + if (!instance) { + err = -ENOMEM; + goto err_shutdown_vchiq; + } mutex_init(&instance->vchiq_mutex); instance->bulk_scratch = vmalloc(PAGE_SIZE); + instance->vchiq_instance = vchiq_instance; mutex_init(&instance->context_map_lock); idr_init_base(&instance->context_map, 1); @@ -1932,7 +1941,9 @@ err_close_services: err_free: vfree(instance->bulk_scratch); kfree(instance); - return -ENODEV; +err_shutdown_vchiq: + vchiq_shutdown(vchiq_instance); + return err; } EXPORT_SYMBOL_GPL(vchiq_mmal_init); diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index 2ffa587aefaa..ed53d0b45592 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -21,7 +21,7 @@ static void device_wakeup(struct wfx_dev *wdev) if (!wdev->pdata.gpio_wakeup) return; - if (gpiod_get_value_cansleep(wdev->pdata.gpio_wakeup) >= 0) + if (gpiod_get_value_cansleep(wdev->pdata.gpio_wakeup) > 0) return; if (wfx_api_older_than(wdev, 1, 4)) { diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index 41f6a604a697..36b36ef39d05 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -31,13 +31,13 @@ static int wfx_get_hw_rate(struct wfx_dev *wdev, } return rate->idx + 14; } + // WFx only support 2GHz, else band information should be retrieved + // from ieee80211_tx_info + band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; if (rate->idx >= band->n_bitrates) { WARN(1, "wrong rate->idx value: %d", rate->idx); return -1; } - // WFx only support 2GHz, else band information should be retrieved - // from ieee80211_tx_info - band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; return band->bitrates[rate->idx].hw_value; } diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index ea84d08747df..590e6d072228 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -194,7 +194,7 @@ struct tcmu_tmr { uint8_t tmr_type; uint32_t tmr_cmd_cnt; - int16_t tmr_cmd_ids[0]; + int16_t tmr_cmd_ids[]; }; /* diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index f53bf336c0a2..6ade4a5c4840 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -200,7 +200,8 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, int name_len; int rc; - if (connection_method == TEE_IOCTL_LOGIN_PUBLIC) { + if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || + connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { /* Nil UUID to be passed to TEE environment */ uuid_copy(uuid, &uuid_null); return 0; diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c index 718e010fcb04..09baef4ccc39 100644 --- a/drivers/tty/serial/21285.c +++ b/drivers/tty/serial/21285.c @@ -50,25 +50,25 @@ static const char serial21285_name[] = "Footbridge UART"; static bool is_enabled(struct uart_port *port, int bit) { - unsigned long private_data = (unsigned long)port->private_data; + unsigned long *private_data = (unsigned long *)&port->private_data; - if (test_bit(bit, &private_data)) + if (test_bit(bit, private_data)) return true; return false; } static void enable(struct uart_port *port, int bit) { - unsigned long private_data = (unsigned long)port->private_data; + unsigned long *private_data = (unsigned long *)&port->private_data; - set_bit(bit, &private_data); + set_bit(bit, private_data); } static void disable(struct uart_port *port, int bit) { - unsigned long private_data = (unsigned long)port->private_data; + unsigned long *private_data = (unsigned long *)&port->private_data; - clear_bit(bit, &private_data); + clear_bit(bit, private_data); } #define is_tx_enabled(port) is_enabled(port, tx_enabled_bit) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ff4b88c637d0..bd047e1f9bea 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -314,9 +314,10 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); -static inline bool is_ls1028a_lpuart(struct lpuart_port *sport) +static inline bool is_layerscape_lpuart(struct lpuart_port *sport) { - return sport->devtype == LS1028A_LPUART; + return (sport->devtype == LS1021A_LPUART || + sport->devtype == LS1028A_LPUART); } static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport) @@ -1701,11 +1702,11 @@ static int lpuart32_startup(struct uart_port *port) UARTFIFO_FIFOSIZE_MASK); /* - * The LS1028A has a fixed length of 16 words. Although it supports the - * RX/TXSIZE fields their encoding is different. Eg the reference manual - * states 0b101 is 16 words. + * The LS1021A and LS1028A have a fixed FIFO depth of 16 words. + * Although they support the RX/TXSIZE fields, their encoding is + * different. Eg the reference manual states 0b101 is 16 words. */ - if (is_ls1028a_lpuart(sport)) { + if (is_layerscape_lpuart(sport)) { sport->rxfifo_size = 16; sport->txfifo_size = 16; sport->port.fifosize = sport->txfifo_size; diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 0db53b5b3acf..78acc270e39a 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -743,8 +743,13 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) return; if ((unsigned)value < ARRAY_SIZE(func_table)) { + unsigned long flags; + + spin_lock_irqsave(&func_buf_lock, flags); if (func_table[value]) puts_queue(vc, func_table[value]); + spin_unlock_irqrestore(&func_buf_lock, flags); + } else pr_err("k_fn called with value=%d\n", value); } @@ -1991,13 +1996,11 @@ out: #undef s #undef v -/* FIXME: This one needs untangling and locking */ +/* FIXME: This one needs untangling */ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) { struct kbsentry *kbs; - char *p; u_char *q; - u_char __user *up; int sz, fnw_sz; int delta; char *first_free, *fj, *fnw; @@ -2023,23 +2026,19 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) i = array_index_nospec(kbs->kb_func, MAX_NR_FUNC); switch (cmd) { - case KDGKBSENT: - sz = sizeof(kbs->kb_string) - 1; /* sz should have been - a struct member */ - up = user_kdgkb->kb_string; - p = func_table[i]; - if(p) - for ( ; *p && sz; p++, sz--) - if (put_user(*p, up++)) { - ret = -EFAULT; - goto reterr; - } - if (put_user('\0', up)) { - ret = -EFAULT; - goto reterr; - } - kfree(kbs); - return ((p && *p) ? -EOVERFLOW : 0); + case KDGKBSENT: { + /* size should have been a struct member */ + ssize_t len = sizeof(user_kdgkb->kb_string); + + spin_lock_irqsave(&func_buf_lock, flags); + len = strlcpy(kbs->kb_string, func_table[i] ? : "", len); + spin_unlock_irqrestore(&func_buf_lock, flags); + + ret = copy_to_user(user_kdgkb->kb_string, kbs->kb_string, + len + 1) ? -EFAULT : 0; + + goto reterr; + } case KDSKBSENT: if (!perm) { ret = -EPERM; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 0a33b8ababe3..2321775ef098 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -549,7 +549,7 @@ static int vt_io_fontreset(struct console_font_op *op) } static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, - struct vc_data *vc) + bool perm, struct vc_data *vc) { struct unimapdesc tmp; @@ -557,9 +557,11 @@ static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, return -EFAULT; switch (cmd) { case PIO_UNIMAP: + if (!perm) + return -EPERM; return con_set_unimap(vc, tmp.entry_ct, tmp.entries); case GIO_UNIMAP: - if (fg_console != vc->vc_num) + if (!perm && fg_console != vc->vc_num) return -EPERM; return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries); @@ -639,10 +641,7 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, case PIO_UNIMAP: case GIO_UNIMAP: - if (!perm) - return -EPERM; - - return do_unimap_ioctl(cmd, up, vc); + return do_unimap_ioctl(cmd, up, perm, vc); default: return -ENOIOCTLCMD; diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index 4761c852d9c4..d3121a32cc68 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -137,48 +137,36 @@ static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, struct usb_ctrlrequest *ctrl_req) { enum usb_device_state device_state = priv_dev->gadget.state; - struct cdns3_endpoint *priv_ep; u32 config = le16_to_cpu(ctrl_req->wValue); int result = 0; - int i; switch (device_state) { case USB_STATE_ADDRESS: - /* Configure non-control EPs */ - for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { - priv_ep = priv_dev->eps[i]; - if (!priv_ep) - continue; - - if (priv_ep->flags & EP_CLAIMED) - cdns3_ep_config(priv_ep); - } - result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); - if (result) - return result; - - if (!config) { - cdns3_hw_reset_eps_config(priv_dev); - usb_gadget_set_state(&priv_dev->gadget, - USB_STATE_ADDRESS); - } + if (result || !config) + goto reset_config; break; case USB_STATE_CONFIGURED: result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + if (!config && !result) + goto reset_config; - if (!config && !result) { - cdns3_hw_reset_eps_config(priv_dev); - usb_gadget_set_state(&priv_dev->gadget, - USB_STATE_ADDRESS); - } break; default: - result = -EINVAL; + return -EINVAL; } + return 0; + +reset_config: + if (result != USB_GADGET_DELAYED_STATUS) + cdns3_hw_reset_eps_config(priv_dev); + + usb_gadget_set_state(&priv_dev->gadget, + USB_STATE_ADDRESS); + return result; } @@ -705,6 +693,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, unsigned long flags; int ret = 0; u8 zlp = 0; + int i; spin_lock_irqsave(&priv_dev->lock, flags); trace_cdns3_ep0_queue(priv_dev, request); @@ -720,6 +709,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, u32 val; cdns3_select_ep(priv_dev, 0x00); + + /* + * Configure all non-control EPs which are not enabled by class driver + */ + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { + priv_ep = priv_dev->eps[i]; + if (priv_ep && priv_ep->flags & EP_CLAIMED && + !(priv_ep->flags & EP_ENABLED)) + cdns3_ep_config(priv_ep, 0); + } + cdns3_set_hw_configuration(priv_dev); cdns3_ep0_complete_setup(priv_dev, 0, 1); /* wait until configuration set */ @@ -811,6 +811,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) struct cdns3_usb_regs __iomem *regs; struct cdns3_endpoint *priv_ep; u32 max_packet_size = 64; + u32 ep_cfg; regs = priv_dev->regs; @@ -842,8 +843,10 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) BIT(0) | BIT(16)); } - writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), - ®s->ep_cfg); + ep_cfg = EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size); + + if (!(priv_ep->flags & EP_CONFIGURED)) + writel(ep_cfg, ®s->ep_cfg); writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); @@ -851,8 +854,10 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) /* init ep in */ cdns3_select_ep(priv_dev, USB_DIR_IN); - writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), - ®s->ep_cfg); + if (!(priv_ep->flags & EP_CONFIGURED)) + writel(ep_cfg, ®s->ep_cfg); + + priv_ep->flags |= EP_CONFIGURED; writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 6e7b70a2e352..66c1e6723eb1 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -296,6 +296,8 @@ static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep) */ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) { + int i; + writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf); cdns3_allow_enable_l1(priv_dev, 0); @@ -304,6 +306,10 @@ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) priv_dev->out_mem_is_allocated = 0; priv_dev->wait_for_setup = 0; priv_dev->using_streams = 0; + + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) + if (priv_dev->eps[i]) + priv_dev->eps[i]->flags &= ~EP_CONFIGURED; } /** @@ -506,7 +512,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, while (!list_empty(&priv_ep->wa2_descmiss_req_list)) { int chunk_end; - int length; descmiss_priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list); @@ -517,7 +522,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, break; chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH; - length = request->actual + descmiss_req->actual; request->status = descmiss_req->status; __cdns3_descmiss_copy_data(request, descmiss_req); list_del_init(&descmiss_priv_req->list); @@ -1746,11 +1750,8 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) { - if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) { - spin_unlock(&priv_dev->lock); + if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) priv_dev->gadget_driver->disconnect(&priv_dev->gadget); - spin_lock(&priv_dev->lock); - } } /** @@ -1761,6 +1762,7 @@ static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) */ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, u32 usb_ists) +__must_hold(&priv_dev->lock) { int speed = 0; @@ -1785,7 +1787,9 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, /* Disconnection detected */ if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) { + spin_unlock(&priv_dev->lock); cdns3_disconnect_gadget(priv_dev); + spin_lock(&priv_dev->lock); priv_dev->gadget.speed = USB_SPEED_UNKNOWN; usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); cdns3_hw_reset_eps_config(priv_dev); @@ -1979,27 +1983,6 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev, return 0; } -static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev, - struct cdns3_endpoint *priv_ep) -{ - if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER) - return; - - if (priv_dev->dev_ver >= DEV_VER_V3) { - u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); - - /* - * Stream capable endpoints are handled by using ep_tdl - * register. Other endpoints use TDL from TRB feature. - */ - cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, mask); - } - - /* Enable Stream Bit TDL chk and SID chk */ - cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_STREAM_EN | - EP_CFG_TDL_CHK | EP_CFG_SID_CHK); -} - static void cdns3_configure_dmult(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep) { @@ -2037,8 +2020,9 @@ static void cdns3_configure_dmult(struct cdns3_device *priv_dev, /** * cdns3_ep_config Configure hardware endpoint * @priv_ep: extended endpoint object + * @enable: set EP_CFG_ENABLE bit in ep_cfg register. */ -void cdns3_ep_config(struct cdns3_endpoint *priv_ep) +int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) { bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); struct cdns3_device *priv_dev = priv_ep->cdns3_dev; @@ -2099,7 +2083,7 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) break; default: /* all other speed are not supported */ - return; + return -EINVAL; } if (max_packet_size == 1024) @@ -2109,11 +2093,33 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) else priv_ep->trb_burst_size = 16; - ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, - !!priv_ep->dir); - if (ret) { - dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); - return; + /* onchip buffer is only allocated before configuration */ + if (!priv_dev->hw_configured_flag) { + ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, + !!priv_ep->dir); + if (ret) { + dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); + return ret; + } + } + + if (enable) + ep_cfg |= EP_CFG_ENABLE; + + if (priv_ep->use_streams && priv_dev->gadget.speed >= USB_SPEED_SUPER) { + if (priv_dev->dev_ver >= DEV_VER_V3) { + u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); + + /* + * Stream capable endpoints are handled by using ep_tdl + * register. Other endpoints use TDL from TRB feature. + */ + cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, + mask); + } + + /* Enable Stream Bit TDL chk and SID chk */ + ep_cfg |= EP_CFG_STREAM_EN | EP_CFG_TDL_CHK | EP_CFG_SID_CHK; } ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | @@ -2123,9 +2129,12 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) cdns3_select_ep(priv_dev, bEndpointAddress); writel(ep_cfg, &priv_dev->regs->ep_cfg); + priv_ep->flags |= EP_CONFIGURED; dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n", priv_ep->name, ep_cfg); + + return 0; } /* Find correct direction for HW endpoint according to description */ @@ -2266,7 +2275,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, u32 bEndpointAddress; unsigned long flags; int enable = 1; - int ret; + int ret = 0; int val; priv_ep = ep_to_cdns3_ep(ep); @@ -2305,6 +2314,17 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, bEndpointAddress = priv_ep->num | priv_ep->dir; cdns3_select_ep(priv_dev, bEndpointAddress); + /* + * For some versions of controller at some point during ISO OUT traffic + * DMA reads Transfer Ring for the EP which has never got doorbell. + * This issue was detected only on simulation, but to avoid this issue + * driver add protection against it. To fix it driver enable ISO OUT + * endpoint before setting DRBL. This special treatment of ISO OUT + * endpoints are recommended by controller specification. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) + enable = 0; + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { /* * Enable stream support (SS mode) related interrupts @@ -2315,13 +2335,17 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, EP_STS_EN_SIDERREN | EP_STS_EN_MD_EXITEN | EP_STS_EN_STREAMREN; priv_ep->use_streams = true; - cdns3_stream_ep_reconfig(priv_dev, priv_ep); + ret = cdns3_ep_config(priv_ep, enable); priv_dev->using_streams |= true; } + } else { + ret = cdns3_ep_config(priv_ep, enable); } - ret = cdns3_allocate_trb_pool(priv_ep); + if (ret) + goto exit; + ret = cdns3_allocate_trb_pool(priv_ep); if (ret) goto exit; @@ -2351,20 +2375,6 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, writel(reg, &priv_dev->regs->ep_sts_en); - /* - * For some versions of controller at some point during ISO OUT traffic - * DMA reads Transfer Ring for the EP which has never got doorbell. - * This issue was detected only on simulation, but to avoid this issue - * driver add protection against it. To fix it driver enable ISO OUT - * endpoint before setting DRBL. This special treatment of ISO OUT - * endpoints are recommended by controller specification. - */ - if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) - enable = 0; - - if (enable) - cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE); - ep->desc = desc; priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING | EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN); @@ -3265,10 +3275,13 @@ err0: } static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup) +__must_hold(&cdns->lock) { struct cdns3_device *priv_dev = cdns->gadget_dev; + spin_unlock(&cdns->lock); cdns3_disconnect_gadget(priv_dev); + spin_lock(&cdns->lock); priv_dev->gadget.speed = USB_SPEED_UNKNOWN; usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 1ccecd237530..21fa461c518e 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1072,7 +1072,7 @@ struct cdns3_trb { #define TRB_TDL_SS_SIZE_GET(p) (((p) & GENMASK(23, 17)) >> 17) /* transfer_len bitmasks - bits 31:24 */ -#define TRB_BURST_LEN(p) (((p) << 24) & GENMASK(31, 24)) +#define TRB_BURST_LEN(p) ((unsigned int)((p) << 24) & GENMASK(31, 24)) #define TRB_BURST_LEN_GET(p) (((p) & GENMASK(31, 24)) >> 24) /* Data buffer pointer bitmasks*/ @@ -1159,6 +1159,7 @@ struct cdns3_endpoint { #define EP_QUIRK_EXTRA_BUF_DET BIT(12) #define EP_QUIRK_EXTRA_BUF_EN BIT(13) #define EP_TDLCHK_EN BIT(15) +#define EP_CONFIGURED BIT(16) u32 flags; struct cdns3_request *descmis_req; @@ -1360,7 +1361,7 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, int cdns3_init_ep0(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep); void cdns3_ep0_config(struct cdns3_device *priv_dev); -void cdns3_ep_config(struct cdns3_endpoint *priv_ep); +int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable); void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 30ef946a8e1a..1e7568867910 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -508,6 +508,7 @@ static void acm_read_bulk_callback(struct urb *urb) "%s - cooling babbling device\n", __func__); usb_mark_last_busy(acm->dev); set_bit(rb->index, &acm->urbs_in_error_delay); + set_bit(ACM_ERROR_DELAY, &acm->flags); cooldown = true; break; default: @@ -533,7 +534,7 @@ static void acm_read_bulk_callback(struct urb *urb) if (stopped || stalled || cooldown) { if (stalled) - schedule_work(&acm->work); + schedule_delayed_work(&acm->dwork, 0); else if (cooldown) schedule_delayed_work(&acm->dwork, HZ / 2); return; @@ -563,13 +564,13 @@ static void acm_write_bulk(struct urb *urb) acm_write_done(acm, wb); spin_unlock_irqrestore(&acm->write_lock, flags); set_bit(EVENT_TTY_WAKEUP, &acm->flags); - schedule_work(&acm->work); + schedule_delayed_work(&acm->dwork, 0); } static void acm_softint(struct work_struct *work) { int i; - struct acm *acm = container_of(work, struct acm, work); + struct acm *acm = container_of(work, struct acm, dwork.work); if (test_bit(EVENT_RX_STALL, &acm->flags)) { smp_mb(); /* against acm_suspend() */ @@ -585,7 +586,7 @@ static void acm_softint(struct work_struct *work) if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) { for (i = 0; i < acm->rx_buflimit; i++) if (test_and_clear_bit(i, &acm->urbs_in_error_delay)) - acm_submit_read_urb(acm, i, GFP_NOIO); + acm_submit_read_urb(acm, i, GFP_KERNEL); } if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags)) @@ -1351,7 +1352,6 @@ made_compressed_probe: acm->ctrlsize = ctrlsize; acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; - INIT_WORK(&acm->work, acm_softint); INIT_DELAYED_WORK(&acm->dwork, acm_softint); init_waitqueue_head(&acm->wioctl); spin_lock_init(&acm->write_lock); @@ -1561,7 +1561,6 @@ static void acm_disconnect(struct usb_interface *intf) } acm_kill_urbs(acm); - cancel_work_sync(&acm->work); cancel_delayed_work_sync(&acm->dwork); tty_unregister_device(acm_tty_driver, acm->minor); @@ -1604,7 +1603,6 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) return 0; acm_kill_urbs(acm); - cancel_work_sync(&acm->work); cancel_delayed_work_sync(&acm->dwork); acm->urbs_in_error_delay = 0; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 9dce179d031b..8aef5eb769a0 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -112,8 +112,7 @@ struct acm { # define ACM_ERROR_DELAY 3 unsigned long urbs_in_error_delay; /* these need to be restarted after a delay */ struct usb_cdc_line_coding line; /* bits, stop, parity */ - struct work_struct work; /* work queue entry for various purposes*/ - struct delayed_work dwork; /* for cool downs needed in error recovery */ + struct delayed_work dwork; /* work queue entry for various purposes */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ struct async_icount iocount; /* counters for control line changes */ diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 98b7449c11f3..4dfa44d6cc3c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -839,6 +839,22 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev, return NULL; } +bool usb_driver_applicable(struct usb_device *udev, + struct usb_device_driver *udrv) +{ + if (udrv->id_table && udrv->match) + return usb_device_match_id(udev, udrv->id_table) != NULL && + udrv->match(udev); + + if (udrv->id_table) + return usb_device_match_id(udev, udrv->id_table) != NULL; + + if (udrv->match) + return udrv->match(udev); + + return false; +} + static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ @@ -853,17 +869,14 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) udev = to_usb_device(dev); udrv = to_usb_device_driver(drv); - if (udrv->id_table) - return usb_device_match_id(udev, udrv->id_table) != NULL; - - if (udrv->match) - return udrv->match(udev); - /* If the device driver under consideration does not have a * id_table or a match function, then let the driver's probe * function decide. */ - return 1; + if (!udrv->id_table && !udrv->match) + return 1; + + return usb_driver_applicable(udev, udrv); } else if (is_usb_interface(dev)) { struct usb_interface *intf; @@ -941,8 +954,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data) return 0; udev = to_usb_device(dev); - if (usb_device_match_id(udev, new_udriver->id_table) == NULL && - (!new_udriver->match || new_udriver->match(udev) == 0)) + if (!usb_driver_applicable(udev, new_udriver)) return 0; ret = device_reprobe(dev); diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 22c887f5c497..26f9fb9f67ca 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -205,9 +205,7 @@ static int __check_for_non_generic_match(struct device_driver *drv, void *data) udrv = to_usb_device_driver(drv); if (udrv == &usb_generic_driver) return 0; - if (usb_device_match_id(udev, udrv->id_table) != NULL) - return 1; - return (udrv->match && udrv->match(udev)); + return usb_driver_applicable(udev, udrv); } static bool usb_generic_driver_match(struct usb_device *udev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index c893f54a3420..82538daac8b8 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -74,6 +74,8 @@ extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev, const struct usb_device_id *id); +extern bool usb_driver_applicable(struct usb_device *udev, + struct usb_device_driver *udrv); extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index bdf0925da6b6..841daec70b6e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * core.c - DesignWare USB3 DRD Controller Core file * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 74323b10a64a..2f95f08ca511 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1277,7 +1277,7 @@ struct dwc3_event_type { #define DWC3_DEPEVT_EPCMDCMPLT 0x07 /** - * struct dwc3_event_depvt - Device Endpoint Events + * struct dwc3_event_depevt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) * @endpoint_number: number of the endpoint * @endpoint_event: The event we have: diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 05b176c82cc5..c6d455f2bb92 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1245,7 +1245,7 @@ int usb_string_id(struct usb_composite_dev *cdev) EXPORT_SYMBOL_GPL(usb_string_id); /** - * usb_string_ids() - allocate unused string IDs in batch + * usb_string_ids_tab() - allocate unused string IDs in batch * @cdev: the device whose string descriptor IDs are being allocated * @str: an array of usb_string objects to assign numbers to * Context: single threaded during gadget setup diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index e077b2ca53c5..869d9c4de5fc 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -479,8 +479,8 @@ static int tegra_ehci_probe(struct platform_device *pdev) u_phy->otg->host = hcd_to_bus(hcd); irq = platform_get_irq(pdev, 0); - if (!irq) { - err = -ENODEV; + if (irq < 0) { + err = irq; goto cleanup_phy; } diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index ae8f60f6e6a5..44a7e58a26e3 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -94,10 +94,13 @@ static struct platform_device *fsl_usb2_device_register( pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask; - if (!pdev->dev.dma_mask) + if (!pdev->dev.dma_mask) { pdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask; - else - dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + } else { + retval = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) + goto error; + } retval = platform_device_add_data(pdev, pdata, sizeof(*pdata)); if (retval) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index fe405cd38dbc..138ba4528dd3 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2252,8 +2252,8 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, if (!rhub->num_ports) return; - rhub->ports = kcalloc_node(rhub->num_ports, sizeof(rhub->ports), flags, - dev_to_node(dev)); + rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports), + flags, dev_to_node(dev)); for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c26c06e5c88c..bf89172c43ca 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -23,6 +23,8 @@ #define SSIC_PORT_CFG2_OFFSET 0x30 #define PROG_DONE (1 << 30) #define SSIC_PORT_UNUSED (1 << 31) +#define SPARSE_DISABLE_BIT 17 +#define SPARSE_CNTL_ENABLE 0xC12C /* Device for a quirk */ #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 @@ -161,6 +163,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) (pdev->device == 0x15e0 || pdev->device == 0x15e1)) xhci->quirks |= XHCI_SNPS_BROKEN_SUSPEND; + if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x15e5) + xhci->quirks |= XHCI_DISABLE_SPARSE; + if (pdev->vendor == PCI_VENDOR_ID_AMD) xhci->quirks |= XHCI_TRUST_TX_LENGTH; @@ -498,6 +503,15 @@ static void xhci_pme_quirk(struct usb_hcd *hcd) readl(reg); } +static void xhci_sparse_control_quirk(struct usb_hcd *hcd) +{ + u32 reg; + + reg = readl(hcd->regs + SPARSE_CNTL_ENABLE); + reg &= ~BIT(SPARSE_DISABLE_BIT); + writel(reg, hcd->regs + SPARSE_CNTL_ENABLE); +} + static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -517,6 +531,9 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) xhci_ssic_port_unused_quirk(hcd, true); + if (xhci->quirks & XHCI_DISABLE_SPARSE) + xhci_sparse_control_quirk(hcd); + ret = xhci_suspend(xhci, do_wakeup); if (ret && (xhci->quirks & XHCI_SSIC_PORT_UNUSED)) xhci_ssic_port_unused_quirk(hcd, false); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 482fe8c5e3b4..d4a8d0efbbc4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3533,11 +3533,14 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg(xhci, "Slot %u ep ctx %u now has streams.\n", udev->slot_id, ep_index); vdev->eps[ep_index].ep_state |= EP_HAS_STREAMS; - xhci_debugfs_create_stream_files(xhci, vdev, ep_index); } xhci_free_command(xhci, config_cmd); spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) { + ep_index = xhci_get_endpoint_index(&eps[i]->desc); + xhci_debugfs_create_stream_files(xhci, vdev, ep_index); + } /* Subtract 1 for stream 0, which drivers can't use */ return num_streams - 1; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8be88379c0fb..ebb359ebb261 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1877,6 +1877,7 @@ struct xhci_hcd { #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) #define XHCI_RENESAS_FW_QUIRK BIT_ULL(36) #define XHCI_SKIP_PHY_INIT BIT_ULL(37) +#define XHCI_DISABLE_SPARSE BIT_ULL(38) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index b403094a6b3a..579d8c84de42 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -163,17 +163,23 @@ static const struct power_supply_desc apple_mfi_fc_desc = { .property_is_writeable = apple_mfi_fc_property_is_writeable }; +static bool mfi_fc_match(struct usb_device *udev) +{ + int idProduct; + + idProduct = le16_to_cpu(udev->descriptor.idProduct); + /* See comment above mfi_fc_id_table[] */ + return (idProduct >= 0x1200 && idProduct <= 0x12ff); +} + static int mfi_fc_probe(struct usb_device *udev) { struct power_supply_config battery_cfg = {}; struct mfi_device *mfi = NULL; - int err, idProduct; + int err; - idProduct = le16_to_cpu(udev->descriptor.idProduct); - /* See comment above mfi_fc_id_table[] */ - if (idProduct < 0x1200 || idProduct > 0x12ff) { + if (!mfi_fc_match(udev)) return -ENODEV; - } mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); if (!mfi) { @@ -220,6 +226,7 @@ static struct usb_device_driver mfi_fc_driver = { .probe = mfi_fc_probe, .disconnect = mfi_fc_disconnect, .id_table = mfi_fc_id_table, + .match = mfi_fc_match, .generic_subclass = 1, }; diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index b069a5122aaa..cf720e944aaa 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -71,7 +71,7 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) EXPORT_SYMBOL_GPL(fwnode_typec_switch_get); /** - * typec_put_switch - Release USB Type-C orientation switch + * typec_switch_put - Release USB Type-C orientation switch * @sw: USB Type-C orientation switch * * Decrement reference count for @sw. diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index ce0bd7b3ad88..2a618f02f4f1 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -544,11 +544,10 @@ static int stusb160x_get_fw_caps(struct stusb160x *chip, */ ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); if (!ret) { - chip->port_type = typec_find_port_power_role(cap_str); - if (chip->port_type < 0) { - ret = chip->port_type; + ret = typec_find_port_power_role(cap_str); + if (ret < 0) return ret; - } + chip->port_type = ret; } chip->capability.type = chip->port_type; @@ -565,15 +564,13 @@ static int stusb160x_get_fw_caps(struct stusb160x *chip, */ ret = fwnode_property_read_string(fwnode, "power-opmode", &cap_str); if (!ret) { - chip->pwr_opmode = typec_find_pwr_opmode(cap_str); + ret = typec_find_pwr_opmode(cap_str); /* Power delivery not yet supported */ - if (chip->pwr_opmode < 0 || - chip->pwr_opmode == TYPEC_PWR_MODE_PD) { - ret = chip->pwr_opmode < 0 ? chip->pwr_opmode : -EINVAL; - dev_err(chip->dev, "bad power operation mode: %d\n", - chip->pwr_opmode); - return ret; + if (ret < 0 || ret == TYPEC_PWR_MODE_PD) { + dev_err(chip->dev, "bad power operation mode: %d\n", ret); + return -EINVAL; } + chip->pwr_opmode = ret; } return 0; @@ -632,6 +629,7 @@ static const struct of_device_id stusb160x_of_match[] = { { .compatible = "st,stusb1600", .data = &stusb1600_regmap_config}, {}, }; +MODULE_DEVICE_TABLE(of, stusb160x_of_match); static int stusb160x_probe(struct i2c_client *client) { @@ -729,8 +727,8 @@ static int stusb160x_probe(struct i2c_client *client) } chip->port = typec_register_port(chip->dev, &chip->capability); - if (!chip->port) { - ret = -ENODEV; + if (IS_ERR(chip->port)) { + ret = PTR_ERR(chip->port); goto all_reg_disable; } diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 55535c4f66bf..a6fae1f86505 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2890,6 +2890,9 @@ static void tcpm_reset_port(struct tcpm_port *port) static void tcpm_detach(struct tcpm_port *port) { + if (tcpm_port_is_disconnected(port)) + port->hard_reset_count = 0; + if (!port->attached) return; @@ -2898,9 +2901,6 @@ static void tcpm_detach(struct tcpm_port *port) port->tcpc->set_bist_data(port->tcpc, false); } - if (tcpm_port_is_disconnected(port)) - port->hard_reset_count = 0; - tcpm_reset_port(port); } diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index ef1c550f8266..4b6195666c58 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -239,7 +239,6 @@ static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr u64 paend; struct scatterlist *sg; struct device *dma = mvdev->mdev->device; - int ret; for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1); map; map = vhost_iotlb_itree_next(map, start, mr->end - 1)) { @@ -277,8 +276,8 @@ static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr done: mr->log_size = log_entity_size; mr->nsg = nsg; - ret = dma_map_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0); - if (!ret) + err = dma_map_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0); + if (!err) goto err_map; err = create_direct_mr(mvdev, mr); diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 2629911c29bb..6a90fdb9cbfc 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -38,6 +38,10 @@ static int batch_mapping = 1; module_param(batch_mapping, int, 0444); MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable"); +static char *macaddr; +module_param(macaddr, charp, 0); +MODULE_PARM_DESC(macaddr, "Ethernet MAC address"); + struct vdpasim_virtqueue { struct vringh vring; struct vringh_kiov iov; @@ -60,7 +64,8 @@ struct vdpasim_virtqueue { static u64 vdpasim_features = (1ULL << VIRTIO_F_ANY_LAYOUT) | (1ULL << VIRTIO_F_VERSION_1) | - (1ULL << VIRTIO_F_ACCESS_PLATFORM); + (1ULL << VIRTIO_F_ACCESS_PLATFORM) | + (1ULL << VIRTIO_NET_F_MAC); /* State of each vdpasim device */ struct vdpasim { @@ -361,7 +366,9 @@ static struct vdpasim *vdpasim_create(void) spin_lock_init(&vdpasim->iommu_lock); dev = &vdpasim->vdpa.dev; - dev->coherent_dma_mask = DMA_BIT_MASK(64); + dev->dma_mask = &dev->coherent_dma_mask; + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + goto err_iommu; set_dma_ops(dev, &vdpasim_dma_ops); vdpasim->iommu = vhost_iotlb_alloc(2048, 0); @@ -372,7 +379,15 @@ static struct vdpasim *vdpasim_create(void) if (!vdpasim->buffer) goto err_iommu; - eth_random_addr(vdpasim->config.mac); + if (macaddr) { + mac_pton(macaddr, vdpasim->config.mac); + if (!is_valid_ether_addr(vdpasim->config.mac)) { + ret = -EADDRNOTAVAIL; + goto err_iommu; + } + } else { + eth_random_addr(vdpasim->config.mac); + } vringh_set_iotlb(&vdpasim->vqs[0].vring, vdpasim->iommu); vringh_set_iotlb(&vdpasim->vqs[1].vring, vdpasim->iommu); @@ -574,6 +589,16 @@ static u32 vdpasim_get_generation(struct vdpa_device *vdpa) return vdpasim->generation; } +static struct vdpa_iova_range vdpasim_get_iova_range(struct vdpa_device *vdpa) +{ + struct vdpa_iova_range range = { + .first = 0ULL, + .last = ULLONG_MAX, + }; + + return range; +} + static int vdpasim_set_map(struct vdpa_device *vdpa, struct vhost_iotlb *iotlb) { @@ -657,6 +682,7 @@ static const struct vdpa_config_ops vdpasim_net_config_ops = { .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, .get_generation = vdpasim_get_generation, + .get_iova_range = vdpasim_get_iova_range, .dma_map = vdpasim_dma_map, .dma_unmap = vdpasim_dma_unmap, .free = vdpasim_free, @@ -683,6 +709,7 @@ static const struct vdpa_config_ops vdpasim_net_batch_config_ops = { .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, .get_generation = vdpasim_get_generation, + .get_iova_range = vdpasim_get_iova_range, .set_map = vdpasim_set_map, .free = vdpasim_free, }; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index a2dbc85e0b0d..2754f3069738 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -47,6 +47,7 @@ struct vhost_vdpa { int minor; struct eventfd_ctx *config_ctx; int in_batch; + struct vdpa_iova_range range; }; static DEFINE_IDA(vhost_vdpa_ida); @@ -103,6 +104,9 @@ static void vhost_vdpa_setup_vq_irq(struct vhost_vdpa *v, u16 qid) vq->call_ctx.producer.token = vq->call_ctx.ctx; vq->call_ctx.producer.irq = irq; ret = irq_bypass_register_producer(&vq->call_ctx.producer); + if (unlikely(ret)) + dev_info(&v->dev, "vq %u, irq bypass producer (token %p) registration fails, ret = %d\n", + qid, vq->call_ctx.producer.token, ret); } static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) @@ -337,6 +341,16 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp) return 0; } +static long vhost_vdpa_get_iova_range(struct vhost_vdpa *v, u32 __user *argp) +{ + struct vhost_vdpa_iova_range range = { + .first = v->range.first, + .last = v->range.last, + }; + + return copy_to_user(argp, &range, sizeof(range)); +} + static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, void __user *argp) { @@ -421,12 +435,11 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, void __user *argp = (void __user *)arg; u64 __user *featurep = argp; u64 features; - long r; + long r = 0; if (cmd == VHOST_SET_BACKEND_FEATURES) { - r = copy_from_user(&features, featurep, sizeof(features)); - if (r) - return r; + if (copy_from_user(&features, featurep, sizeof(features))) + return -EFAULT; if (features & ~VHOST_VDPA_BACKEND_FEATURES) return -EOPNOTSUPP; vhost_set_backend_features(&v->vdev, features); @@ -469,7 +482,11 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, break; case VHOST_GET_BACKEND_FEATURES: features = VHOST_VDPA_BACKEND_FEATURES; - r = copy_to_user(featurep, &features, sizeof(features)); + if (copy_to_user(featurep, &features, sizeof(features))) + r = -EFAULT; + break; + case VHOST_VDPA_GET_IOVA_RANGE: + r = vhost_vdpa_get_iova_range(v, argp); break; default: r = vhost_dev_ioctl(&v->vdev, cmd, argp); @@ -588,19 +605,25 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, struct vhost_dev *dev = &v->vdev; struct vhost_iotlb *iotlb = dev->iotlb; struct page **page_list; - struct vm_area_struct **vmas; + unsigned long list_size = PAGE_SIZE / sizeof(struct page *); unsigned int gup_flags = FOLL_LONGTERM; - unsigned long map_pfn, last_pfn = 0; - unsigned long npages, lock_limit; - unsigned long i, nmap = 0; + unsigned long npages, cur_base, map_pfn, last_pfn = 0; + unsigned long locked, lock_limit, pinned, i; u64 iova = msg->iova; - long pinned; int ret = 0; + if (msg->iova < v->range.first || + msg->iova + msg->size - 1 > v->range.last) + return -EINVAL; + if (vhost_iotlb_itree_first(iotlb, msg->iova, msg->iova + msg->size - 1)) return -EEXIST; + page_list = (struct page **) __get_free_page(GFP_KERNEL); + if (!page_list) + return -ENOMEM; + if (msg->perm & VHOST_ACCESS_WO) gup_flags |= FOLL_WRITE; @@ -608,86 +631,61 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, if (!npages) return -EINVAL; - page_list = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); - vmas = kvmalloc_array(npages, sizeof(struct vm_area_struct *), - GFP_KERNEL); - if (!page_list || !vmas) { - ret = -ENOMEM; - goto free; - } - mmap_read_lock(dev->mm); + locked = atomic64_add_return(npages, &dev->mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (npages + atomic64_read(&dev->mm->pinned_vm) > lock_limit) { - ret = -ENOMEM; - goto unlock; - } - pinned = pin_user_pages(msg->uaddr & PAGE_MASK, npages, gup_flags, - page_list, vmas); - if (npages != pinned) { - if (pinned < 0) { - ret = pinned; - } else { - unpin_user_pages(page_list, pinned); - ret = -ENOMEM; - } - goto unlock; + if (locked > lock_limit) { + ret = -ENOMEM; + goto out; } + cur_base = msg->uaddr & PAGE_MASK; iova &= PAGE_MASK; - map_pfn = page_to_pfn(page_list[0]); - - /* One more iteration to avoid extra vdpa_map() call out of loop. */ - for (i = 0; i <= npages; i++) { - unsigned long this_pfn; - u64 csize; - - /* The last chunk may have no valid PFN next to it */ - this_pfn = i < npages ? page_to_pfn(page_list[i]) : -1UL; - - if (last_pfn && (this_pfn == -1UL || - this_pfn != last_pfn + 1)) { - /* Pin a contiguous chunk of memory */ - csize = last_pfn - map_pfn + 1; - ret = vhost_vdpa_map(v, iova, csize << PAGE_SHIFT, - map_pfn << PAGE_SHIFT, - msg->perm); - if (ret) { - /* - * Unpin the rest chunks of memory on the - * flight with no corresponding vdpa_map() - * calls having been made yet. On the other - * hand, vdpa_unmap() in the failure path - * is in charge of accounting the number of - * pinned pages for its own. - * This asymmetrical pattern of accounting - * is for efficiency to pin all pages at - * once, while there is no other callsite - * of vdpa_map() than here above. - */ - unpin_user_pages(&page_list[nmap], - npages - nmap); - goto out; + + while (npages) { + pinned = min_t(unsigned long, npages, list_size); + ret = pin_user_pages(cur_base, pinned, + gup_flags, page_list, NULL); + if (ret != pinned) + goto out; + + if (!last_pfn) + map_pfn = page_to_pfn(page_list[0]); + + for (i = 0; i < ret; i++) { + unsigned long this_pfn = page_to_pfn(page_list[i]); + u64 csize; + + if (last_pfn && (this_pfn != last_pfn + 1)) { + /* Pin a contiguous chunk of memory */ + csize = (last_pfn - map_pfn + 1) << PAGE_SHIFT; + if (vhost_vdpa_map(v, iova, csize, + map_pfn << PAGE_SHIFT, + msg->perm)) + goto out; + map_pfn = this_pfn; + iova += csize; } - atomic64_add(csize, &dev->mm->pinned_vm); - nmap += csize; - iova += csize << PAGE_SHIFT; - map_pfn = this_pfn; + + last_pfn = this_pfn; } - last_pfn = this_pfn; + + cur_base += ret << PAGE_SHIFT; + npages -= ret; } - WARN_ON(nmap != npages); + /* Pin the rest chunk */ + ret = vhost_vdpa_map(v, iova, (last_pfn - map_pfn + 1) << PAGE_SHIFT, + map_pfn << PAGE_SHIFT, msg->perm); out: - if (ret) + if (ret) { vhost_vdpa_unmap(v, msg->iova, msg->size); -unlock: + atomic64_sub(npages, &dev->mm->pinned_vm); + } mmap_read_unlock(dev->mm); -free: - kvfree(vmas); - kvfree(page_list); + free_page((unsigned long)page_list); return ret; } @@ -783,6 +781,27 @@ static void vhost_vdpa_free_domain(struct vhost_vdpa *v) v->domain = NULL; } +static void vhost_vdpa_set_iova_range(struct vhost_vdpa *v) +{ + struct vdpa_iova_range *range = &v->range; + struct iommu_domain_geometry geo; + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + if (ops->get_iova_range) { + *range = ops->get_iova_range(vdpa); + } else if (v->domain && + !iommu_domain_get_attr(v->domain, + DOMAIN_ATTR_GEOMETRY, &geo) && + geo.force_aperture) { + range->first = geo.aperture_start; + range->last = geo.aperture_end; + } else { + range->first = 0; + range->last = ULLONG_MAX; + } +} + static int vhost_vdpa_open(struct inode *inode, struct file *filep) { struct vhost_vdpa *v; @@ -823,6 +842,8 @@ static int vhost_vdpa_open(struct inode *inode, struct file *filep) if (r) goto err_init_iotlb; + vhost_vdpa_set_iova_range(v); + filep->private_data = v; return 0; diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 02411d89cb46..e36fb1a0ecdb 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -1114,8 +1114,15 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) getmem_done: remove_conflicting_framebuffers(info->apertures, KBUILD_MODNAME, false); - if (!gen2vm) + + if (gen2vm) { + /* framebuffer is reallocated, clear screen_info to avoid misuse from kexec */ + screen_info.lfb_size = 0; + screen_info.lfb_base = 0; + screen_info.orig_video_isVGA = 0; + } else { pci_dev_put(pdev); + } kfree(info->apertures); return 0; |