diff options
Diffstat (limited to 'drivers')
548 files changed, 26235 insertions, 19649 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 1a693d3f9d51..af02a8a8ec4a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -1,5 +1,7 @@ menu "Device Drivers" +source "drivers/amba/Kconfig" + source "drivers/base/Kconfig" source "drivers/bus/Kconfig" diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 807a88a0f394..9d75ead2a1f9 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1164,7 +1164,8 @@ static bool acpi_video_device_in_dod(struct acpi_video_device *device) return true; for (i = 0; i < video->attached_count; i++) { - if (video->attached_array[i].bind_info == device) + if ((video->attached_array[i].value.int_val & 0xfff) == + (device->device_id & 0xfff)) return true; } diff --git a/drivers/amba/Kconfig b/drivers/amba/Kconfig new file mode 100644 index 000000000000..4a5c9d279059 --- /dev/null +++ b/drivers/amba/Kconfig @@ -0,0 +1,14 @@ +config ARM_AMBA + bool + +if ARM_AMBA + +config TEGRA_AHB + bool "Enable AHB driver for NVIDIA Tegra SoCs" + default y if ARCH_TEGRA + help + Adds AHB configuration functionality for NVIDIA Tegra SoCs, + which controls AHB bus master arbitration and some performance + parameters (priority, prefetch size). + +endif diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e45f83789809..49f1e6890587 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -321,6 +321,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */ { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */ { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */ + { PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */ + { PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */ @@ -492,6 +495,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731 */ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi }, + { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi }, /* Enmotus */ { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 0586f66d70fa..dd45c6a03e5d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1164,7 +1164,7 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id)); depth = min(ATA_MAX_QUEUE - 1, depth); - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth); + scsi_change_queue_depth(sdev, depth); } blk_queue_flush_queueable(q, false); @@ -1243,21 +1243,17 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev) * @ap: ATA port to which the device change the queue depth * @sdev: SCSI device to configure queue depth for * @queue_depth: new queue depth - * @reason: calling context * * libsas and libata have different approaches for associating a sdev to * its ata_port. * */ int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev, - int queue_depth, int reason) + int queue_depth) { struct ata_device *dev; unsigned long flags; - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (queue_depth < 1 || queue_depth == sdev->queue_depth) return sdev->queue_depth; @@ -1282,15 +1278,13 @@ int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev, if (sdev->queue_depth == queue_depth) return -EINVAL; - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth); - return queue_depth; + return scsi_change_queue_depth(sdev, queue_depth); } /** * ata_scsi_change_queue_depth - SCSI callback for queue depth config * @sdev: SCSI device to configure queue depth for * @queue_depth: new queue depth - * @reason: calling context * * This is libata standard hostt->change_queue_depth callback. * SCSI will call into this callback when user tries to set queue @@ -1302,12 +1296,11 @@ int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev, * RETURNS: * Newly configured queue depth. */ -int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth, - int reason) +int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) { struct ata_port *ap = ata_shost_to_port(sdev->host); - return __ata_change_queue_depth(ap, sdev, queue_depth, reason); + return __ata_change_queue_depth(ap, sdev, queue_depth); } /** @@ -3570,7 +3563,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); break; - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); else diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 07bc7e4dbd04..65071591b143 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1488,7 +1488,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) host_priv->csr_base = csr_base; irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); - if (irq < 0) { + if (!irq) { dev_err(&ofdev->dev, "invalid irq from platform\n"); goto error_exit_with_cleanup; } diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index cdf99fac139a..1db6f5ce5e89 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -1951,7 +1951,7 @@ static int nv_swncq_slave_config(struct scsi_device *sdev) ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num)); if (strncmp(model_num, "Maxtor", 6) == 0) { - ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT); + ata_scsi_change_queue_depth(sdev, 1); ata_dev_notice(dev, "Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth); } diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 7652e8dc188f..21b0bc6a9c96 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -1225,11 +1225,13 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE); if (!card->config_regs) { dev_warn(&dev->dev, "Failed to ioremap config registers\n"); + err = -ENOMEM; goto out_release_regions; } card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE); if (!card->buffers) { dev_warn(&dev->dev, "Failed to ioremap data buffers\n"); + err = -ENOMEM; goto out_unmap_config; } diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 8a3f51f7b1b9..db9d00c36a3e 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -3,12 +3,15 @@ # subsystems should select the appropriate symbols. config REGMAP - default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ) + default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) select LZO_COMPRESS select LZO_DECOMPRESS select IRQ_DOMAIN if REGMAP_IRQ bool +config REGMAP_AC97 + tristate + config REGMAP_I2C tristate depends on I2C diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index a7c670b4123a..0a533653ef3b 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o +obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index d9762e41959b..0246f44ded74 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -10,9 +10,9 @@ * published by the Free Software Foundation. */ -#include <linux/slab.h> #include <linux/device.h> #include <linux/seq_file.h> +#include <linux/slab.h> #include "internal.h" diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index e210a6d1406a..2d53f6f138e1 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -10,9 +10,9 @@ * published by the Free Software Foundation. */ -#include <linux/slab.h> #include <linux/device.h> #include <linux/lzo.h> +#include <linux/slab.h> #include "internal.h" diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index f3e8fe0cc650..d453a2c98ad0 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -10,11 +10,11 @@ * published by the Free Software Foundation. */ -#include <linux/slab.h> -#include <linux/device.h> #include <linux/debugfs.h> +#include <linux/device.h> #include <linux/rbtree.h> #include <linux/seq_file.h> +#include <linux/slab.h> #include "internal.h" diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index f1280dc356d0..f373c35f9e1d 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -10,12 +10,12 @@ * published by the Free Software Foundation. */ -#include <linux/slab.h> -#include <linux/export.h> -#include <linux/device.h> -#include <trace/events/regmap.h> #include <linux/bsearch.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/slab.h> #include <linux/sort.h> +#include <trace/events/regmap.h> #include "internal.h" @@ -36,6 +36,23 @@ static int regcache_hw_init(struct regmap *map) if (!map->num_reg_defaults_raw) return -EINVAL; + /* calculate the size of reg_defaults */ + for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) + if (!regmap_volatile(map, i * map->reg_stride)) + count++; + + /* all registers are volatile, so just bypass */ + if (!count) { + map->cache_bypass = true; + return 0; + } + + map->num_reg_defaults = count; + map->reg_defaults = kmalloc_array(count, sizeof(struct reg_default), + GFP_KERNEL); + if (!map->reg_defaults) + return -ENOMEM; + if (!map->reg_defaults_raw) { u32 cache_bypass = map->cache_bypass; dev_warn(map->dev, "No cache defaults, reading back from HW\n"); @@ -43,40 +60,25 @@ static int regcache_hw_init(struct regmap *map) /* Bypass the cache access till data read from HW*/ map->cache_bypass = 1; tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); - if (!tmp_buf) - return -EINVAL; + if (!tmp_buf) { + ret = -ENOMEM; + goto err_free; + } ret = regmap_raw_read(map, 0, tmp_buf, map->num_reg_defaults_raw); map->cache_bypass = cache_bypass; - if (ret < 0) { - kfree(tmp_buf); - return ret; - } + if (ret < 0) + goto err_cache_free; + map->reg_defaults_raw = tmp_buf; map->cache_free = 1; } - /* calculate the size of reg_defaults */ - for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { - val = regcache_get_val(map, map->reg_defaults_raw, i); - if (regmap_volatile(map, i * map->reg_stride)) - continue; - count++; - } - - map->reg_defaults = kmalloc(count * sizeof(struct reg_default), - GFP_KERNEL); - if (!map->reg_defaults) { - ret = -ENOMEM; - goto err_free; - } - /* fill the reg_defaults */ - map->num_reg_defaults = count; for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { - val = regcache_get_val(map, map->reg_defaults_raw, i); if (regmap_volatile(map, i * map->reg_stride)) continue; + val = regcache_get_val(map, map->reg_defaults_raw, i); map->reg_defaults[j].reg = i * map->reg_stride; map->reg_defaults[j].def = val; j++; @@ -84,9 +86,10 @@ static int regcache_hw_init(struct regmap *map) return 0; +err_cache_free: + kfree(tmp_buf); err_free: - if (map->cache_free) - kfree(map->reg_defaults_raw); + kfree(map->reg_defaults); return ret; } @@ -150,6 +153,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) ret = regcache_hw_init(map); if (ret < 0) return ret; + if (map->cache_bypass) + return 0; } if (!map->max_register) diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c new file mode 100644 index 000000000000..e4c45d2299c1 --- /dev/null +++ b/drivers/base/regmap/regmap-ac97.c @@ -0,0 +1,114 @@ +/* + * Register map access API - AC'97 support + * + * Copyright 2013 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <sound/ac97_codec.h> + +bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET: + case AC97_POWERDOWN: + case AC97_INT_PAGING: + case AC97_EXTENDED_ID: + case AC97_EXTENDED_STATUS: + case AC97_EXTENDED_MID: + case AC97_EXTENDED_MSTATUS: + case AC97_GPIO_STATUS: + case AC97_MISC_AFE: + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + case AC97_CODEC_CLASS_REV: + case AC97_PCI_SVID: + case AC97_PCI_SID: + case AC97_FUNC_SELECT: + case AC97_FUNC_INFO: + case AC97_SENSE_INFO: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile); + +static int regmap_ac97_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct snd_ac97 *ac97 = context; + + *val = ac97->bus->ops->read(ac97, reg); + + return 0; +} + +static int regmap_ac97_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct snd_ac97 *ac97 = context; + + ac97->bus->ops->write(ac97, reg, val); + + return 0; +} + +static const struct regmap_bus ac97_regmap_bus = { + .reg_write = regmap_ac97_reg_write, + .reg_read = regmap_ac97_reg_read, +}; + +/** + * regmap_init_ac97(): Initialise AC'97 register map + * + * @ac97: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config) +{ + return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); +} +EXPORT_SYMBOL_GPL(regmap_init_ac97); + +/** + * devm_regmap_init_ac97(): Initialise AC'97 register map + * + * @ac97: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config) +{ + return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_ac97); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c index a4cd6d691c63..0b4b2775600e 100644 --- a/drivers/block/nvme-scsi.c +++ b/drivers/block/nvme-scsi.c @@ -329,7 +329,7 @@ INQUIRY_EVPD_BIT_MASK) ? 1 : 0) (GET_U32_FROM_CDB(cdb, READ_CAP_16_CDB_ALLOC_LENGTH_OFFSET)) #define IS_READ_CAP_16(cdb) \ -((cdb[0] == SERVICE_ACTION_IN && cdb[1] == SAI_READ_CAPACITY_16) ? 1 : 0) +((cdb[0] == SERVICE_ACTION_IN_16 && cdb[1] == SAI_READ_CAPACITY_16) ? 1 : 0) /* Request Sense Helper Macros */ #define GET_REQUEST_SENSE_ALLOC_LENGTH(cdb) \ @@ -2947,7 +2947,7 @@ static int nvme_scsi_translate(struct nvme_ns *ns, struct sg_io_hdr *hdr) case READ_CAPACITY: retcode = nvme_trans_read_capacity(ns, hdr, cmd); break; - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: if (IS_READ_CAP_16(cmd)) retcode = nvme_trans_read_capacity(ns, hdr, cmd); else diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 603eb1be4f6a..b99729e36860 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -6,7 +6,7 @@ menu "Bus devices" config BRCMSTB_GISB_ARB bool "Broadcom STB GISB bus arbiter" - depends on ARM + depends on ARM || MIPS help Driver for the Broadcom Set Top Box System-on-a-chip internal bus arbiter. This driver provides timeout and target abort error handling diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index 7af78df241f2..860da40b78ef 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -16,17 +16,17 @@ #include <linux/arm-cci.h> #include <linux/io.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/perf_event.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <asm/cacheflush.h> -#include <asm/irq_regs.h> -#include <asm/pmu.h> #include <asm/smp_plat.h> #define DRIVER_NAME "CCI-400" @@ -98,6 +98,8 @@ static unsigned long cci_ctrl_phys; #define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K) +#define CCI_PMU_CNTR_MASK ((1ULL << 32) -1) + /* * Instead of an event id to monitor CCI cycles, a dedicated counter is * provided. Use 0xff to represent CCI cycles and hope that no future revisions @@ -170,18 +172,29 @@ static char *const pmu_names[] = { [CCI_REV_R1] = "CCI_400_r1", }; -struct cci_pmu_drv_data { +struct cci_pmu_hw_events { + struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; + unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; + raw_spinlock_t pmu_lock; +}; + +struct cci_pmu { void __iomem *base; - struct arm_pmu *cci_pmu; + struct pmu pmu; int nr_irqs; int irqs[CCI_PMU_MAX_HW_EVENTS]; unsigned long active_irqs; - struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; - unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; struct pmu_port_event_ranges *port_ranges; - struct pmu_hw_events hw_events; + struct cci_pmu_hw_events hw_events; + struct platform_device *plat_device; + int num_events; + atomic_t active_events; + struct mutex reserve_mutex; + cpumask_t cpus; }; -static struct cci_pmu_drv_data *pmu; +static struct cci_pmu *pmu; + +#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu)) static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) { @@ -252,7 +265,7 @@ static int pmu_validate_hw_event(u8 hw_event) return -ENOENT; } -static int pmu_is_valid_counter(struct arm_pmu *cci_pmu, int idx) +static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) { return CCI_PMU_CYCLE_CNTR_IDX <= idx && idx <= CCI_PMU_CNTR_LAST(cci_pmu); @@ -293,14 +306,9 @@ static u32 pmu_get_max_counters(void) return n_cnts + 1; } -static struct pmu_hw_events *pmu_get_hw_events(void) -{ - return &pmu->hw_events; -} - -static int pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event) +static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *event) { - struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); struct hw_perf_event *hw_event = &event->hw; unsigned long cci_event = hw_event->config_base & CCI_PMU_EVENT_MASK; int idx; @@ -336,7 +344,7 @@ static int pmu_map_event(struct perf_event *event) return mapping; } -static int pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler) +static int pmu_request_irq(struct cci_pmu *cci_pmu, irq_handler_t handler) { int i; struct platform_device *pmu_device = cci_pmu->plat_device; @@ -371,17 +379,91 @@ static int pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler) return 0; } +static void pmu_free_irq(struct cci_pmu *cci_pmu) +{ + int i; + + for (i = 0; i < pmu->nr_irqs; i++) { + if (!test_and_clear_bit(i, &pmu->active_irqs)) + continue; + + free_irq(pmu->irqs[i], cci_pmu); + } +} + +static u32 pmu_read_counter(struct perf_event *event) +{ + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + u32 value; + + if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return 0; + } + value = pmu_read_register(idx, CCI_PMU_CNTR); + + return value; +} + +static void pmu_write_counter(struct perf_event *event, u32 value) +{ + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + else + pmu_write_register(value, idx, CCI_PMU_CNTR); +} + +static u64 pmu_event_update(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 delta, prev_raw_count, new_raw_count; + + do { + prev_raw_count = local64_read(&hwc->prev_count); + new_raw_count = pmu_read_counter(event); + } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count); + + delta = (new_raw_count - prev_raw_count) & CCI_PMU_CNTR_MASK; + + local64_add(delta, &event->count); + + return new_raw_count; +} + +static void pmu_read(struct perf_event *event) +{ + pmu_event_update(event); +} + +void pmu_event_set_period(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + /* + * The CCI PMU counters have a period of 2^32. To account for the + * possiblity of extreme interrupt latency we program for a period of + * half that. Hopefully we can handle the interrupt before another 2^31 + * events occur and the counter overtakes its previous value. + */ + u64 val = 1ULL << 31; + local64_set(&hwc->prev_count, val); + pmu_write_counter(event, val); +} + static irqreturn_t pmu_handle_irq(int irq_num, void *dev) { unsigned long flags; - struct arm_pmu *cci_pmu = (struct arm_pmu *)dev; - struct pmu_hw_events *events = cci_pmu->get_hw_events(); - struct perf_sample_data data; - struct pt_regs *regs; + struct cci_pmu *cci_pmu = dev; + struct cci_pmu_hw_events *events = &pmu->hw_events; int idx, handled = IRQ_NONE; raw_spin_lock_irqsave(&events->pmu_lock, flags); - regs = get_irq_regs(); /* * Iterate over counters and update the corresponding perf events. * This should work regardless of whether we have per-counter overflow @@ -403,154 +485,407 @@ static irqreturn_t pmu_handle_irq(int irq_num, void *dev) pmu_write_register(CCI_PMU_OVRFLW_FLAG, idx, CCI_PMU_OVRFLW); + pmu_event_update(event); + pmu_event_set_period(event); handled = IRQ_HANDLED; - - armpmu_event_update(event); - perf_sample_data_init(&data, 0, hw_counter->last_period); - if (!armpmu_event_set_period(event)) - continue; - - if (perf_event_overflow(event, &data, regs)) - cci_pmu->disable(event); } raw_spin_unlock_irqrestore(&events->pmu_lock, flags); return IRQ_RETVAL(handled); } -static void pmu_free_irq(struct arm_pmu *cci_pmu) +static int cci_pmu_get_hw(struct cci_pmu *cci_pmu) { - int i; + int ret = pmu_request_irq(cci_pmu, pmu_handle_irq); + if (ret) { + pmu_free_irq(cci_pmu); + return ret; + } + return 0; +} - for (i = 0; i < pmu->nr_irqs; i++) { - if (!test_and_clear_bit(i, &pmu->active_irqs)) - continue; +static void cci_pmu_put_hw(struct cci_pmu *cci_pmu) +{ + pmu_free_irq(cci_pmu); +} - free_irq(pmu->irqs[i], cci_pmu); +static void hw_perf_event_destroy(struct perf_event *event) +{ + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + atomic_t *active_events = &cci_pmu->active_events; + struct mutex *reserve_mutex = &cci_pmu->reserve_mutex; + + if (atomic_dec_and_mutex_lock(active_events, reserve_mutex)) { + cci_pmu_put_hw(cci_pmu); + mutex_unlock(reserve_mutex); } } -static void pmu_enable_event(struct perf_event *event) +static void cci_pmu_enable(struct pmu *pmu) { + struct cci_pmu *cci_pmu = to_cci_pmu(pmu); + struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; + int enabled = bitmap_weight(hw_events->used_mask, cci_pmu->num_events); unsigned long flags; - struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); - struct pmu_hw_events *events = cci_pmu->get_hw_events(); - struct hw_perf_event *hw_counter = &event->hw; - int idx = hw_counter->idx; + u32 val; + + if (!enabled) + return; + + raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); + + /* Enable all the PMU counters. */ + val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN; + writel(val, cci_ctrl_base + CCI_PMCR); + raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); + +} + +static void cci_pmu_disable(struct pmu *pmu) +{ + struct cci_pmu *cci_pmu = to_cci_pmu(pmu); + struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); + + /* Disable all the PMU counters. */ + val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN; + writel(val, cci_ctrl_base + CCI_PMCR); + raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); +} + +static void cci_pmu_start(struct perf_event *event, int pmu_flags) +{ + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + unsigned long flags; + + /* + * To handle interrupt latency, we always reprogram the period + * regardlesss of PERF_EF_RELOAD. + */ + if (pmu_flags & PERF_EF_RELOAD) + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); + + hwc->state = 0; if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); return; } - raw_spin_lock_irqsave(&events->pmu_lock, flags); + raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); /* Configure the event to count, unless you are counting cycles */ if (idx != CCI_PMU_CYCLE_CNTR_IDX) - pmu_set_event(idx, hw_counter->config_base); + pmu_set_event(idx, hwc->config_base); + pmu_event_set_period(event); pmu_enable_counter(idx); - raw_spin_unlock_irqrestore(&events->pmu_lock, flags); + raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); } -static void pmu_disable_event(struct perf_event *event) +static void cci_pmu_stop(struct perf_event *event, int pmu_flags) { - struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); - struct hw_perf_event *hw_counter = &event->hw; - int idx = hw_counter->idx; + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + if (hwc->state & PERF_HES_STOPPED) + return; if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); return; } + /* + * We always reprogram the counter, so ignore PERF_EF_UPDATE. See + * cci_pmu_start() + */ pmu_disable_counter(idx); + pmu_event_update(event); + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; } -static void pmu_start(struct arm_pmu *cci_pmu) +static int cci_pmu_add(struct perf_event *event, int flags) { - u32 val; - unsigned long flags; - struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; + struct hw_perf_event *hwc = &event->hw; + int idx; + int err = 0; - raw_spin_lock_irqsave(&events->pmu_lock, flags); + perf_pmu_disable(event->pmu); - /* Enable all the PMU counters. */ - val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN; - writel(val, cci_ctrl_base + CCI_PMCR); + /* If we don't have a space for the counter then finish early. */ + idx = pmu_get_event_idx(hw_events, event); + if (idx < 0) { + err = idx; + goto out; + } - raw_spin_unlock_irqrestore(&events->pmu_lock, flags); + event->hw.idx = idx; + hw_events->events[idx] = event; + + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + if (flags & PERF_EF_START) + cci_pmu_start(event, PERF_EF_RELOAD); + + /* Propagate our changes to the userspace mapping. */ + perf_event_update_userpage(event); + +out: + perf_pmu_enable(event->pmu); + return err; } -static void pmu_stop(struct arm_pmu *cci_pmu) +static void cci_pmu_del(struct perf_event *event, int flags) { - u32 val; - unsigned long flags; - struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; - raw_spin_lock_irqsave(&events->pmu_lock, flags); + cci_pmu_stop(event, PERF_EF_UPDATE); + hw_events->events[idx] = NULL; + clear_bit(idx, hw_events->used_mask); - /* Disable all the PMU counters. */ - val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN; - writel(val, cci_ctrl_base + CCI_PMCR); + perf_event_update_userpage(event); +} - raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +static int +validate_event(struct cci_pmu_hw_events *hw_events, + struct perf_event *event) +{ + if (is_software_event(event)) + return 1; + + if (event->state < PERF_EVENT_STATE_OFF) + return 1; + + if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) + return 1; + + return pmu_get_event_idx(hw_events, event) >= 0; } -static u32 pmu_read_counter(struct perf_event *event) +static int +validate_group(struct perf_event *event) { - struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); - struct hw_perf_event *hw_counter = &event->hw; - int idx = hw_counter->idx; - u32 value; + struct perf_event *sibling, *leader = event->group_leader; + struct cci_pmu_hw_events fake_pmu = { + /* + * Initialise the fake PMU. We only need to populate the + * used_mask for the purposes of validation. + */ + .used_mask = CPU_BITS_NONE, + }; - if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { - dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); - return 0; + if (!validate_event(&fake_pmu, leader)) + return -EINVAL; + + list_for_each_entry(sibling, &leader->sibling_list, group_entry) { + if (!validate_event(&fake_pmu, sibling)) + return -EINVAL; } - value = pmu_read_register(idx, CCI_PMU_CNTR); - return value; + if (!validate_event(&fake_pmu, event)) + return -EINVAL; + + return 0; } -static void pmu_write_counter(struct perf_event *event, u32 value) +static int +__hw_perf_event_init(struct perf_event *event) { - struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); - struct hw_perf_event *hw_counter = &event->hw; - int idx = hw_counter->idx; + struct hw_perf_event *hwc = &event->hw; + int mapping; - if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) - dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); - else - pmu_write_register(value, idx, CCI_PMU_CNTR); + mapping = pmu_map_event(event); + + if (mapping < 0) { + pr_debug("event %x:%llx not supported\n", event->attr.type, + event->attr.config); + return mapping; + } + + /* + * We don't assign an index until we actually place the event onto + * hardware. Use -1 to signify that we haven't decided where to put it + * yet. + */ + hwc->idx = -1; + hwc->config_base = 0; + hwc->config = 0; + hwc->event_base = 0; + + /* + * Store the event encoding into the config_base field. + */ + hwc->config_base |= (unsigned long)mapping; + + /* + * Limit the sample_period to half of the counter width. That way, the + * new counter value is far less likely to overtake the previous one + * unless you have some serious IRQ latency issues. + */ + hwc->sample_period = CCI_PMU_CNTR_MASK >> 1; + hwc->last_period = hwc->sample_period; + local64_set(&hwc->period_left, hwc->sample_period); + + if (event->group_leader != event) { + if (validate_group(event) != 0) + return -EINVAL; + } + + return 0; +} + +static int cci_pmu_event_init(struct perf_event *event) +{ + struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); + atomic_t *active_events = &cci_pmu->active_events; + int err = 0; + int cpu; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + /* Shared by all CPUs, no meaningful state to sample */ + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + /* We have no filtering of any kind */ + if (event->attr.exclude_user || + event->attr.exclude_kernel || + event->attr.exclude_hv || + event->attr.exclude_idle || + event->attr.exclude_host || + event->attr.exclude_guest) + return -EINVAL; + + /* + * Following the example set by other "uncore" PMUs, we accept any CPU + * and rewrite its affinity dynamically rather than having perf core + * handle cpu == -1 and pid == -1 for this case. + * + * The perf core will pin online CPUs for the duration of this call and + * the event being installed into its context, so the PMU's CPU can't + * change under our feet. + */ + cpu = cpumask_first(&cci_pmu->cpus); + if (event->cpu < 0 || cpu < 0) + return -EINVAL; + event->cpu = cpu; + + event->destroy = hw_perf_event_destroy; + if (!atomic_inc_not_zero(active_events)) { + mutex_lock(&cci_pmu->reserve_mutex); + if (atomic_read(active_events) == 0) + err = cci_pmu_get_hw(cci_pmu); + if (!err) + atomic_inc(active_events); + mutex_unlock(&cci_pmu->reserve_mutex); + } + if (err) + return err; + + err = __hw_perf_event_init(event); + if (err) + hw_perf_event_destroy(event); + + return err; } -static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev) +static ssize_t pmu_attr_cpumask_show(struct device *dev, + struct device_attribute *attr, char *buf) { - *cci_pmu = (struct arm_pmu){ - .name = pmu_names[probe_cci_revision()], - .max_period = (1LLU << 32) - 1, - .get_hw_events = pmu_get_hw_events, - .get_event_idx = pmu_get_event_idx, - .map_event = pmu_map_event, - .request_irq = pmu_request_irq, - .handle_irq = pmu_handle_irq, - .free_irq = pmu_free_irq, - .enable = pmu_enable_event, - .disable = pmu_disable_event, - .start = pmu_start, - .stop = pmu_stop, - .read_counter = pmu_read_counter, - .write_counter = pmu_write_counter, + int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &pmu->cpus); + + buf[n++] = '\n'; + buf[n] = '\0'; + return n; +} + +static DEVICE_ATTR(cpumask, S_IRUGO, pmu_attr_cpumask_show, NULL); + +static struct attribute *pmu_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static struct attribute_group pmu_attr_group = { + .attrs = pmu_attrs, +}; + +static const struct attribute_group *pmu_attr_groups[] = { + &pmu_attr_group, + NULL +}; + +static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) +{ + char *name = pmu_names[probe_cci_revision()]; + cci_pmu->pmu = (struct pmu) { + .name = pmu_names[probe_cci_revision()], + .task_ctx_nr = perf_invalid_context, + .pmu_enable = cci_pmu_enable, + .pmu_disable = cci_pmu_disable, + .event_init = cci_pmu_event_init, + .add = cci_pmu_add, + .del = cci_pmu_del, + .start = cci_pmu_start, + .stop = cci_pmu_stop, + .read = pmu_read, + .attr_groups = pmu_attr_groups, }; cci_pmu->plat_device = pdev; cci_pmu->num_events = pmu_get_max_counters(); - return armpmu_register(cci_pmu, -1); + return perf_pmu_register(&cci_pmu->pmu, name, -1); } +static int cci_pmu_cpu_notifier(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (long)hcpu; + unsigned int target; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_DOWN_PREPARE: + if (!cpumask_test_and_clear_cpu(cpu, &pmu->cpus)) + break; + target = cpumask_any_but(cpu_online_mask, cpu); + if (target < 0) // UP, last CPU + break; + /* + * TODO: migrate context once core races on event->ctx have + * been fixed. + */ + cpumask_set_cpu(target, &pmu->cpus); + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block cci_pmu_cpu_nb = { + .notifier_call = cci_pmu_cpu_notifier, + /* + * to migrate uncore events, our notifier should be executed + * before perf core's notifier. + */ + .priority = CPU_PRI_PERF + 1, +}; + static const struct of_device_id arm_cci_pmu_matches[] = { { .compatible = "arm,cci-400-pmu", @@ -604,15 +939,16 @@ static int cci_pmu_probe(struct platform_device *pdev) return -EINVAL; } - pmu->cci_pmu = devm_kzalloc(&pdev->dev, sizeof(*(pmu->cci_pmu)), GFP_KERNEL); - if (!pmu->cci_pmu) - return -ENOMEM; - - pmu->hw_events.events = pmu->events; - pmu->hw_events.used_mask = pmu->used_mask; raw_spin_lock_init(&pmu->hw_events.pmu_lock); + mutex_init(&pmu->reserve_mutex); + atomic_set(&pmu->active_events, 0); + cpumask_set_cpu(smp_processor_id(), &pmu->cpus); + + ret = register_cpu_notifier(&cci_pmu_cpu_nb); + if (ret) + return ret; - ret = cci_pmu_init(pmu->cci_pmu, pdev); + ret = cci_pmu_init(pmu, pdev); if (ret) return ret; diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index f2cd6a2d40b4..46de8dc39eb4 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -23,35 +23,103 @@ #include <linux/list.h> #include <linux/of.h> #include <linux/bitops.h> +#include <linux/pm.h> +#ifdef CONFIG_ARM #include <asm/bug.h> #include <asm/signal.h> +#endif -#define ARB_TIMER 0x008 -#define ARB_ERR_CAP_CLR 0x7e4 #define ARB_ERR_CAP_CLEAR (1 << 0) -#define ARB_ERR_CAP_HI_ADDR 0x7e8 -#define ARB_ERR_CAP_ADDR 0x7ec -#define ARB_ERR_CAP_DATA 0x7f0 -#define ARB_ERR_CAP_STATUS 0x7f4 #define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12) #define ARB_ERR_CAP_STATUS_TEA (1 << 11) #define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2) #define ARB_ERR_CAP_STATUS_BS_MASK 0x3c #define ARB_ERR_CAP_STATUS_WRITE (1 << 1) #define ARB_ERR_CAP_STATUS_VALID (1 << 0) -#define ARB_ERR_CAP_MASTER 0x7f8 + +enum { + ARB_TIMER, + ARB_ERR_CAP_CLR, + ARB_ERR_CAP_HI_ADDR, + ARB_ERR_CAP_ADDR, + ARB_ERR_CAP_DATA, + ARB_ERR_CAP_STATUS, + ARB_ERR_CAP_MASTER, +}; + +static const int gisb_offsets_bcm7038[] = { + [ARB_TIMER] = 0x00c, + [ARB_ERR_CAP_CLR] = 0x0c4, + [ARB_ERR_CAP_HI_ADDR] = -1, + [ARB_ERR_CAP_ADDR] = 0x0c8, + [ARB_ERR_CAP_DATA] = 0x0cc, + [ARB_ERR_CAP_STATUS] = 0x0d0, + [ARB_ERR_CAP_MASTER] = -1, +}; + +static const int gisb_offsets_bcm7400[] = { + [ARB_TIMER] = 0x00c, + [ARB_ERR_CAP_CLR] = 0x0c8, + [ARB_ERR_CAP_HI_ADDR] = -1, + [ARB_ERR_CAP_ADDR] = 0x0cc, + [ARB_ERR_CAP_DATA] = 0x0d0, + [ARB_ERR_CAP_STATUS] = 0x0d4, + [ARB_ERR_CAP_MASTER] = 0x0d8, +}; + +static const int gisb_offsets_bcm7435[] = { + [ARB_TIMER] = 0x00c, + [ARB_ERR_CAP_CLR] = 0x168, + [ARB_ERR_CAP_HI_ADDR] = -1, + [ARB_ERR_CAP_ADDR] = 0x16c, + [ARB_ERR_CAP_DATA] = 0x170, + [ARB_ERR_CAP_STATUS] = 0x174, + [ARB_ERR_CAP_MASTER] = 0x178, +}; + +static const int gisb_offsets_bcm7445[] = { + [ARB_TIMER] = 0x008, + [ARB_ERR_CAP_CLR] = 0x7e4, + [ARB_ERR_CAP_HI_ADDR] = 0x7e8, + [ARB_ERR_CAP_ADDR] = 0x7ec, + [ARB_ERR_CAP_DATA] = 0x7f0, + [ARB_ERR_CAP_STATUS] = 0x7f4, + [ARB_ERR_CAP_MASTER] = 0x7f8, +}; struct brcmstb_gisb_arb_device { void __iomem *base; + const int *gisb_offsets; struct mutex lock; struct list_head next; u32 valid_mask; const char *master_names[sizeof(u32) * BITS_PER_BYTE]; + u32 saved_timeout; }; static LIST_HEAD(brcmstb_gisb_arb_device_list); +static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg) +{ + int offset = gdev->gisb_offsets[reg]; + + /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */ + if (offset == -1) + return 1; + + return ioread32(gdev->base + offset); +} + +static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg) +{ + int offset = gdev->gisb_offsets[reg]; + + if (offset == -1) + return; + iowrite32(val, gdev->base + reg); +} + static ssize_t gisb_arb_get_timeout(struct device *dev, struct device_attribute *attr, char *buf) @@ -61,7 +129,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev, u32 timeout; mutex_lock(&gdev->lock); - timeout = ioread32(gdev->base + ARB_TIMER); + timeout = gisb_read(gdev, ARB_TIMER); mutex_unlock(&gdev->lock); return sprintf(buf, "%d", timeout); @@ -83,7 +151,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev, return -EINVAL; mutex_lock(&gdev->lock); - iowrite32(val, gdev->base + ARB_TIMER); + gisb_write(gdev, val, ARB_TIMER); mutex_unlock(&gdev->lock); return count; @@ -110,18 +178,18 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, const char *m_name; char m_fmt[11]; - cap_status = ioread32(gdev->base + ARB_ERR_CAP_STATUS); + cap_status = gisb_read(gdev, ARB_ERR_CAP_STATUS); /* Invalid captured address, bail out */ if (!(cap_status & ARB_ERR_CAP_STATUS_VALID)) return 1; /* Read the address and master */ - arb_addr = ioread32(gdev->base + ARB_ERR_CAP_ADDR) & 0xffffffff; + arb_addr = gisb_read(gdev, ARB_ERR_CAP_ADDR) & 0xffffffff; #if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) - arb_addr |= (u64)ioread32(gdev->base + ARB_ERR_CAP_HI_ADDR) << 32; + arb_addr |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32; #endif - master = ioread32(gdev->base + ARB_ERR_CAP_MASTER); + master = gisb_read(gdev, ARB_ERR_CAP_MASTER); m_name = brcmstb_gisb_master_to_str(gdev, master); if (!m_name) { @@ -136,11 +204,12 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, m_name); /* clear the GISB error */ - iowrite32(ARB_ERR_CAP_CLEAR, gdev->base + ARB_ERR_CAP_CLR); + gisb_write(gdev, ARB_ERR_CAP_CLEAR, ARB_ERR_CAP_CLR); return 0; } +#ifdef CONFIG_ARM static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { @@ -159,12 +228,7 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, return ret; } - -void __init brcmstb_hook_fault_code(void) -{ - hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, - "imprecise external abort"); -} +#endif static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) { @@ -192,10 +256,20 @@ static struct attribute_group gisb_arb_sysfs_attr_group = { .attrs = gisb_arb_sysfs_attrs, }; -static int brcmstb_gisb_arb_probe(struct platform_device *pdev) +static const struct of_device_id brcmstb_gisb_arb_of_match[] = { + { .compatible = "brcm,gisb-arb", .data = gisb_offsets_bcm7445 }, + { .compatible = "brcm,bcm7445-gisb-arb", .data = gisb_offsets_bcm7445 }, + { .compatible = "brcm,bcm7435-gisb-arb", .data = gisb_offsets_bcm7435 }, + { .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 }, + { .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 }, + { }, +}; + +static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; struct brcmstb_gisb_arb_device *gdev; + const struct of_device_id *of_id; struct resource *r; int err, timeout_irq, tea_irq; unsigned int num_masters, j = 0; @@ -216,6 +290,13 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) if (IS_ERR(gdev->base)) return PTR_ERR(gdev->base); + of_id = of_match_node(brcmstb_gisb_arb_of_match, dn); + if (!of_id) { + pr_err("failed to look up compatible string\n"); + return -EINVAL; + } + gdev->gisb_offsets = of_id->data; + err = devm_request_irq(&pdev->dev, timeout_irq, brcmstb_gisb_timeout_handler, 0, pdev->name, gdev); @@ -261,29 +342,63 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); +#ifdef CONFIG_ARM + hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, + "imprecise external abort"); +#endif + dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", gdev->base, timeout_irq, tea_irq); return 0; } -static const struct of_device_id brcmstb_gisb_arb_of_match[] = { - { .compatible = "brcm,gisb-arb" }, - { }, +#ifdef CONFIG_PM_SLEEP +static int brcmstb_gisb_arb_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + + gdev->saved_timeout = gisb_read(gdev, ARB_TIMER); + + return 0; +} + +/* Make sure we provide the same timeout value that was configured before, and + * do this before the GISB timeout interrupt handler has any chance to run. + */ +static int brcmstb_gisb_arb_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + + gisb_write(gdev, gdev->saved_timeout, ARB_TIMER); + + return 0; +} +#else +#define brcmstb_gisb_arb_suspend NULL +#define brcmstb_gisb_arb_resume_noirq NULL +#endif + +static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = { + .suspend = brcmstb_gisb_arb_suspend, + .resume_noirq = brcmstb_gisb_arb_resume_noirq, }; static struct platform_driver brcmstb_gisb_arb_driver = { - .probe = brcmstb_gisb_arb_probe, .driver = { .name = "brcm-gisb-arb", .owner = THIS_MODULE, .of_match_table = brcmstb_gisb_arb_of_match, + .pm = &brcmstb_gisb_arb_pm_ops, }, }; static int __init brcm_gisb_driver_init(void) { - return platform_driver_register(&brcmstb_gisb_arb_driver); + return platform_driver_probe(&brcmstb_gisb_arb_driver, + brcmstb_gisb_arb_probe); } module_init(brcm_gisb_driver_init); diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 26c3779d871d..eb7682dc123b 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -57,6 +57,7 @@ #include <linux/of_address.h> #include <linux/debugfs.h> #include <linux/log2.h> +#include <linux/syscore_ops.h> /* * DDR target is the same on all platforms. @@ -94,20 +95,42 @@ #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) +/* Relative to mbusbridge_base */ +#define MBUS_BRIDGE_CTRL_OFF 0x0 +#define MBUS_BRIDGE_BASE_OFF 0x4 + +/* Maximum number of windows, for all known platforms */ +#define MBUS_WINS_MAX 20 + struct mvebu_mbus_state; struct mvebu_mbus_soc_data { unsigned int num_wins; unsigned int num_remappable_wins; + bool has_mbus_bridge; unsigned int (*win_cfg_offset)(const int win); void (*setup_cpu_target)(struct mvebu_mbus_state *s); + int (*save_cpu_target)(struct mvebu_mbus_state *s, + u32 *store_addr); int (*show_cpu_target)(struct mvebu_mbus_state *s, struct seq_file *seq, void *v); }; +/* + * Used to store the state of one MBus window accross suspend/resume. + */ +struct mvebu_mbus_win_data { + u32 ctrl; + u32 base; + u32 remap_lo; + u32 remap_hi; +}; + struct mvebu_mbus_state { void __iomem *mbuswins_base; void __iomem *sdramwins_base; + void __iomem *mbusbridge_base; + phys_addr_t sdramwins_phys_base; struct dentry *debugfs_root; struct dentry *debugfs_sdram; struct dentry *debugfs_devs; @@ -115,6 +138,11 @@ struct mvebu_mbus_state { struct resource pcie_io_aperture; const struct mvebu_mbus_soc_data *soc; int hw_io_coherency; + + /* Used during suspend/resume */ + u32 mbus_bridge_ctrl; + u32 mbus_bridge_base; + struct mvebu_mbus_win_data wins[MBUS_WINS_MAX]; }; static struct mvebu_mbus_state mbus_state; @@ -516,6 +544,28 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) mvebu_mbus_dram_info.num_cs = cs; } +static int +mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, + u32 *store_addr) +{ + int i; + + for (i = 0; i < 4; i++) { + u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); + u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); + + writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i), + store_addr++); + writel(base, store_addr++); + writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i), + store_addr++); + writel(size, store_addr++); + } + + /* We've written 16 words to the store address */ + return 16; +} + static void __init mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) { @@ -546,10 +596,35 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) mvebu_mbus_dram_info.num_cs = cs; } +static int +mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, + u32 *store_addr) +{ + int i; + + for (i = 0; i < 2; i++) { + u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); + + writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i), + store_addr++); + writel(map, store_addr++); + } + + /* We've written 4 words to the store address */ + return 4; +} + +int mvebu_mbus_save_cpu_target(u32 *store_addr) +{ + return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); +} + static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = { .num_wins = 20, .num_remappable_wins = 8, + .has_mbus_bridge = true, .win_cfg_offset = armada_370_xp_mbus_win_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -558,6 +633,7 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = { .num_wins = 8, .num_remappable_wins = 4, .win_cfg_offset = orion_mbus_win_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -566,6 +642,7 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = { .num_wins = 8, .num_remappable_wins = 4, .win_cfg_offset = orion_mbus_win_offset, + .save_cpu_target = mvebu_mbus_dove_save_cpu_target, .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_dove, }; @@ -578,6 +655,7 @@ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = { .num_wins = 8, .num_remappable_wins = 4, .win_cfg_offset = orion_mbus_win_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -586,6 +664,7 @@ static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = { .num_wins = 8, .num_remappable_wins = 2, .win_cfg_offset = orion_mbus_win_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -594,6 +673,7 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = { .num_wins = 14, .num_remappable_wins = 8, .win_cfg_offset = mv78xx0_mbus_win_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -698,11 +778,73 @@ static __init int mvebu_mbus_debugfs_init(void) } fs_initcall(mvebu_mbus_debugfs_init); +static int mvebu_mbus_suspend(void) +{ + struct mvebu_mbus_state *s = &mbus_state; + int win; + + if (!s->mbusbridge_base) + return -ENODEV; + + for (win = 0; win < s->soc->num_wins; win++) { + void __iomem *addr = s->mbuswins_base + + s->soc->win_cfg_offset(win); + + s->wins[win].base = readl(addr + WIN_BASE_OFF); + s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF); + + if (win >= s->soc->num_remappable_wins) + continue; + + s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF); + s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF); + } + + s->mbus_bridge_ctrl = readl(s->mbusbridge_base + + MBUS_BRIDGE_CTRL_OFF); + s->mbus_bridge_base = readl(s->mbusbridge_base + + MBUS_BRIDGE_BASE_OFF); + + return 0; +} + +static void mvebu_mbus_resume(void) +{ + struct mvebu_mbus_state *s = &mbus_state; + int win; + + writel(s->mbus_bridge_ctrl, + s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF); + writel(s->mbus_bridge_base, + s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF); + + for (win = 0; win < s->soc->num_wins; win++) { + void __iomem *addr = s->mbuswins_base + + s->soc->win_cfg_offset(win); + + writel(s->wins[win].base, addr + WIN_BASE_OFF); + writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF); + + if (win >= s->soc->num_remappable_wins) + continue; + + writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF); + writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF); + } +} + +struct syscore_ops mvebu_mbus_syscore_ops = { + .suspend = mvebu_mbus_suspend, + .resume = mvebu_mbus_resume, +}; + static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, phys_addr_t mbuswins_phys_base, size_t mbuswins_size, phys_addr_t sdramwins_phys_base, - size_t sdramwins_size) + size_t sdramwins_size, + phys_addr_t mbusbridge_phys_base, + size_t mbusbridge_size) { int win; @@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, return -ENOMEM; } + mbus->sdramwins_phys_base = sdramwins_phys_base; + + if (mbusbridge_phys_base) { + mbus->mbusbridge_base = ioremap(mbusbridge_phys_base, + mbusbridge_size); + if (!mbus->mbusbridge_base) { + iounmap(mbus->sdramwins_base); + iounmap(mbus->mbuswins_base); + return -ENOMEM; + } + } else + mbus->mbusbridge_base = NULL; + for (win = 0; win < mbus->soc->num_wins; win++) mvebu_mbus_disable_window(mbus, win); mbus->soc->setup_cpu_target(mbus); + register_syscore_ops(&mvebu_mbus_syscore_ops); + return 0; } @@ -746,7 +903,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, mbuswins_phys_base, mbuswins_size, sdramwins_phys_base, - sdramwins_size); + sdramwins_size, 0, 0); } #ifdef CONFIG_OF @@ -887,7 +1044,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, int __init mvebu_mbus_dt_init(bool is_coherent) { - struct resource mbuswins_res, sdramwins_res; + struct resource mbuswins_res, sdramwins_res, mbusbridge_res; struct device_node *np, *controller; const struct of_device_id *of_id; const __be32 *prop; @@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent) return -EINVAL; } + /* + * Set the resource to 0 so that it can be left unmapped by + * mvebu_mbus_common_init() if the DT doesn't carry the + * necessary information. This is needed to preserve backward + * compatibility. + */ + memset(&mbusbridge_res, 0, sizeof(mbusbridge_res)); + + if (mbus_state.soc->has_mbus_bridge) { + if (of_address_to_resource(controller, 2, &mbusbridge_res)) + pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n"); + } + mbus_state.hw_io_coherency = is_coherent; /* Get optional pcie-{mem,io}-aperture properties */ @@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent) mbuswins_res.start, resource_size(&mbuswins_res), sdramwins_res.start, - resource_size(&sdramwins_res)); + resource_size(&sdramwins_res), + mbusbridge_res.start, + resource_size(&mbusbridge_res)); if (ret) return ret; diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 531ae591783b..17d86595951c 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -222,10 +222,14 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) } /* Error found so break the for loop */ - break; + return IRQ_HANDLED; } } - return IRQ_HANDLED; + + dev_err(l3->dev, "L3 %s IRQ not handled!!\n", + inttype ? "debug" : "application"); + + return IRQ_NONE; } static const struct of_device_id l3_noc_match[] = { @@ -296,11 +300,66 @@ static int omap_l3_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM + +/** + * l3_resume_noirq() - resume function for l3_noc + * @dev: pointer to l3_noc device structure + * + * We only have the resume handler only since we + * have already maintained the delta register + * configuration as part of configuring the system + */ +static int l3_resume_noirq(struct device *dev) +{ + struct omap_l3 *l3 = dev_get_drvdata(dev); + int i; + struct l3_flagmux_data *flag_mux; + void __iomem *base, *mask_regx = NULL; + u32 mask_val; + + for (i = 0; i < l3->num_modules; i++) { + base = l3->l3_base[i]; + flag_mux = l3->l3_flagmux[i]; + if (!flag_mux->mask_app_bits && !flag_mux->mask_dbg_bits) + continue; + + mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + + (L3_APPLICATION_ERROR << 3); + mask_val = readl_relaxed(mask_regx); + mask_val &= ~(flag_mux->mask_app_bits); + + writel_relaxed(mask_val, mask_regx); + mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + + (L3_DEBUG_ERROR << 3); + mask_val = readl_relaxed(mask_regx); + mask_val &= ~(flag_mux->mask_dbg_bits); + + writel_relaxed(mask_val, mask_regx); + } + + /* Dummy read to force OCP barrier */ + if (mask_regx) + (void)readl(mask_regx); + + return 0; +} + +static const struct dev_pm_ops l3_dev_pm_ops = { + .resume_noirq = l3_resume_noirq, +}; + +#define L3_DEV_PM_OPS (&l3_dev_pm_ops) +#else +#define L3_DEV_PM_OPS NULL +#endif + static struct platform_driver omap_l3_driver = { .probe = omap_l3_probe, .driver = { .name = "omap_l3_noc", .owner = THIS_MODULE, + .pm = L3_DEV_PM_OPS, .of_match_table = of_match_ptr(l3_noc_match), }, }; diff --git a/drivers/bus/omap_l3_smx.c b/drivers/bus/omap_l3_smx.c index acc216491b8a..597fdaee7315 100644 --- a/drivers/bus/omap_l3_smx.c +++ b/drivers/bus/omap_l3_smx.c @@ -27,6 +27,10 @@ #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> + #include "omap_l3_smx.h" static inline u64 omap3_l3_readll(void __iomem *base, u16 reg) @@ -211,7 +215,17 @@ static irqreturn_t omap3_l3_app_irq(int irq, void *_l3) return ret; } -static int __init omap3_l3_probe(struct platform_device *pdev) +#if IS_BUILTIN(CONFIG_OF) +static const struct of_device_id omap3_l3_match[] = { + { + .compatible = "ti,omap3-l3-smx", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap3_l3_match); +#endif + +static int omap3_l3_probe(struct platform_device *pdev) { struct omap3_l3 *l3; struct resource *res; @@ -265,7 +279,7 @@ err0: return ret; } -static int __exit omap3_l3_remove(struct platform_device *pdev) +static int omap3_l3_remove(struct platform_device *pdev) { struct omap3_l3 *l3 = platform_get_drvdata(pdev); @@ -278,15 +292,17 @@ static int __exit omap3_l3_remove(struct platform_device *pdev) } static struct platform_driver omap3_l3_driver = { - .remove = __exit_p(omap3_l3_remove), + .probe = omap3_l3_probe, + .remove = omap3_l3_remove, .driver = { - .name = "omap_l3_smx", + .name = "omap_l3_smx", + .of_match_table = of_match_ptr(omap3_l3_match), }, }; static int __init omap3_l3_init(void) { - return platform_driver_probe(&omap3_l3_driver, omap3_l3_probe); + return platform_driver_register(&omap3_l3_driver); } postcore_initcall_sync(omap3_l3_init); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 524b707894ef..4c58333b4257 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -84,9 +84,12 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) } #endif -void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) +#ifndef unxlate_dev_mem_ptr +#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr +void __weak unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) { } +#endif /* * This funcion reads the *physical* memory. The f_pos points directly to the @@ -97,7 +100,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, { phys_addr_t p = *ppos; ssize_t read, sz; - char *ptr; + void *ptr; if (p != *ppos) return 0; @@ -400,7 +403,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf, * uncached, then it must also be accessed uncached * by the kernel or data corruption may occur */ - kbuf = xlate_dev_kmem_ptr((char *)p); + kbuf = xlate_dev_kmem_ptr((void *)p); if (copy_to_user(buf, kbuf, sz)) return -EFAULT; @@ -461,7 +464,7 @@ static ssize_t do_write_kmem(unsigned long p, const char __user *buf, #endif while (count > 0) { - char *ptr; + void *ptr; sz = size_inside_page(p, count); @@ -470,7 +473,7 @@ static ssize_t do_write_kmem(unsigned long p, const char __user *buf, * it must also be accessed uncached by the kernel or data * corruption may occur. */ - ptr = xlate_dev_kmem_ptr((char *)p); + ptr = xlate_dev_kmem_ptr((void *)p); copied = copy_from_user(ptr, buf, sz); if (copied) { diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 24b5b020753a..a23ac0c724f0 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -52,29 +52,26 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, tmp = pmc_read(pmc, AT91_PMC_USB); usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; - return parent_rate / (usbdiv + 1); + + return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); } static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long div; - unsigned long bestrate; - unsigned long tmp; + + if (!rate) + return -EINVAL; if (rate >= *parent_rate) return *parent_rate; - div = *parent_rate / rate; - if (div >= SAM9X5_USB_MAX_DIV) - return *parent_rate / (SAM9X5_USB_MAX_DIV + 1); - - bestrate = *parent_rate / div; - tmp = *parent_rate / (div + 1); - if (bestrate - rate > rate - tmp) - bestrate = tmp; + div = DIV_ROUND_CLOSEST(*parent_rate, rate); + if (div > SAM9X5_USB_MAX_DIV + 1) + div = SAM9X5_USB_MAX_DIV + 1; - return bestrate; + return DIV_ROUND_CLOSEST(*parent_rate, div); } static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) @@ -106,9 +103,13 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, u32 tmp; struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); struct at91_pmc *pmc = usb->pmc; - unsigned long div = parent_rate / rate; + unsigned long div; + + if (!rate) + return -EINVAL; - if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV) + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > SAM9X5_USB_MAX_DIV + 1 || !div) return -EINVAL; tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV; @@ -253,7 +254,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, tmp_parent_rate = rate * usb->divisors[i]; tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate); - tmprate = tmp_parent_rate / usb->divisors[i]; + tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]); if (tmprate < rate) tmpdiff = rate - tmprate; else @@ -281,10 +282,10 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, struct at91_pmc *pmc = usb->pmc; unsigned long div; - if (!rate || parent_rate % rate) + if (!rate) return -EINVAL; - div = parent_rate / rate; + div = DIV_ROUND_CLOSEST(parent_rate, rate); for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 18a9de29df0e..c0a842b335c5 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -263,6 +263,14 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!rate) rate = 1; + /* if read only, just return current value */ + if (divider->flags & CLK_DIVIDER_READ_ONLY) { + bestdiv = readl(divider->reg) >> divider->shift; + bestdiv &= div_mask(divider); + bestdiv = _get_div(divider, bestdiv); + return bestdiv; + } + maxdiv = _get_maxdiv(divider); if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { @@ -361,11 +369,6 @@ const struct clk_ops clk_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ops); -const struct clk_ops clk_divider_ro_ops = { - .recalc_rate = clk_divider_recalc_rate, -}; -EXPORT_SYMBOL_GPL(clk_divider_ro_ops); - static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, @@ -391,10 +394,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, } init.name = name; - if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) - init.ops = &clk_divider_ro_ops; - else - init.ops = &clk_divider_ops; + init.ops = &clk_divider_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index b7797fb12e12..7bb13af8e214 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -23,6 +23,7 @@ #include <linux/clk-provider.h> #include <linux/platform_device.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s5m8767.h> #include <linux/mfd/samsung/core.h> @@ -120,6 +121,24 @@ static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = { }, }; +static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = { + [S2MPS11_CLK_AP] = { + .name = "s2mps13_ap", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_CP] = { + .name = "s2mps13_cp", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_BT] = { + .name = "s2mps13_bt", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = { [S2MPS11_CLK_AP] = { .name = "s2mps14_ap", @@ -184,6 +203,10 @@ static int s2mps11_clk_probe(struct platform_device *pdev) s2mps11_reg = S2MPS11_REG_RTC_CTRL; clks_init = s2mps11_clks_init; break; + case S2MPS13X: + s2mps11_reg = S2MPS13_REG_RTCCTRL; + clks_init = s2mps13_clks_init; + break; case S2MPS14X: s2mps11_reg = S2MPS14_REG_RTCCTRL; clks_init = s2mps14_clks_init; @@ -279,6 +302,7 @@ static int s2mps11_clk_remove(struct platform_device *pdev) static const struct platform_device_id s2mps11_clk_id[] = { { "s2mps11-clk", S2MPS11X}, + { "s2mps13-clk", S2MPS13X}, { "s2mps14-clk", S2MPS14X}, { "s5m8767-clk", S5M8767X}, { }, diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index b7fcb469c87a..0d4d1216f2dd 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/syscore_ops.h> #include "common.h" @@ -177,14 +178,17 @@ struct clk_gating_ctrl { spinlock_t *lock; struct clk **gates; int num_gates; + void __iomem *base; + u32 saved_reg; }; #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) +static struct clk_gating_ctrl *ctrl; + static struct clk *clk_gating_get_src( struct of_phandle_args *clkspec, void *data) { - struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data; int n; if (clkspec->args_count < 1) @@ -199,15 +203,35 @@ static struct clk *clk_gating_get_src( return ERR_PTR(-ENODEV); } +static int mvebu_clk_gating_suspend(void) +{ + ctrl->saved_reg = readl(ctrl->base); + return 0; +} + +static void mvebu_clk_gating_resume(void) +{ + writel(ctrl->saved_reg, ctrl->base); +} + +static struct syscore_ops clk_gate_syscore_ops = { + .suspend = mvebu_clk_gating_suspend, + .resume = mvebu_clk_gating_resume, +}; + void __init mvebu_clk_gating_setup(struct device_node *np, const struct clk_gating_soc_desc *desc) { - struct clk_gating_ctrl *ctrl; struct clk *clk; void __iomem *base; const char *default_parent = NULL; int n; + if (ctrl) { + pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n"); + return; + } + base = of_iomap(np, 0); if (WARN_ON(!base)) return; @@ -225,6 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, /* lock must already be initialized */ ctrl->lock = &ctrl_gating_lock; + ctrl->base = base; + /* Count, allocate, and register clock gates */ for (n = 0; desc[n].name;) n++; @@ -246,6 +272,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, of_clk_add_provider(np, clk_gating_get_src, ctrl); + register_syscore_ops(&clk_gate_syscore_ops); + return; gates_out: kfree(ctrl); diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c index b345cc791e5d..88b9fe13fa44 100644 --- a/drivers/clk/pxa/clk-pxa27x.c +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -322,7 +322,7 @@ static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw, unsigned long ccsr = CCSR; osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); - a = cccr & CCCR_A_BIT; + a = cccr & (1 << CCCR_A_BIT); l = ccsr & CCSR_L_MASK; if (osc_forced || a) @@ -341,7 +341,7 @@ static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw) unsigned long ccsr = CCSR; osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); - a = cccr & CCCR_A_BIT; + a = cccr & (1 << CCCR_A_BIT); if (osc_forced) return PXA_MEM_13Mhz; if (a) diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index dab988ab8cf1..157139a5c1ca 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -3122,7 +3122,7 @@ static struct clk_regmap *mmcc_apq8084_clocks[] = { [ESC1_CLK_SRC] = &esc1_clk_src.clkr, [HDMI_CLK_SRC] = &hdmi_clk_src.clkr, [VSYNC_CLK_SRC] = &vsync_clk_src.clkr, - [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, + [MMSS_RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr, [MAPLE_CLK_SRC] = &maple_clk_src.clkr, [VDP_CLK_SRC] = &vdp_clk_src.clkr, diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 1e68bff481b8..880a266f0143 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -90,9 +90,7 @@ static struct clk *rockchip_clk_register_branch(const char *name, div->width = div_width; div->lock = lock; div->table = div_table; - div_ops = (div_flags & CLK_DIVIDER_READ_ONLY) - ? &clk_divider_ro_ops - : &clk_divider_ops; + div_ops = &clk_divider_ops; } clk = clk_register_composite(NULL, name, parent_names, num_parents, diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c index 00d1d00a41de..979e81389cdd 100644 --- a/drivers/clk/samsung/clk-exynos5440.c +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -15,6 +15,8 @@ #include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/notifier.h> +#include <linux/reboot.h> #include "clk.h" #include "clk-pll.h" @@ -23,6 +25,8 @@ #define CPU_CLK_STATUS 0xfc #define MISC_DOUT1 0x558 +static void __iomem *reg_base; + /* parent clock name list */ PNAME(mout_armclk_p) = { "cplla", "cpllb" }; PNAME(mout_spi_p) = { "div125", "div200" }; @@ -89,10 +93,30 @@ static const struct of_device_id ext_clk_match[] __initconst = { {}, }; +static int exynos5440_clk_restart_notify(struct notifier_block *this, + unsigned long code, void *unused) +{ + u32 val, status; + + status = readl_relaxed(reg_base + 0xbc); + val = readl_relaxed(reg_base + 0xcc); + val = (val & 0xffff0000) | (status & 0xffff); + writel_relaxed(val, reg_base + 0xcc); + + return NOTIFY_DONE; +} + +/* + * Exynos5440 Clock restart notifier, handles restart functionality + */ +static struct notifier_block exynos5440_clk_restart_handler = { + .notifier_call = exynos5440_clk_restart_notify, + .priority = 128, +}; + /* register exynos5440 clocks */ static void __init exynos5440_clk_init(struct device_node *np) { - void __iomem *reg_base; struct samsung_clk_provider *ctx; reg_base = of_iomap(np, 0); @@ -125,6 +149,9 @@ static void __init exynos5440_clk_init(struct device_node *np) samsung_clk_of_add_provider(np, ctx); + if (register_restart_handler(&exynos5440_clk_restart_handler)) + pr_warn("exynos5440 clock can't register restart handler\n"); + pr_info("Exynos5440: arm_clk = %ldHz\n", _get_rate("arm_clk")); pr_info("exynos5440 clock initialization complete\n"); } diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 290f9c1a3749..59a5714dfe18 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -185,3 +185,16 @@ struct clk *tegra_clk_register_divider(const char *name, return clk; } + +static const struct clk_div_table mc_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 1 }, + { .val = 0, .div = 0 }, +}; + +struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, + void __iomem *reg, spinlock_t *lock) +{ + return clk_register_divider_table(NULL, name, parent_name, 0, reg, + 16, 1, 0, mc_div_table, lock); +} diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index f760f31d05c4..0b03d2cf7264 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -173,6 +173,7 @@ static DEFINE_SPINLOCK(pll_d_lock); static DEFINE_SPINLOCK(pll_d2_lock); static DEFINE_SPINLOCK(pll_u_lock); static DEFINE_SPINLOCK(pll_re_lock); +static DEFINE_SPINLOCK(emc_lock); static struct div_nmp pllxc_nmp = { .divm_shift = 0, @@ -1228,7 +1229,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, ARRAY_SIZE(mux_pllmcp_clkm), CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, - 29, 3, 0, NULL); + 29, 3, 0, &emc_lock); + + clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + &emc_lock); + clks[TEGRA114_CLK_MC] = clk; for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { data = &tegra_periph_clk_list[i]; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index e3a85842ce0c..f5f9baca7bb6 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -132,6 +132,7 @@ static DEFINE_SPINLOCK(pll_d2_lock); static DEFINE_SPINLOCK(pll_e_lock); static DEFINE_SPINLOCK(pll_re_lock); static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(emc_lock); /* possible OSC frequencies in Hz */ static unsigned long tegra124_input_freq[] = { @@ -1127,7 +1128,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, ARRAY_SIZE(mux_pllmcp_clkm), 0, clk_base + CLK_SOURCE_EMC, - 29, 3, 0, NULL); + 29, 3, 0, &emc_lock); + + clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + &emc_lock); + clks[TEGRA124_CLK_MC] = clk; /* cml0 */ clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index dace2b1b5ae6..41272dcc9e22 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -140,6 +140,8 @@ static struct cpu_clk_suspend_context { static void __iomem *clk_base; static void __iomem *pmc_base; +static DEFINE_SPINLOCK(emc_lock); + #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ _clk_num, _gate_flags, _clk_id) \ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \ @@ -819,11 +821,15 @@ static void __init tegra20_periph_clk_init(void) ARRAY_SIZE(mux_pllmcp_clkm), CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, - 30, 2, 0, NULL); + 30, 2, 0, &emc_lock); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, 57, periph_clk_enb_refcnt); clks[TEGRA20_CLK_EMC] = clk; + clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + &emc_lock); + clks[TEGRA20_CLK_MC] = clk; + /* dsi */ clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, 48, periph_clk_enb_refcnt); diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 5bbacd01094f..4b9d8bd3d0bf 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -177,6 +177,7 @@ static unsigned long input_freq; static DEFINE_SPINLOCK(cml_lock); static DEFINE_SPINLOCK(pll_d_lock); +static DEFINE_SPINLOCK(emc_lock); #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ _clk_num, _gate_flags, _clk_id) \ @@ -1157,11 +1158,15 @@ static void __init tegra30_periph_clk_init(void) ARRAY_SIZE(mux_pllmcp_clkm), CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, - 30, 2, 0, NULL); + 30, 2, 0, &emc_lock); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, 57, periph_clk_enb_refcnt); clks[TEGRA30_CLK_EMC] = clk; + clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + &emc_lock); + clks[TEGRA30_CLK_MC] = clk; + /* cml0 */ clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, 0, 0, &cml_lock); diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 16ec8d6bb87f..4e458aa8d45c 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -86,6 +86,8 @@ struct clk *tegra_clk_register_divider(const char *name, const char *parent_name, void __iomem *reg, unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, u8 frac_width, spinlock_t *lock); +struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, + void __iomem *reg, spinlock_t *lock); /* * Tegra PLL: diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 79791e1bf282..85ac0dd501de 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -33,6 +33,9 @@ static const struct clk_ops dpll_m4xen_ck_ops = { .recalc_rate = &omap4_dpll_regm4xen_recalc, .round_rate = &omap4_dpll_regm4xen_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap4_dpll_regm4xen_determine_rate, .get_parent = &omap2_init_dpll_parent, }; #else @@ -53,6 +56,9 @@ static const struct clk_ops dpll_ck_ops = { .recalc_rate = &omap3_dpll_recalc, .round_rate = &omap2_dpll_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, .get_parent = &omap2_init_dpll_parent, }; @@ -61,6 +67,9 @@ static const struct clk_ops dpll_no_gate_ck_ops = { .get_parent = &omap2_init_dpll_parent, .round_rate = &omap2_dpll_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, }; #else static const struct clk_ops dpll_core_ck_ops = {}; @@ -97,6 +106,9 @@ static const struct clk_ops omap3_dpll_ck_ops = { .get_parent = &omap2_init_dpll_parent, .recalc_rate = &omap3_dpll_recalc, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, .round_rate = &omap2_dpll_round_rate, }; @@ -106,6 +118,9 @@ static const struct clk_ops omap3_dpll_per_ck_ops = { .get_parent = &omap2_init_dpll_parent, .recalc_rate = &omap3_dpll_recalc, .set_rate = &omap3_dpll4_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_dpll4_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, .round_rate = &omap2_dpll_round_rate, }; #endif diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index 162e519cb0f9..8ff03744fe98 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -2,6 +2,5 @@ obj-$(CONFIG_ICST) += clk-icst.o clk-versatile.o obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o -obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o obj-$(CONFIG_CLK_SP810) += clk-sp810.o obj-$(CONFIG_CLK_VEXPRESS_OSC) += clk-vexpress-osc.o diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 529a59c0fbfa..765f1e0eeeb2 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -70,7 +70,6 @@ static struct clk_ops vexpress_osc_ops = { static int vexpress_osc_probe(struct platform_device *pdev) { - struct clk_lookup *cl = pdev->dev.platform_data; /* Non-DT lookup */ struct clk_init_data init; struct vexpress_osc *osc; struct clk *clk; @@ -106,12 +105,6 @@ static int vexpress_osc_probe(struct platform_device *pdev) of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk); - /* Only happens for non-DT cases */ - if (cl) { - cl->clk = clk; - clkdev_add(cl); - } - dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name); return 0; diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c deleted file mode 100644 index 2d5e1b4820e0..000000000000 --- a/drivers/clk/versatile/clk-vexpress.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 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. - * - * Copyright (C) 2012 ARM Limited - */ - -#include <linux/amba/sp810.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/err.h> -#include <linux/vexpress.h> - -static struct clk *vexpress_sp810_timerclken[4]; -static DEFINE_SPINLOCK(vexpress_sp810_lock); - -static void __init vexpress_sp810_init(void __iomem *base) -{ - int i; - - if (WARN_ON(!base)) - return; - - for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++) { - char name[12]; - const char *parents[] = { - "v2m:refclk32khz", /* REFCLK */ - "v2m:refclk1mhz" /* TIMCLK */ - }; - - snprintf(name, ARRAY_SIZE(name), "timerclken%d", i); - - vexpress_sp810_timerclken[i] = clk_register_mux(NULL, name, - parents, 2, CLK_SET_RATE_NO_REPARENT, - base + SCCTRL, SCCTRL_TIMERENnSEL_SHIFT(i), 1, - 0, &vexpress_sp810_lock); - - if (WARN_ON(IS_ERR(vexpress_sp810_timerclken[i]))) - break; - } -} - - -static const char * const vexpress_clk_24mhz_periphs[] __initconst = { - "mb:uart0", "mb:uart1", "mb:uart2", "mb:uart3", - "mb:mmci", "mb:kmi0", "mb:kmi1" -}; - -void __init vexpress_clk_init(void __iomem *sp810_base) -{ - struct clk *clk; - int i; - - clk = clk_register_fixed_rate(NULL, "dummy_apb_pclk", NULL, - CLK_IS_ROOT, 0); - WARN_ON(clk_register_clkdev(clk, "apb_pclk", NULL)); - - clk = clk_register_fixed_rate(NULL, "v2m:clk_24mhz", NULL, - CLK_IS_ROOT, 24000000); - for (i = 0; i < ARRAY_SIZE(vexpress_clk_24mhz_periphs); i++) - WARN_ON(clk_register_clkdev(clk, NULL, - vexpress_clk_24mhz_periphs[i])); - - clk = clk_register_fixed_rate(NULL, "v2m:refclk32khz", NULL, - CLK_IS_ROOT, 32768); - WARN_ON(clk_register_clkdev(clk, NULL, "v2m:wdt")); - - clk = clk_register_fixed_rate(NULL, "v2m:refclk1mhz", NULL, - CLK_IS_ROOT, 1000000); - - vexpress_sp810_init(sp810_base); - - for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++) - WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i], clk)); - - WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0], - "v2m-timer0", "sp804")); - WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1], - "v2m-timer1", "sp804")); -} diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 90420600e1eb..f657a48d20eb 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -32,6 +32,7 @@ config ARMADA_370_XP_TIMER config MESON6_TIMER bool + select CLKSRC_MMIO config ORION_TIMER select CLKSRC_OF diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 756f6f10efa0..fae0435cc23d 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -45,4 +45,5 @@ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o +obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 43005d4d3348..6a79fc4f900c 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -462,7 +462,10 @@ static void __init arch_counter_register(unsigned type) /* Register the CP15 based counter if we have one */ if (type & ARCH_CP15_TIMER) { - arch_timer_read_counter = arch_counter_get_cntvct; + if (arch_timer_use_virtual) + arch_timer_read_counter = arch_counter_get_cntvct; + else + arch_timer_read_counter = arch_counter_get_cntpct; } else { arch_timer_read_counter = arch_counter_get_cntvct_mem; @@ -702,6 +705,14 @@ static void __init arch_timer_init(struct device_node *np) arch_timer_detect_rate(NULL, np); /* + * If we cannot rely on firmware initializing the timer registers then + * we should use the physical timers instead. + */ + if (IS_ENABLED(CONFIG_ARM) && + of_property_read_bool(np, "arm,cpu-registers-not-fw-configured")) + arch_timer_use_virtual = false; + + /* * If HYP mode is available, we know that the physical timer * has been configured to be accessible from PL1. Use it, so * that a guest can use the virtual timer instead. diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index 0451e62fac7a..ff37d3abb806 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -43,6 +43,7 @@ #include <linux/module.h> #include <linux/sched_clock.h> #include <linux/percpu.h> +#include <linux/syscore_ops.h> /* * Timer block registers. @@ -223,6 +224,28 @@ static struct notifier_block armada_370_xp_timer_cpu_nb = { .notifier_call = armada_370_xp_timer_cpu_notify, }; +static u32 timer0_ctrl_reg, timer0_local_ctrl_reg; + +static int armada_370_xp_timer_suspend(void) +{ + timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF); + timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF); + return 0; +} + +static void armada_370_xp_timer_resume(void) +{ + writel(0xffffffff, timer_base + TIMER0_VAL_OFF); + writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); + writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF); + writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF); +} + +struct syscore_ops armada_370_xp_timer_syscore_ops = { + .suspend = armada_370_xp_timer_suspend, + .resume = armada_370_xp_timer_resume, +}; + static void __init armada_370_xp_timer_common_init(struct device_node *np) { u32 clr = 0, set = 0; @@ -285,6 +308,8 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np) /* Immediately configure the timer on the boot CPU */ if (!res) armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt)); + + register_syscore_ops(&armada_370_xp_timer_syscore_ops); } static void __init armada_xp_timer_init(struct device_node *np) diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c index d5289098b3df..b5b4d4585c9a 100644 --- a/drivers/clocksource/timer-atmel-pit.c +++ b/drivers/clocksource/timer-atmel-pit.c @@ -262,35 +262,3 @@ static void __init at91sam926x_pit_dt_init(struct device_node *node) } CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", at91sam926x_pit_dt_init); - -static void __iomem *pit_base_addr; - -void __init at91sam926x_pit_init(int irq) -{ - struct pit_data *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - panic(pr_fmt("Unable to allocate memory\n")); - - data->base = pit_base_addr; - - data->mck = clk_get(NULL, "mck"); - if (IS_ERR(data->mck)) - panic(pr_fmt("Unable to get mck clk\n")); - - data->irq = irq; - - at91sam926x_pit_common_init(data); -} - -void __init at91sam926x_ioremap_pit(u32 addr) -{ - if (of_have_populated_dt()) - return; - - pit_base_addr = ioremap(addr, 16); - - if (!pit_base_addr) - panic(pr_fmt("Impossible to ioremap PIT\n")); -} diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c new file mode 100644 index 000000000000..b9efd30513d5 --- /dev/null +++ b/drivers/clocksource/timer-integrator-ap.c @@ -0,0 +1,210 @@ +/* + * Integrator/AP timer driver + * Copyright (C) 2000-2003 Deep Blue Solutions Ltd + * Copyright (c) 2014, Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/sched_clock.h> +#include <asm/hardware/arm_timer.h> + +static void __iomem * sched_clk_base; + +static u64 notrace integrator_read_sched_clock(void) +{ + return -readl(sched_clk_base + TIMER_VALUE); +} + +static void integrator_clocksource_init(unsigned long inrate, + void __iomem *base) +{ + u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC; + unsigned long rate = inrate; + + if (rate >= 1500000) { + rate /= 16; + ctrl |= TIMER_CTRL_DIV16; + } + + writel(0xffff, base + TIMER_LOAD); + writel(ctrl, base + TIMER_CTRL); + + clocksource_mmio_init(base + TIMER_VALUE, "timer2", + rate, 200, 16, clocksource_mmio_readl_down); + + sched_clk_base = base; + sched_clock_register(integrator_read_sched_clock, 16, rate); +} + +static unsigned long timer_reload; +static void __iomem * clkevt_base; + +/* + * IRQ handler for the timer + */ +static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + /* clear the interrupt */ + writel(1, clkevt_base + TIMER_INTCLR); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static void clkevt_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE; + + /* Disable timer */ + writel(ctrl, clkevt_base + TIMER_CTRL); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* Enable the timer and start the periodic tick */ + writel(timer_reload, clkevt_base + TIMER_LOAD); + ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; + writel(ctrl, clkevt_base + TIMER_CTRL); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* Leave the timer disabled, .set_next_event will enable it */ + ctrl &= ~TIMER_CTRL_PERIODIC; + writel(ctrl, clkevt_base + TIMER_CTRL); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_RESUME: + default: + /* Just leave in disabled state */ + break; + } + +} + +static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt) +{ + unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); + + writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); + writel(next, clkevt_base + TIMER_LOAD); + writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); + + return 0; +} + +static struct clock_event_device integrator_clockevent = { + .name = "timer1", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = clkevt_set_mode, + .set_next_event = clkevt_set_next_event, + .rating = 300, +}; + +static struct irqaction integrator_timer_irq = { + .name = "timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = integrator_timer_interrupt, + .dev_id = &integrator_clockevent, +}; + +static void integrator_clockevent_init(unsigned long inrate, + void __iomem *base, int irq) +{ + unsigned long rate = inrate; + unsigned int ctrl = 0; + + clkevt_base = base; + /* Calculate and program a divisor */ + if (rate > 0x100000 * HZ) { + rate /= 256; + ctrl |= TIMER_CTRL_DIV256; + } else if (rate > 0x10000 * HZ) { + rate /= 16; + ctrl |= TIMER_CTRL_DIV16; + } + timer_reload = rate / HZ; + writel(ctrl, clkevt_base + TIMER_CTRL); + + setup_irq(irq, &integrator_timer_irq); + clockevents_config_and_register(&integrator_clockevent, + rate, + 1, + 0xffffU); +} + +static void __init integrator_ap_timer_init_of(struct device_node *node) +{ + const char *path; + void __iomem *base; + int err; + int irq; + struct clk *clk; + unsigned long rate; + struct device_node *pri_node; + struct device_node *sec_node; + + base = of_io_request_and_map(node, 0, "integrator-timer"); + if (!base) + return; + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) { + pr_err("No clock for %s\n", node->name); + return; + } + clk_prepare_enable(clk); + rate = clk_get_rate(clk); + writel(0, base + TIMER_CTRL); + + err = of_property_read_string(of_aliases, + "arm,timer-primary", &path); + if (WARN_ON(err)) + return; + pri_node = of_find_node_by_path(path); + err = of_property_read_string(of_aliases, + "arm,timer-secondary", &path); + if (WARN_ON(err)) + return; + sec_node = of_find_node_by_path(path); + + if (node == pri_node) { + /* The primary timer lacks IRQ, use as clocksource */ + integrator_clocksource_init(rate, base); + return; + } + + if (node == sec_node) { + /* The secondary timer will drive the clock event */ + irq = irq_of_parse_and_map(node, 0); + integrator_clockevent_init(rate, base, irq); + return; + } + + pr_info("Timer @%p unused\n", base); + clk_disable_unprepare(clk); +} + +CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer", + integrator_ap_timer_init_of); diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 7072c2892d63..49c265255a07 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -61,14 +61,14 @@ config EDAC_DECODE_MCE has been initialized. config EDAC_MCE_INJ - tristate "Simple MCE injection interface over /sysfs" - depends on EDAC_DECODE_MCE + tristate "Simple MCE injection interface" + depends on EDAC_DECODE_MCE && DEBUG_FS default n help - This is a simple interface to inject MCEs over /sysfs and test - the MCE decoding code in EDAC. + This is a simple debugfs interface to inject MCEs and test different + aspects of the MCE handling code. - This is currently AMD-only. + WARNING: Do not even assume this interface is staying stable! config EDAC_MM_EDAC tristate "Main Memory EDAC (Error Detection And Correction) reporting" @@ -105,11 +105,11 @@ config EDAC_GHES In doubt, say 'Y'. config EDAC_AMD64 - tristate "AMD64 (Opteron, Athlon64) K8, F10h" - depends on EDAC_MM_EDAC && AMD_NB && X86_64 && EDAC_DECODE_MCE + tristate "AMD64 (Opteron, Athlon64)" + depends on EDAC_MM_EDAC && AMD_NB && EDAC_DECODE_MCE help Support for error detection and correction of DRAM ECC errors on - the AMD64 families of memory controllers (K8 and F10h) + the AMD64 families (>= K8) of memory controllers. config EDAC_AMD64_ERROR_INJECTION bool "Sysfs HW Error injection facilities" diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 359aa499b200..d40c69a04df7 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_EDAC) := edac_stub.o obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o -edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o +edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_core-y += edac_module.o edac_device_sysfs.o ifdef CONFIG_PCI diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index bbd65149cdb2..17638d7cf5c2 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -692,9 +692,19 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) { edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); - edac_dbg(1, " DIMM type: %sbuffered; all DIMMs support ECC: %s\n", - (dclr & BIT(16)) ? "un" : "", - (dclr & BIT(19)) ? "yes" : "no"); + if (pvt->dram_type == MEM_LRDDR3) { + u32 dcsm = pvt->csels[chan].csmasks[0]; + /* + * It's assumed all LRDIMMs in a DCT are going to be of + * same 'type' until proven otherwise. So, use a cs + * value of '0' here to get dcsm value. + */ + edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); + } + + edac_dbg(1, "All DIMMs support ECC:%s\n", + (dclr & BIT(19)) ? "yes" : "no"); + edac_dbg(1, " PAR/ERR parity: %s\n", (dclr & BIT(8)) ? "enabled" : "disabled"); @@ -756,7 +766,7 @@ static void prep_chip_selects(struct amd64_pvt *pvt) if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; - } else if (pvt->fam == 0x15 && pvt->model >= 0x30) { + } else if (pvt->fam == 0x15 && pvt->model == 0x30) { pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; } else { @@ -813,25 +823,63 @@ static void read_dct_base_mask(struct amd64_pvt *pvt) } } -static enum mem_type determine_memory_type(struct amd64_pvt *pvt, int cs) +static void determine_memory_type(struct amd64_pvt *pvt) { - enum mem_type type; + u32 dram_ctrl, dcsm; - /* F15h supports only DDR3 */ - if (pvt->fam >= 0x15) - type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; - else if (pvt->fam == 0x10 || pvt->ext_model >= K8_REV_F) { + switch (pvt->fam) { + case 0xf: + if (pvt->ext_model >= K8_REV_F) + goto ddr3; + + pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; + return; + + case 0x10: if (pvt->dchr0 & DDR3_MODE) - type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; + goto ddr3; + + pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; + return; + + case 0x15: + if (pvt->model < 0x60) + goto ddr3; + + /* + * Model 0x60h needs special handling: + * + * We use a Chip Select value of '0' to obtain dcsm. + * Theoretically, it is possible to populate LRDIMMs of different + * 'Rank' value on a DCT. But this is not the common case. So, + * it's reasonable to assume all DIMMs are going to be of same + * 'type' until proven otherwise. + */ + amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); + dcsm = pvt->csels[0].csmasks[0]; + + if (((dram_ctrl >> 8) & 0x7) == 0x2) + pvt->dram_type = MEM_DDR4; + else if (pvt->dclr0 & BIT(16)) + pvt->dram_type = MEM_DDR3; + else if (dcsm & 0x3) + pvt->dram_type = MEM_LRDDR3; else - type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; - } else { - type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; - } + pvt->dram_type = MEM_RDDR3; - amd64_info("CS%d: %s\n", cs, edac_mem_types[type]); + return; - return type; + case 0x16: + goto ddr3; + + default: + WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); + pvt->dram_type = MEM_EMPTY; + } + return; + +ddr3: + pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; } /* Get the number of DCT channels the memory controller is using. */ @@ -958,8 +1006,12 @@ static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) if (WARN_ON(!nb)) return; - pci_func = (pvt->model == 0x30) ? PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 - : PCI_DEVICE_ID_AMD_15H_NB_F1; + if (pvt->model == 0x60) + pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; + else if (pvt->model == 0x30) + pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; + else + pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); if (WARN_ON(!f1)) @@ -1049,7 +1101,7 @@ static int ddr2_cs_size(unsigned i, bool dct_width) } static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; @@ -1167,8 +1219,43 @@ static int ddr3_cs_size(unsigned i, bool dct_width) return cs_size; } +static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) +{ + unsigned shift = 0; + int cs_size = 0; + + if (i < 4 || i == 6) + cs_size = -1; + else if (i == 12) + shift = 7; + else if (!(i & 0x1)) + shift = i >> 1; + else + shift = (i + 1) >> 1; + + if (cs_size != -1) + cs_size = rank_multiply * (128 << shift); + + return cs_size; +} + +static int ddr4_cs_size(unsigned i) +{ + int cs_size = 0; + + if (i == 0) + cs_size = -1; + else if (i == 1) + cs_size = 1024; + else + /* Min cs_size = 1G */ + cs_size = 1024 * (1 << (i >> 1)); + + return cs_size; +} + static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; @@ -1184,18 +1271,49 @@ static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, * F15h supports only 64bit DCT interfaces */ static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { WARN_ON(cs_mode > 12); return ddr3_cs_size(cs_mode, false); } +/* F15h M60h supports DDR4 mapping as well.. */ +static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, + unsigned cs_mode, int cs_mask_nr) +{ + int cs_size; + u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; + + WARN_ON(cs_mode > 12); + + if (pvt->dram_type == MEM_DDR4) { + if (cs_mode > 9) + return -1; + + cs_size = ddr4_cs_size(cs_mode); + } else if (pvt->dram_type == MEM_LRDDR3) { + unsigned rank_multiply = dcsm & 0xf; + + if (rank_multiply == 3) + rank_multiply = 4; + cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); + } else { + /* Minimum cs size is 512mb for F15hM60h*/ + if (cs_mode == 0x1) + return -1; + + cs_size = ddr3_cs_size(cs_mode, false); + } + + return cs_size; +} + /* * F16h and F15h model 30h have only limited cs_modes. */ static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, - unsigned cs_mode) + unsigned cs_mode, int cs_mask_nr) { WARN_ON(cs_mode > 12); @@ -1757,13 +1875,20 @@ static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) size0 = 0; if (dcsb[dimm*2] & DCSB_CS_ENABLE) + /* For f15m60h, need multiplier for LRDIMM cs_size + * calculation. We pass 'dimm' value to the dbam_to_cs + * mapper so we can find the multiplier from the + * corresponding DCSM. + */ size0 = pvt->ops->dbam_to_cs(pvt, ctrl, - DBAM_DIMM(dimm, dbam)); + DBAM_DIMM(dimm, dbam), + dimm); size1 = 0; if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) size1 = pvt->ops->dbam_to_cs(pvt, ctrl, - DBAM_DIMM(dimm, dbam)); + DBAM_DIMM(dimm, dbam), + dimm); amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", dimm * 2, size0, @@ -1812,6 +1937,16 @@ static struct amd64_family_type family_types[] = { .dbam_to_cs = f16_dbam_to_chip_select, } }, + [F15_M60H_CPUS] = { + .ctl_name = "F15h_M60h", + .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, + .f3_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F3, + .ops = { + .early_channel_count = f1x_early_channel_count, + .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, + .dbam_to_cs = f15_m60h_dbam_to_chip_select, + } + }, [F16_CPUS] = { .ctl_name = "F16h", .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, @@ -2175,6 +2310,8 @@ static void read_mc_regs(struct amd64_pvt *pvt) } pvt->ecc_sym_sz = 4; + determine_memory_type(pvt); + edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); if (pvt->fam >= 0x10) { amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); @@ -2238,7 +2375,8 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) */ cs_mode = DBAM_DIMM(csrow_nr / 2, dbam); - nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT); + nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2)) + << (20 - PAGE_SHIFT); edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", csrow_nr, dct, cs_mode); @@ -2257,7 +2395,6 @@ static int init_csrows(struct mem_ctl_info *mci) struct csrow_info *csrow; struct dimm_info *dimm; enum edac_type edac_mode; - enum mem_type mtype; int i, j, empty = 1; int nr_pages = 0; u32 val; @@ -2302,8 +2439,6 @@ static int init_csrows(struct mem_ctl_info *mci) nr_pages += row_dct1_pages; } - mtype = determine_memory_type(pvt, i); - edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); /* @@ -2317,7 +2452,7 @@ static int init_csrows(struct mem_ctl_info *mci) for (j = 0; j < pvt->channel_count; j++) { dimm = csrow->channels[j]->dimm; - dimm->mtype = mtype; + dimm->mtype = pvt->dram_type; dimm->edac_mode = edac_mode; } } @@ -2604,6 +2739,10 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) fam_type = &family_types[F15_M30H_CPUS]; pvt->ops = &family_types[F15_M30H_CPUS].ops; break; + } else if (pvt->model == 0x60) { + fam_type = &family_types[F15_M60H_CPUS]; + pvt->ops = &family_types[F15_M60H_CPUS].ops; + break; } fam_type = &family_types[F15_CPUS]; @@ -2828,55 +2967,13 @@ static void remove_one_instance(struct pci_dev *pdev) * inquiry this table to see if this driver is for a given device found. */ static const struct pci_device_id amd64_pci_table[] = { - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_10H_NB_DRAM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_15H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_16H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - }, - + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_K8_NB_MEMCTL) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_DRAM) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F2) }, {0, } }; MODULE_DEVICE_TABLE(pci, amd64_pci_table); @@ -2938,6 +3035,11 @@ static int __init amd64_edac_init(void) goto err_no_instances; setup_pci_device(); + +#ifdef CONFIG_X86_32 + amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); +#endif + return 0; err_no_instances: diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 55fb5941c6d4..d8468c667925 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -162,10 +162,12 @@ /* * PCI-defined configuration space registers */ -#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b -#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F2 0x141c #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 +#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b +#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F2 0x141c +#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F1 0x1571 +#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F2 0x1572 #define PCI_DEVICE_ID_AMD_16H_NB_F1 0x1531 #define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532 #define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581 @@ -221,6 +223,8 @@ #define csrow_enabled(i, dct, pvt) ((pvt)->csels[(dct)].csbases[(i)] & DCSB_CS_ENABLE) +#define DRAM_CONTROL 0x78 + #define DBAM0 0x80 #define DBAM1 0x180 @@ -301,6 +305,7 @@ enum amd_families { F10_CPUS, F15_CPUS, F15_M30H_CPUS, + F15_M60H_CPUS, F16_CPUS, F16_M30H_CPUS, NUM_FAMILIES, @@ -379,6 +384,9 @@ struct amd64_pvt { /* place to store error injection parameters prior to issue */ struct error_injection injection; + + /* cache the dram_type */ + enum mem_type dram_type; }; enum err_codes { @@ -480,7 +488,8 @@ struct low_ops { int (*early_channel_count) (struct amd64_pvt *pvt); void (*map_sysaddr_to_csrow) (struct mem_ctl_info *mci, u64 sys_addr, struct err_info *); - int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct, unsigned cs_mode); + int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct, + unsigned cs_mode, int cs_mask_nr); }; struct amd64_family_type { diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index c3893b0ddb18..1747906f10ce 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -125,27 +125,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) #endif /* CONFIG_EDAC_DEBUG */ -/* - * keep those in sync with the enum mem_type - */ const char * const edac_mem_types[] = { - "Empty csrow", - "Reserved csrow type", - "Unknown csrow type", - "Fast page mode RAM", - "Extended data out RAM", - "Burst Extended data out RAM", - "Single data rate SDRAM", - "Registered single data rate SDRAM", - "Double data rate SDRAM", - "Registered Double data rate SDRAM", - "Rambus DRAM", - "Unbuffered DDR2 RAM", - "Fully buffered DDR2", - "Registered DDR2 RAM", - "Rambus XDR", - "Unbuffered DDR3 RAM", - "Registered DDR3 RAM", + [MEM_EMPTY] = "Empty csrow", + [MEM_RESERVED] = "Reserved csrow type", + [MEM_UNKNOWN] = "Unknown csrow type", + [MEM_FPM] = "Fast page mode RAM", + [MEM_EDO] = "Extended data out RAM", + [MEM_BEDO] = "Burst Extended data out RAM", + [MEM_SDR] = "Single data rate SDRAM", + [MEM_RDR] = "Registered single data rate SDRAM", + [MEM_DDR] = "Double data rate SDRAM", + [MEM_RDDR] = "Registered Double data rate SDRAM", + [MEM_RMBS] = "Rambus DRAM", + [MEM_DDR2] = "Unbuffered DDR2 RAM", + [MEM_FB_DDR2] = "Fully buffered DDR2", + [MEM_RDDR2] = "Registered DDR2 RAM", + [MEM_XDR] = "Rambus XDR", + [MEM_DDR3] = "Unbuffered DDR3 RAM", + [MEM_RDDR3] = "Registered DDR3 RAM", + [MEM_LRDDR3] = "Load-Reduced DDR3 RAM", + [MEM_DDR4] = "Unbuffered DDR4 RAM", + [MEM_RDDR4] = "Registered DDR4 RAM", }; EXPORT_SYMBOL_GPL(edac_mem_types); diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index e8658e451762..24d877f6e577 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -14,9 +14,6 @@ #include "edac_core.h" #include "edac_module.h" -/* Turn off this whole feature if PCI is not configured */ -#ifdef CONFIG_PCI - #define EDAC_PCI_SYMLINK "device" /* data variables exported via sysfs */ @@ -761,5 +758,3 @@ MODULE_PARM_DESC(check_pci_errors, module_param(edac_pci_panic_on_pe, int, 0644); MODULE_PARM_DESC(edac_pci_panic_on_pe, "Panic on PCI Bus Parity error: 0=off 1=on"); - -#endif /* CONFIG_PCI */ diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 8399b4e16fe0..b24681998740 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -413,8 +413,8 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, /* Generate the trace event */ grain_bits = fls_long(e->grain); - sprintf(pvt->detail_location, "APEI location: %s %s", - e->location, e->other_detail); + snprintf(pvt->detail_location, sizeof(pvt->detail_location), + "APEI location: %s %s", e->location, e->other_detail); trace_mc_event(type, e->msg, e->label, e->error_count, mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer, PAGES_TO_MiB(e->page_frame_number) | e->offset_in_page, diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index cd28b968e5c7..5cb36a6022cc 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -542,8 +542,7 @@ fail1: pci_unregister_driver(&i3000_driver); fail0: - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index aa98b136f5d0..4ad062b0ef26 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -523,8 +523,7 @@ fail1: pci_unregister_driver(&i3200_driver); fail0: - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index d730e276d1a8..b4705d9366bf 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -458,8 +458,7 @@ static void __exit i82443bxgx_edacmc_exit(void) if (!i82443bxgx_registered) i82443bxgx_edacmc_remove_one(mci_pdev); - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); } module_init(i82443bxgx_edacmc_init); diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index f78c1c54dbd5..58586d59bf8e 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -138,6 +138,15 @@ static const char * const mc5_mce_desc[] = { "Retire status queue" }; +static const char * const mc6_mce_desc[] = { + "Hardware Assertion", + "Free List", + "Physical Register File", + "Retire Queue", + "Scheduler table", + "Status Register File", +}; + static bool f12h_mc0_mce(u16 ec, u8 xec) { bool ret = false; @@ -432,8 +441,8 @@ static bool k8_mc2_mce(u16 ec, u8 xec) pr_cont(": %s error in the L2 cache tags.\n", R4_MSG(ec)); else if (xec == 0x0) { if (TLB_ERROR(ec)) - pr_cont(": %s error in a Page Descriptor Cache or " - "Guest TLB.\n", TT_MSG(ec)); + pr_cont("%s error in a Page Descriptor Cache or Guest TLB.\n", + TT_MSG(ec)); else if (BUS_ERROR(ec)) pr_cont(": %s/ECC error in data read from NB: %s.\n", R4_MSG(ec), PP_MSG(ec)); @@ -672,38 +681,10 @@ static void decode_mc6_mce(struct mce *m) pr_emerg(HW_ERR "MC6 Error: "); - switch (xec) { - case 0x0: - pr_cont("Hardware Assertion"); - break; - - case 0x1: - pr_cont("Free List"); - break; - - case 0x2: - pr_cont("Physical Register File"); - break; - - case 0x3: - pr_cont("Retire Queue"); - break; - - case 0x4: - pr_cont("Scheduler table"); - break; - - case 0x5: - pr_cont("Status Register File"); - break; - - default: + if (xec > 0x5) goto wrong_mc6_mce; - break; - } - - pr_cont(" parity error.\n"); + pr_cont("%s parity error.\n", mc6_mce_desc[xec]); return; wrong_mc6_mce: @@ -800,7 +781,7 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) pr_cont("]: 0x%016llx\n", m->status); if (m->status & MCI_STATUS_ADDRV) - pr_emerg(HW_ERR "MC%d_ADDR: 0x%016llx\n", m->bank, m->addr); + pr_emerg(HW_ERR "MC%d Error Address: 0x%016llx\n", m->bank, m->addr); if (!fam_ops) goto err_code; diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 5e46a9fea31b..0bd91a802c67 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -1,173 +1,262 @@ /* - * A simple MCE injection facility for testing the MCE decoding code. This - * driver should be built as module so that it can be loaded on production - * kernels for testing purposes. + * A simple MCE injection facility for testing different aspects of the RAS + * code. This driver should be built as module so that it can be loaded + * on production kernels for testing purposes. * * This file may be distributed under the terms of the GNU General Public * License version 2. * - * Copyright (c) 2010: Borislav Petkov <bp@alien8.de> + * Copyright (c) 2010-14: Borislav Petkov <bp@alien8.de> * Advanced Micro Devices Inc. */ #include <linux/kobject.h> +#include <linux/debugfs.h> #include <linux/device.h> -#include <linux/edac.h> #include <linux/module.h> +#include <linux/cpu.h> #include <asm/mce.h> #include "mce_amd.h" -struct edac_mce_attr { - struct attribute attr; - ssize_t (*show) (struct kobject *kobj, struct edac_mce_attr *attr, char *buf); - ssize_t (*store)(struct kobject *kobj, struct edac_mce_attr *attr, - const char *buf, size_t count); -}; - -#define EDAC_MCE_ATTR(_name, _mode, _show, _store) \ -static struct edac_mce_attr mce_attr_##_name = __ATTR(_name, _mode, _show, _store) - -static struct kobject *mce_kobj; - /* * Collect all the MCi_XXX settings */ static struct mce i_mce; +static struct dentry *dfs_inj; -#define MCE_INJECT_STORE(reg) \ -static ssize_t edac_inject_##reg##_store(struct kobject *kobj, \ - struct edac_mce_attr *attr, \ - const char *data, size_t count)\ +#define MCE_INJECT_SET(reg) \ +static int inj_##reg##_set(void *data, u64 val) \ { \ - int ret = 0; \ - unsigned long value; \ - \ - ret = kstrtoul(data, 16, &value); \ - if (ret < 0) \ - printk(KERN_ERR "Error writing MCE " #reg " field.\n"); \ + struct mce *m = (struct mce *)data; \ \ - i_mce.reg = value; \ - \ - return count; \ + m->reg = val; \ + return 0; \ } -MCE_INJECT_STORE(status); -MCE_INJECT_STORE(misc); -MCE_INJECT_STORE(addr); +MCE_INJECT_SET(status); +MCE_INJECT_SET(misc); +MCE_INJECT_SET(addr); -#define MCE_INJECT_SHOW(reg) \ -static ssize_t edac_inject_##reg##_show(struct kobject *kobj, \ - struct edac_mce_attr *attr, \ - char *buf) \ +#define MCE_INJECT_GET(reg) \ +static int inj_##reg##_get(void *data, u64 *val) \ { \ - return sprintf(buf, "0x%016llx\n", i_mce.reg); \ + struct mce *m = (struct mce *)data; \ + \ + *val = m->reg; \ + return 0; \ } -MCE_INJECT_SHOW(status); -MCE_INJECT_SHOW(misc); -MCE_INJECT_SHOW(addr); +MCE_INJECT_GET(status); +MCE_INJECT_GET(misc); +MCE_INJECT_GET(addr); -EDAC_MCE_ATTR(status, 0644, edac_inject_status_show, edac_inject_status_store); -EDAC_MCE_ATTR(misc, 0644, edac_inject_misc_show, edac_inject_misc_store); -EDAC_MCE_ATTR(addr, 0644, edac_inject_addr_show, edac_inject_addr_store); +DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n"); /* - * This denotes into which bank we're injecting and triggers - * the injection, at the same time. + * Caller needs to be make sure this cpu doesn't disappear + * from under us, i.e.: get_cpu/put_cpu. */ -static ssize_t edac_inject_bank_store(struct kobject *kobj, - struct edac_mce_attr *attr, - const char *data, size_t count) +static int toggle_hw_mce_inject(unsigned int cpu, bool enable) { - int ret = 0; - unsigned long value; + u32 l, h; + int err; - ret = kstrtoul(data, 10, &value); - if (ret < 0) { - printk(KERN_ERR "Invalid bank value!\n"); - return -EINVAL; + err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h); + if (err) { + pr_err("%s: error reading HWCR\n", __func__); + return err; } - if (value > 5) - if (boot_cpu_data.x86 != 0x15 || value > 6) { - printk(KERN_ERR "Non-existent MCE bank: %lu\n", value); - return -EINVAL; - } + enable ? (l |= BIT(18)) : (l &= ~BIT(18)); - i_mce.bank = value; + err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h); + if (err) + pr_err("%s: error writing HWCR\n", __func__); - amd_decode_mce(NULL, 0, &i_mce); + return err; +} - return count; +static int flags_get(void *data, u64 *val) +{ + struct mce *m = (struct mce *)data; + + *val = m->inject_flags; + + return 0; } -static ssize_t edac_inject_bank_show(struct kobject *kobj, - struct edac_mce_attr *attr, char *buf) +static int flags_set(void *data, u64 val) { - return sprintf(buf, "%d\n", i_mce.bank); + struct mce *m = (struct mce *)data; + + m->inject_flags = (u8)val; + return 0; } -EDAC_MCE_ATTR(bank, 0644, edac_inject_bank_show, edac_inject_bank_store); +DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n"); -static struct edac_mce_attr *sysfs_attrs[] = { &mce_attr_status, &mce_attr_misc, - &mce_attr_addr, &mce_attr_bank -}; +/* + * On which CPU to inject? + */ +MCE_INJECT_GET(extcpu); -static int __init edac_init_mce_inject(void) +static int inj_extcpu_set(void *data, u64 val) { - struct bus_type *edac_subsys = NULL; - int i, err = 0; + struct mce *m = (struct mce *)data; - edac_subsys = edac_get_sysfs_subsys(); - if (!edac_subsys) + if (val >= nr_cpu_ids || !cpu_online(val)) { + pr_err("%s: Invalid CPU: %llu\n", __func__, val); return -EINVAL; + } + m->extcpu = val; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n"); - mce_kobj = kobject_create_and_add("mce", &edac_subsys->dev_root->kobj); - if (!mce_kobj) { - printk(KERN_ERR "Error creating a mce kset.\n"); - err = -ENOMEM; - goto err_mce_kobj; +static void trigger_mce(void *info) +{ + asm volatile("int $18"); +} + +static void do_inject(void) +{ + u64 mcg_status = 0; + unsigned int cpu = i_mce.extcpu; + u8 b = i_mce.bank; + + if (!(i_mce.inject_flags & MCJ_EXCEPTION)) { + amd_decode_mce(NULL, 0, &i_mce); + return; } - for (i = 0; i < ARRAY_SIZE(sysfs_attrs); i++) { - err = sysfs_create_file(mce_kobj, &sysfs_attrs[i]->attr); - if (err) { - printk(KERN_ERR "Error creating %s in sysfs.\n", - sysfs_attrs[i]->attr.name); - goto err_sysfs_create; + get_online_cpus(); + if (!cpu_online(cpu)) + goto err; + + /* prep MCE global settings for the injection */ + mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV; + + if (!(i_mce.status & MCI_STATUS_PCC)) + mcg_status |= MCG_STATUS_RIPV; + + toggle_hw_mce_inject(cpu, true); + + wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS, + (u32)mcg_status, (u32)(mcg_status >> 32)); + + wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b), + (u32)i_mce.status, (u32)(i_mce.status >> 32)); + + wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b), + (u32)i_mce.addr, (u32)(i_mce.addr >> 32)); + + wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b), + (u32)i_mce.misc, (u32)(i_mce.misc >> 32)); + + toggle_hw_mce_inject(cpu, false); + + smp_call_function_single(cpu, trigger_mce, NULL, 0); + +err: + put_online_cpus(); + +} + +/* + * This denotes into which bank we're injecting and triggers + * the injection, at the same time. + */ +static int inj_bank_set(void *data, u64 val) +{ + struct mce *m = (struct mce *)data; + + if (val > 5) { + if (boot_cpu_data.x86 != 0x15 || val > 6) { + pr_err("Non-existent MCE bank: %llu\n", val); + return -EINVAL; } } - return 0; -err_sysfs_create: - while (--i >= 0) - sysfs_remove_file(mce_kobj, &sysfs_attrs[i]->attr); + m->bank = val; + do_inject(); - kobject_del(mce_kobj); + return 0; +} -err_mce_kobj: - edac_put_sysfs_subsys(); +static int inj_bank_get(void *data, u64 *val) +{ + struct mce *m = (struct mce *)data; - return err; + *val = m->bank; + return 0; } -static void __exit edac_exit_mce_inject(void) +DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); + +struct dfs_node { + char *name; + struct dentry *d; + const struct file_operations *fops; +} dfs_fls[] = { + { .name = "status", .fops = &status_fops }, + { .name = "misc", .fops = &misc_fops }, + { .name = "addr", .fops = &addr_fops }, + { .name = "bank", .fops = &bank_fops }, + { .name = "flags", .fops = &flags_fops }, + { .name = "cpu", .fops = &extcpu_fops }, +}; + +static int __init init_mce_inject(void) { int i; - for (i = 0; i < ARRAY_SIZE(sysfs_attrs); i++) - sysfs_remove_file(mce_kobj, &sysfs_attrs[i]->attr); + dfs_inj = debugfs_create_dir("mce-inject", NULL); + if (!dfs_inj) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) { + dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name, + S_IRUSR | S_IWUSR, + dfs_inj, + &i_mce, + dfs_fls[i].fops); + + if (!dfs_fls[i].d) + goto err_dfs_add; + } + + return 0; + +err_dfs_add: + while (--i >= 0) + debugfs_remove(dfs_fls[i].d); - kobject_del(mce_kobj); + debugfs_remove(dfs_inj); + dfs_inj = NULL; - edac_put_sysfs_subsys(); + return -ENOMEM; } -module_init(edac_init_mce_inject); -module_exit(edac_exit_mce_inject); +static void __exit exit_mce_inject(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) + debugfs_remove(dfs_fls[i].d); + + memset(&dfs_fls, 0, sizeof(dfs_fls)); + + debugfs_remove(dfs_inj); + dfs_inj = NULL; +} +module_init(init_mce_inject); +module_exit(exit_mce_inject); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>"); MODULE_AUTHOR("AMD Inc."); -MODULE_DESCRIPTION("MCE injection facility for testing MCE decoding"); +MODULE_DESCRIPTION("MCE injection facility for RAS testing"); diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index 542fad70e360..6366e880f978 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -178,7 +178,7 @@ static int mv64x60_pci_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_pci_isr, - IRQF_DISABLED, + 0, "[EDAC] PCI err", pci); if (res < 0) { @@ -345,7 +345,7 @@ static int mv64x60_sram_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_sram_isr, - IRQF_DISABLED, + 0, "[EDAC] SRAM err", edac_dev); if (res < 0) { @@ -540,7 +540,7 @@ static int mv64x60_cpu_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_cpu_isr, - IRQF_DISABLED, + 0, "[EDAC] CPU err", edac_dev); if (res < 0) { @@ -800,7 +800,7 @@ static int mv64x60_mc_err_probe(struct platform_device *pdev) res = devm_request_irq(&pdev->dev, pdata->irq, mv64x60_mc_isr, - IRQF_DISABLED, + 0, "[EDAC] MC err", mci); if (res < 0) { diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 0f04d5ead521..41593539cec4 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -1120,7 +1120,7 @@ static int ppc4xx_edac_register_irq(struct platform_device *op, status = request_irq(ded_irq, ppc4xx_edac_isr, - IRQF_DISABLED, + 0, "[EDAC] MC ECCDED", mci); @@ -1134,7 +1134,7 @@ static int ppc4xx_edac_register_irq(struct platform_device *op, status = request_irq(sec_irq, ppc4xx_edac_isr, - IRQF_DISABLED, + 0, "[EDAC] MC ECCSEC", mci); diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index e644b52c287c..7c5cdc62f31c 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -500,8 +500,7 @@ fail1: pci_unregister_driver(&x38_driver); fail0: - if (mci_pdev) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 17afc51f3054..c5f7b4e9eb6c 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -93,6 +93,12 @@ static void dmi_table(u8 *buf, int len, int num, const struct dmi_header *dm = (const struct dmi_header *)data; /* + * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] + */ + if (dm->type == DMI_ENTRY_END_OF_TABLE) + break; + + /* * We want to know the total length (formatted area and * strings) before decoding to make sure we won't run off the * table in dmi_decode or dmi_string @@ -107,7 +113,7 @@ static void dmi_table(u8 *buf, int len, int num, } } -static u32 dmi_base; +static phys_addr_t dmi_base; static u16 dmi_len; static u16 dmi_num; @@ -467,7 +473,7 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { - smbios_ver = (buf[6] << 8) + buf[7]; + smbios_ver = get_unaligned_be16(buf + 6); /* Some BIOS report weird SMBIOS version, fix that up */ switch (smbios_ver) { @@ -489,10 +495,9 @@ static int __init dmi_present(const u8 *buf) buf += 16; if (memcmp(buf, "_DMI_", 5) == 0 && dmi_checksum(buf, 15)) { - dmi_num = (buf[13] << 8) | buf[12]; - dmi_len = (buf[7] << 8) | buf[6]; - dmi_base = (buf[11] << 24) | (buf[10] << 16) | - (buf[9] << 8) | buf[8]; + dmi_num = get_unaligned_le16(buf + 12); + dmi_len = get_unaligned_le16(buf + 6); + dmi_base = get_unaligned_le32(buf + 8); if (dmi_walk_early(dmi_decode) == 0) { if (smbios_ver) { @@ -514,12 +519,72 @@ static int __init dmi_present(const u8 *buf) return 1; } +/* + * Check for the SMBIOS 3.0 64-bit entry point signature. Unlike the legacy + * 32-bit entry point, there is no embedded DMI header (_DMI_) in here. + */ +static int __init dmi_smbios3_present(const u8 *buf) +{ + if (memcmp(buf, "_SM3_", 5) == 0 && + buf[6] < 32 && dmi_checksum(buf, buf[6])) { + dmi_ver = get_unaligned_be16(buf + 7); + dmi_len = get_unaligned_le32(buf + 12); + dmi_base = get_unaligned_le64(buf + 16); + + /* + * The 64-bit SMBIOS 3.0 entry point no longer has a field + * containing the number of structures present in the table. + * Instead, it defines the table size as a maximum size, and + * relies on the end-of-table structure type (#127) to be used + * to signal the end of the table. + * So let's define dmi_num as an upper bound as well: each + * structure has a 4 byte header, so dmi_len / 4 is an upper + * bound for the number of structures in the table. + */ + dmi_num = dmi_len / 4; + + if (dmi_walk_early(dmi_decode) == 0) { + pr_info("SMBIOS %d.%d present.\n", + dmi_ver >> 8, dmi_ver & 0xFF); + dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string)); + pr_debug("DMI: %s\n", dmi_ids_string); + return 0; + } + } + return 1; +} + void __init dmi_scan_machine(void) { char __iomem *p, *q; char buf[32]; if (efi_enabled(EFI_CONFIG_TABLES)) { + /* + * According to the DMTF SMBIOS reference spec v3.0.0, it is + * allowed to define both the 64-bit entry point (smbios3) and + * the 32-bit entry point (smbios), in which case they should + * either both point to the same SMBIOS structure table, or the + * table pointed to by the 64-bit entry point should contain a + * superset of the table contents pointed to by the 32-bit entry + * point (section 5.2) + * This implies that the 64-bit entry point should have + * precedence if it is defined and supported by the OS. If we + * have the 64-bit entry point, but fail to decode it, fall + * back to the legacy one (if available) + */ + if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) { + p = dmi_early_remap(efi.smbios3, 32); + if (p == NULL) + goto error; + memcpy_fromio(buf, p, 32); + dmi_early_unmap(p, 32); + + if (!dmi_smbios3_present(buf)) { + dmi_available = 1; + goto out; + } + } if (efi.smbios == EFI_INVALID_TABLE_ADDR) goto error; @@ -552,7 +617,7 @@ void __init dmi_scan_machine(void) memset(buf, 0, 16); for (q = p; q < p + 0x10000; q += 16) { memcpy_fromio(buf + 16, q, 16); - if (!dmi_present(buf)) { + if (!dmi_smbios3_present(buf) || !dmi_present(buf)) { dmi_available = 1; dmi_early_unmap(p, 0x10000); goto out; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8590099ac148..9035c1b74d58 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -30,6 +30,7 @@ struct efi __read_mostly efi = { .acpi = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, + .smbios3 = EFI_INVALID_TABLE_ADDR, .sal_systab = EFI_INVALID_TABLE_ADDR, .boot_info = EFI_INVALID_TABLE_ADDR, .hcdp = EFI_INVALID_TABLE_ADDR, @@ -86,6 +87,8 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); if (efi.smbios != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); + if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); if (efi.hcdp != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); if (efi.boot_info != EFI_INVALID_TABLE_ADDR) @@ -260,6 +263,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {MPS_TABLE_GUID, "MPS", &efi.mps}, {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, + {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, {NULL_GUID, NULL, NULL}, }; diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 75ee05964cbc..eb48a1a1a576 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -247,9 +247,18 @@ unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, goto fail_free_cmdline; } } - if (!fdt_addr) + + if (fdt_addr) { + pr_efi(sys_table, "Using DTB from command line\n"); + } else { /* Look for a device tree configuration table entry. */ fdt_addr = (uintptr_t)get_fdt(sys_table); + if (fdt_addr) + pr_efi(sys_table, "Using DTB from configuration table\n"); + } + + if (!fdt_addr) + pr_efi(sys_table, "Generating empty DTB\n"); status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=", dram_base + SZ_512M, diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0959ca9b6b27..23dfd5f59b39 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -905,4 +905,16 @@ config GPIO_VIPERBOARD River Tech's viperboard.h for detailed meaning of the module parameters. +config GPIO_DLN2 + tristate "Diolan DLN2 GPIO support" + depends on MFD_DLN2 + select GPIOLIB_IRQCHIP + + help + Select this option to enable GPIO driver for the Diolan DLN2 + board. + + This driver can also be built as a module. If so, the module + will be called gpio-dln2. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e5d346cf3b6e..e60677b8ccb4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o +obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c new file mode 100644 index 000000000000..978b51eae2ec --- /dev/null +++ b/drivers/gpio/gpio-dln2.c @@ -0,0 +1,553 @@ +/* + * Driver for the Diolan DLN-2 USB-GPIO adapter + * + * Copyright (c) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#define DLN2_GPIO_ID 0x01 + +#define DLN2_GPIO_GET_PIN_COUNT DLN2_CMD(0x01, DLN2_GPIO_ID) +#define DLN2_GPIO_SET_DEBOUNCE DLN2_CMD(0x04, DLN2_GPIO_ID) +#define DLN2_GPIO_GET_DEBOUNCE DLN2_CMD(0x05, DLN2_GPIO_ID) +#define DLN2_GPIO_PORT_GET_VAL DLN2_CMD(0x06, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_VAL DLN2_CMD(0x0B, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_OUT_VAL DLN2_CMD(0x0C, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_OUT_VAL DLN2_CMD(0x0D, DLN2_GPIO_ID) +#define DLN2_GPIO_CONDITION_MET_EV DLN2_CMD(0x0F, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_ENABLE DLN2_CMD(0x10, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_DISABLE DLN2_CMD(0x11, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_DIRECTION DLN2_CMD(0x13, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_DIRECTION DLN2_CMD(0x14, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_EVENT_CFG DLN2_CMD(0x1E, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_EVENT_CFG DLN2_CMD(0x1F, DLN2_GPIO_ID) + +#define DLN2_GPIO_EVENT_NONE 0 +#define DLN2_GPIO_EVENT_CHANGE 1 +#define DLN2_GPIO_EVENT_LVL_HIGH 2 +#define DLN2_GPIO_EVENT_LVL_LOW 3 +#define DLN2_GPIO_EVENT_CHANGE_RISING 0x11 +#define DLN2_GPIO_EVENT_CHANGE_FALLING 0x21 +#define DLN2_GPIO_EVENT_MASK 0x0F + +#define DLN2_GPIO_MAX_PINS 32 + +struct dln2_irq_work { + struct work_struct work; + struct dln2_gpio *dln2; + int pin; + int type; +}; + +struct dln2_gpio { + struct platform_device *pdev; + struct gpio_chip gpio; + + /* + * Cache pin direction to save us one transfer, since the hardware has + * separate commands to read the in and out values. + */ + DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS); + + DECLARE_BITMAP(irqs_masked, DLN2_GPIO_MAX_PINS); + DECLARE_BITMAP(irqs_enabled, DLN2_GPIO_MAX_PINS); + DECLARE_BITMAP(irqs_pending, DLN2_GPIO_MAX_PINS); + struct dln2_irq_work *irq_work; +}; + +struct dln2_gpio_pin { + __le16 pin; +}; + +struct dln2_gpio_pin_val { + __le16 pin __packed; + u8 value; +}; + +static int dln2_gpio_get_pin_count(struct platform_device *pdev) +{ + int ret; + __le16 count; + int len = sizeof(count); + + ret = dln2_transfer_rx(pdev, DLN2_GPIO_GET_PIN_COUNT, &count, &len); + if (ret < 0) + return ret; + if (len < sizeof(count)) + return -EPROTO; + + return le16_to_cpu(count); +} + +static int dln2_gpio_pin_cmd(struct dln2_gpio *dln2, int cmd, unsigned pin) +{ + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(pin), + }; + + return dln2_transfer_tx(dln2->pdev, cmd, &req, sizeof(req)); +} + +static int dln2_gpio_pin_val(struct dln2_gpio *dln2, int cmd, unsigned int pin) +{ + int ret; + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(pin), + }; + struct dln2_gpio_pin_val rsp; + int len = sizeof(rsp); + + ret = dln2_transfer(dln2->pdev, cmd, &req, sizeof(req), &rsp, &len); + if (ret < 0) + return ret; + if (len < sizeof(rsp) || req.pin != rsp.pin) + return -EPROTO; + + return rsp.value; +} + +static int dln2_gpio_pin_get_in_val(struct dln2_gpio *dln2, unsigned int pin) +{ + int ret; + + ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_VAL, pin); + if (ret < 0) + return ret; + return !!ret; +} + +static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin) +{ + int ret; + + ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_OUT_VAL, pin); + if (ret < 0) + return ret; + return !!ret; +} + +static void dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2, + unsigned int pin, int value) +{ + struct dln2_gpio_pin_val req = { + .pin = cpu_to_le16(pin), + .value = value, + }; + + dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req, + sizeof(req)); +} + +#define DLN2_GPIO_DIRECTION_IN 0 +#define DLN2_GPIO_DIRECTION_OUT 1 + +static int dln2_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(offset), + }; + struct dln2_gpio_pin_val rsp; + int len = sizeof(rsp); + int ret; + + ret = dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_ENABLE, offset); + if (ret < 0) + return ret; + + /* cache the pin direction */ + ret = dln2_transfer(dln2->pdev, DLN2_GPIO_PIN_GET_DIRECTION, + &req, sizeof(req), &rsp, &len); + if (ret < 0) + return ret; + if (len < sizeof(rsp) || req.pin != rsp.pin) { + ret = -EPROTO; + goto out_disable; + } + + switch (rsp.value) { + case DLN2_GPIO_DIRECTION_IN: + clear_bit(offset, dln2->output_enabled); + return 0; + case DLN2_GPIO_DIRECTION_OUT: + set_bit(offset, dln2->output_enabled); + return 0; + default: + ret = -EPROTO; + goto out_disable; + } + +out_disable: + dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); + return ret; +} + +static void dln2_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); +} + +static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + if (test_bit(offset, dln2->output_enabled)) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + int dir; + + dir = dln2_gpio_get_direction(chip, offset); + if (dir < 0) + return dir; + + if (dir == GPIOF_DIR_IN) + return dln2_gpio_pin_get_in_val(dln2, offset); + + return dln2_gpio_pin_get_out_val(dln2, offset); +} + +static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + dln2_gpio_pin_set_out_val(dln2, offset, value); +} + +static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, + unsigned dir) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + struct dln2_gpio_pin_val req = { + .pin = cpu_to_le16(offset), + .value = dir, + }; + int ret; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_DIRECTION, + &req, sizeof(req)); + if (ret < 0) + return ret; + + if (dir == DLN2_GPIO_DIRECTION_OUT) + set_bit(offset, dln2->output_enabled); + else + clear_bit(offset, dln2->output_enabled); + + return ret; +} + +static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_IN); +} + +static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT); +} + +static int dln2_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + __le32 duration = cpu_to_le32(debounce); + + return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_SET_DEBOUNCE, + &duration, sizeof(duration)); +} + +static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin, + unsigned type, unsigned period) +{ + struct { + __le16 pin; + u8 type; + __le16 period; + } __packed req = { + .pin = cpu_to_le16(pin), + .type = type, + .period = cpu_to_le16(period), + }; + + return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_EVENT_CFG, + &req, sizeof(req)); +} + +static void dln2_irq_work(struct work_struct *w) +{ + struct dln2_irq_work *iw = container_of(w, struct dln2_irq_work, work); + struct dln2_gpio *dln2 = iw->dln2; + u8 type = iw->type & DLN2_GPIO_EVENT_MASK; + + if (test_bit(iw->pin, dln2->irqs_enabled)) + dln2_gpio_set_event_cfg(dln2, iw->pin, type, 0); + else + dln2_gpio_set_event_cfg(dln2, iw->pin, DLN2_GPIO_EVENT_NONE, 0); +} + +static void dln2_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + set_bit(pin, dln2->irqs_enabled); + schedule_work(&dln2->irq_work[pin].work); +} + +static void dln2_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + clear_bit(pin, dln2->irqs_enabled); + schedule_work(&dln2->irq_work[pin].work); +} + +static void dln2_irq_mask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + set_bit(pin, dln2->irqs_masked); +} + +static void dln2_irq_unmask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + struct device *dev = dln2->gpio.dev; + int pin = irqd_to_hwirq(irqd); + + if (test_and_clear_bit(pin, dln2->irqs_pending)) { + int irq; + + irq = irq_find_mapping(dln2->gpio.irqdomain, pin); + if (!irq) { + dev_err(dev, "pin %d not mapped to IRQ\n", pin); + return; + } + + generic_handle_irq(irq); + } +} + +static int dln2_irq_set_type(struct irq_data *irqd, unsigned type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_LOW; + break; + case IRQ_TYPE_EDGE_BOTH: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE; + break; + case IRQ_TYPE_EDGE_RISING: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_FALLING; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct irq_chip dln2_gpio_irqchip = { + .name = "dln2-irq", + .irq_enable = dln2_irq_enable, + .irq_disable = dln2_irq_disable, + .irq_mask = dln2_irq_mask, + .irq_unmask = dln2_irq_unmask, + .irq_set_type = dln2_irq_set_type, +}; + +static void dln2_gpio_event(struct platform_device *pdev, u16 echo, + const void *data, int len) +{ + int pin, irq; + const struct { + __le16 count; + __u8 type; + __le16 pin; + __u8 value; + } __packed *event = data; + struct dln2_gpio *dln2 = platform_get_drvdata(pdev); + + if (len < sizeof(*event)) { + dev_err(dln2->gpio.dev, "short event message\n"); + return; + } + + pin = le16_to_cpu(event->pin); + if (pin >= dln2->gpio.ngpio) { + dev_err(dln2->gpio.dev, "out of bounds pin %d\n", pin); + return; + } + + irq = irq_find_mapping(dln2->gpio.irqdomain, pin); + if (!irq) { + dev_err(dln2->gpio.dev, "pin %d not mapped to IRQ\n", pin); + return; + } + + if (!test_bit(pin, dln2->irqs_enabled)) + return; + if (test_bit(pin, dln2->irqs_masked)) { + set_bit(pin, dln2->irqs_pending); + return; + } + + switch (dln2->irq_work[pin].type) { + case DLN2_GPIO_EVENT_CHANGE_RISING: + if (event->value) + generic_handle_irq(irq); + break; + case DLN2_GPIO_EVENT_CHANGE_FALLING: + if (!event->value) + generic_handle_irq(irq); + break; + default: + generic_handle_irq(irq); + } +} + +static int dln2_gpio_probe(struct platform_device *pdev) +{ + struct dln2_gpio *dln2; + struct device *dev = &pdev->dev; + int pins; + int i, ret; + + pins = dln2_gpio_get_pin_count(pdev); + if (pins < 0) { + dev_err(dev, "failed to get pin count: %d\n", pins); + return pins; + } + if (pins > DLN2_GPIO_MAX_PINS) { + pins = DLN2_GPIO_MAX_PINS; + dev_warn(dev, "clamping pins to %d\n", DLN2_GPIO_MAX_PINS); + } + + dln2 = devm_kzalloc(&pdev->dev, sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->irq_work = devm_kcalloc(&pdev->dev, pins, + sizeof(struct dln2_irq_work), GFP_KERNEL); + if (!dln2->irq_work) + return -ENOMEM; + for (i = 0; i < pins; i++) { + INIT_WORK(&dln2->irq_work[i].work, dln2_irq_work); + dln2->irq_work[i].pin = i; + dln2->irq_work[i].dln2 = dln2; + } + + dln2->pdev = pdev; + + dln2->gpio.label = "dln2"; + dln2->gpio.dev = dev; + dln2->gpio.owner = THIS_MODULE; + dln2->gpio.base = -1; + dln2->gpio.ngpio = pins; + dln2->gpio.exported = true; + dln2->gpio.can_sleep = true; + dln2->gpio.irq_not_threaded = true; + dln2->gpio.set = dln2_gpio_set; + dln2->gpio.get = dln2_gpio_get; + dln2->gpio.request = dln2_gpio_request; + dln2->gpio.free = dln2_gpio_free; + dln2->gpio.get_direction = dln2_gpio_get_direction; + dln2->gpio.direction_input = dln2_gpio_direction_input; + dln2->gpio.direction_output = dln2_gpio_direction_output; + dln2->gpio.set_debounce = dln2_gpio_set_debounce; + + platform_set_drvdata(pdev, dln2); + + ret = gpiochip_add(&dln2->gpio); + if (ret < 0) { + dev_err(dev, "failed to add gpio chip: %d\n", ret); + goto out; + } + + ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret < 0) { + dev_err(dev, "failed to add irq chip: %d\n", ret); + goto out_gpiochip_remove; + } + + ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV, + dln2_gpio_event); + if (ret) { + dev_err(dev, "failed to register event cb: %d\n", ret); + goto out_gpiochip_remove; + } + + return 0; + +out_gpiochip_remove: + gpiochip_remove(&dln2->gpio); +out: + return ret; +} + +static int dln2_gpio_remove(struct platform_device *pdev) +{ + struct dln2_gpio *dln2 = platform_get_drvdata(pdev); + int i; + + dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV); + for (i = 0; i < dln2->gpio.ngpio; i++) + flush_work(&dln2->irq_work[i].work); + gpiochip_remove(&dln2->gpio); + + return 0; +} + +static struct platform_driver dln2_gpio_driver = { + .driver.name = "dln2-gpio", + .probe = dln2_gpio_probe, + .remove = dln2_gpio_remove, +}; + +module_platform_driver(dln2_gpio_driver); + +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com"); +MODULE_DESCRIPTION("Driver for the Diolan DLN2 GPIO interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-gpio"); diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index ae0f6466eb09..abdcf58935f5 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -262,7 +262,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip = template_chip; tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; tc3589x_gpio->chip.dev = &pdev->dev; - tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1; + tc3589x_gpio->chip.base = -1; #ifdef CONFIG_OF_GPIO tc3589x_gpio->chip.of_node = np; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f0a1a56406eb..9cb5c95d5898 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4325,7 +4325,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ironlake_fdi_disable(crtc); ironlake_disable_pch_transcoder(dev_priv, pipe); - intel_set_pch_fifo_underrun_reporting(dev, pipe, true); if (HAS_PCH_CPT(dev)) { /* disable TRANS_DP_CTL */ @@ -4389,7 +4388,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) { lpt_disable_pch_transcoder(dev_priv); - intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); intel_ddi_fdi_disable(crtc); } @@ -9408,6 +9406,10 @@ static bool page_flip_finished(struct intel_crtc *crtc) struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + if (i915_reset_in_progress(&dev_priv->gpu_error) || + crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) + return true; + /* * The relevant registers doen't exist on pre-ctg. * As the flip done interrupt doesn't trigger for mmio diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5ad45bfff3fe..4bcd91757321 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4450,6 +4450,7 @@ static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) * vdd might still be enabled do to the delayed vdd off. * Make sure vdd is actually turned off here. */ + cancel_delayed_work_sync(&intel_dp->panel_vdd_work); pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); pps_unlock(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index a6bd1422e38f..c0bbf2172446 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -899,6 +899,17 @@ void intel_lvds_init(struct drm_device *dev) int pipe; u8 pin; + /* + * Unlock registers and just leave them unlocked. Do this before + * checking quirk lists to avoid bogus WARNINGs. + */ + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } if (!intel_lvds_supported(dev)) return; @@ -1097,17 +1108,6 @@ out: lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK; - /* - * Unlock registers and just - * leave them unlocked - */ - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_PP_CONTROL, - I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); - } else { - I915_WRITE(PP_CONTROL, - I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); - } lvds_connector->lid_notifier.notifier_call = intel_lid_notify; if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) { DRM_DEBUG_KMS("lid notifier registration failed\n"); diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index cd05677ad4b7..72a40f95d048 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -218,7 +218,6 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; - device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass; device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass; break; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index 5ae6a43893b5..1931057f9962 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -551,8 +551,8 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (status & 0x40000000) { - nouveau_fifo_uevent(&priv->base); nv_wr32(priv, 0x002100, 0x40000000); + nouveau_fifo_uevent(&priv->base); status &= ~0x40000000; } } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index 1fe1f8fbda0c..074d434c3077 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -740,6 +740,8 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) u32 inte = nv_rd32(priv, 0x002628); u32 unkn; + nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); + for (unkn = 0; unkn < 8; unkn++) { u32 ints = (intr >> (unkn * 0x04)) & inte; if (ints & 0x1) { @@ -751,8 +753,6 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) nv_mask(priv, 0x002628, ints, 0); } } - - nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); } static void diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index d2f0fd39c145..f8734eb74eaa 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -952,8 +952,8 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) } if (stat & 0x80000000) { - nve0_fifo_intr_engine(priv); nv_wr32(priv, 0x002100, 0x80000000); + nve0_fifo_intr_engine(priv); stat &= ~0x80000000; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 57238076049f..62b97c4eef8d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -629,7 +629,6 @@ int nouveau_pmops_suspend(struct device *dev) pci_save_state(pdev); pci_disable_device(pdev); - pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; } @@ -933,6 +932,7 @@ static int nouveau_pmops_runtime_suspend(struct device *dev) ret = nouveau_do_suspend(drm_dev, true); pci_save_state(pdev); pci_disable_device(pdev); + pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3cold); drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 515cd9aebb99..f32a434724e3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence) return container_of(fence->base.lock, struct nouveau_fence_chan, lock); } -static void +static int nouveau_fence_signal(struct nouveau_fence *fence) { + int drop = 0; + fence_signal_locked(&fence->base); list_del(&fence->head); + rcu_assign_pointer(fence->channel, NULL); if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); if (!--fctx->notify_ref) - nvif_notify_put(&fctx->notify); + drop = 1; } fence_put(&fence->base); + return drop; } static struct nouveau_fence * @@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence; - nvif_notify_fini(&fctx->notify); - spin_lock_irq(&fctx->lock); while (!list_empty(&fctx->pending)) { fence = list_entry(fctx->pending.next, typeof(*fence), head); - nouveau_fence_signal(fence); - fence->channel = NULL; + if (nouveau_fence_signal(fence)) + nvif_notify_put(&fctx->notify); } spin_unlock_irq(&fctx->lock); + + nvif_notify_fini(&fctx->notify); + fctx->dead = 1; + + /* + * Ensure that all accesses to fence->channel complete before freeing + * the channel. + */ + synchronize_rcu(); } static void @@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) kref_put(&fctx->fence_ref, nouveau_fence_context_put); } -static void +static int nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence; - + int drop = 0; u32 seq = fctx->read(chan); while (!list_empty(&fctx->pending)) { fence = list_entry(fctx->pending.next, typeof(*fence), head); if ((int)(seq - fence->base.seqno) < 0) - return; + break; - nouveau_fence_signal(fence); + drop |= nouveau_fence_signal(fence); } + + return drop; } static int @@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) struct nouveau_fence_chan *fctx = container_of(notify, typeof(*fctx), notify); unsigned long flags; + int ret = NVIF_NOTIFY_KEEP; spin_lock_irqsave(&fctx->lock, flags); if (!list_empty(&fctx->pending)) { struct nouveau_fence *fence; + struct nouveau_channel *chan; fence = list_entry(fctx->pending.next, typeof(*fence), head); - nouveau_fence_update(fence->channel, fctx); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (nouveau_fence_update(fence->channel, fctx)) + ret = NVIF_NOTIFY_DROP; } spin_unlock_irqrestore(&fctx->lock, flags); - /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */ - return NVIF_NOTIFY_KEEP; + return ret; } void @@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) if (!ret) { fence_get(&fence->base); spin_lock_irq(&fctx->lock); - nouveau_fence_update(chan, fctx); + + if (nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); + list_add_tail(&fence->head, &fctx->pending); spin_unlock_irq(&fctx->lock); } @@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence) if (fence->base.ops == &nouveau_fence_ops_legacy || fence->base.ops == &nouveau_fence_ops_uevent) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; unsigned long flags; if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) return true; spin_lock_irqsave(&fctx->lock, flags); - nouveau_fence_update(fence->channel, fctx); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (chan && nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); spin_unlock_irqrestore(&fctx->lock, flags); } return fence_is_signaled(&fence->base); @@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e if (fence && (!exclusive || !fobj || !fobj->shared_count)) { struct nouveau_channel *prev = NULL; + bool must_wait = true; f = nouveau_local_fence(fence, chan->drm); - if (f) - prev = f->channel; + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } - if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + if (must_wait) ret = fence_wait(fence, intr); return ret; @@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e for (i = 0; i < fobj->shared_count && !ret; ++i) { struct nouveau_channel *prev = NULL; + bool must_wait = true; fence = rcu_dereference_protected(fobj->shared[i], reservation_object_held(resv)); f = nouveau_local_fence(fence, chan->drm); - if (f) - prev = f->channel; + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } - if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + if (must_wait) ret = fence_wait(fence, intr); - - if (ret) - break; } return ret; @@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f) struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - return fence->channel ? fctx->name : "dead channel"; + return !fctx->dead ? fctx->name : "dead channel"; } /* @@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f) { struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - struct nouveau_channel *chan = fence->channel; + struct nouveau_channel *chan; + bool ret = false; + + rcu_read_lock(); + chan = rcu_dereference(fence->channel); + if (chan) + ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0; + rcu_read_unlock(); - return (int)(fctx->read(chan) - fence->base.seqno) >= 0; + return ret; } static bool nouveau_fence_no_signaling(struct fence *f) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 943b0b17b1fc..96e461c6f68f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -14,7 +14,7 @@ struct nouveau_fence { bool sysmem; - struct nouveau_channel *channel; + struct nouveau_channel __rcu *channel; unsigned long timeout; }; @@ -47,7 +47,7 @@ struct nouveau_fence_chan { char name[32]; struct nvif_notify notify; - int notify_ref; + int notify_ref, dead; }; struct nouveau_fence_priv { diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 300c4b3d4669..26baa9c05f6c 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -322,6 +322,12 @@ static void radeon_connector_get_edid(struct drm_connector *connector) } if (!radeon_connector->edid) { + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + return; + if (rdev->is_atom_bios) { /* some laptops provide a hardcoded edid in rom for LCDs */ if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) || @@ -826,6 +832,8 @@ static int radeon_lvds_mode_valid(struct drm_connector *connector, static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = radeon_best_single_encoder(connector); enum drm_connector_status ret = connector_status_disconnected; @@ -842,7 +850,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) /* check if panel is valid */ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) ret = connector_status_connected; - + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + ret = connector_status_disconnected; } /* check for edid as well */ @@ -1589,6 +1601,11 @@ radeon_dp_detect(struct drm_connector *connector, bool force) /* check if panel is valid */ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) ret = connector_status_connected; + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + ret = connector_status_disconnected; } /* eDP is always DP */ radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index a3e7aed7e680..6f377de099f9 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -251,22 +251,19 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority static int radeon_cs_sync_rings(struct radeon_cs_parser *p) { - int i, r = 0; + struct radeon_cs_reloc *reloc; + int r; - for (i = 0; i < p->nrelocs; i++) { + list_for_each_entry(reloc, &p->validated, tv.head) { struct reservation_object *resv; - if (!p->relocs[i].robj) - continue; - - resv = p->relocs[i].robj->tbo.resv; + resv = reloc->robj->tbo.resv; r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv, - p->relocs[i].tv.shared); - + reloc->tv.shared); if (r) - break; + return r; } - return r; + return 0; } /* XXX: note that this is called from the legacy UMS CS ioctl as well */ diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 7784911d78ef..00fc59762e0d 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -185,6 +185,16 @@ static bool radeon_msi_ok(struct radeon_device *rdev) if (rdev->flags & RADEON_IS_AGP) return false; + /* + * Older chips have a HW limitation, they can only generate 40 bits + * of address for "64-bit" MSIs which breaks on some platforms, notably + * IBM POWER servers, so we limit them + */ + if (rdev->family < CHIP_BONAIRE) { + dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n"); + rdev->pdev->no_64bit_msi = 1; + } + /* force MSI on */ if (radeon_msi == 1) return true; diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 8309b11e674d..03586763ee86 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -795,6 +795,8 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, /* Get associated drm_crtc: */ drmcrtc = &rdev->mode_info.crtcs[crtc]->base; + if (!drmcrtc) + return -EINVAL; /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 99a960a4f302..4c0d786d5c7a 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -213,6 +213,13 @@ int radeon_bo_create(struct radeon_device *rdev, if (!(rdev->flags & RADEON_IS_PCIE)) bo->flags &= ~(RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC); +#ifdef CONFIG_X86_32 + /* XXX: Write-combined CPU mappings of GTT seem broken on 32-bit + * See https://bugs.freedesktop.org/show_bug.cgi?id=84627 + */ + bo->flags &= ~RADEON_GEM_GTT_WC; +#endif + radeon_ttm_placement_from_domain(bo, domain); /* Kernel allocation are uninterruptible */ down_read(&rdev->pm.mclk_lock); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index e6d8e18dae97..6a58b6c723aa 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -641,9 +641,6 @@ static int sensor_hub_probe(struct hid_device *hdev, goto err_stop_hw; } sd->hid_sensor_hub_client_devs[ - sd->hid_sensor_client_cnt].id = - PLATFORM_DEVID_AUTO; - sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].name = name; sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].platform_data = @@ -659,8 +656,9 @@ static int sensor_hub_probe(struct hid_device *hdev, if (last_hsdev) last_hsdev->end_collection_index = i; - ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, - sd->hid_sensor_client_cnt, NULL, 0, NULL); + ret = mfd_add_hotplug_devices(&hdev->dev, + sd->hid_sensor_hub_client_devs, + sd->hid_sensor_client_cnt); if (ret < 0) goto err_stop_hw; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5286d7ce1f9e..6529c09c46f0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1028,11 +1028,11 @@ config SENSORS_LM93 will be called lm93. config SENSORS_LM95234 - tristate "National Semiconductor LM95234" + tristate "National Semiconductor LM95234 and compatibles" depends on I2C help - If you say yes here you get support for the LM95234 temperature - sensor. + If you say yes here you get support for the LM95233 and LM95234 + temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm95234. @@ -1048,10 +1048,11 @@ config SENSORS_LM95241 will be called lm95241. config SENSORS_LM95245 - tristate "National Semiconductor LM95245 sensor chip" + tristate "National Semiconductor LM95245 and compatibles" depends on I2C help - If you say yes here you get support for LM95245 sensor chip. + If you say yes here you get support for LM95235 and LM95245 + temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm95245. @@ -1117,12 +1118,23 @@ config SENSORS_NCT6775 help If you say yes here you get support for the hardware monitoring functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, - NCT6791D and compatible Super-I/O chips. This driver replaces the - w83627ehf driver for NCT6775F and NCT6776F. + NCT6791D, NCT6792D and compatible Super-I/O chips. This driver + replaces the w83627ehf driver for NCT6775F and NCT6776F. This driver can also be built as a module. If so, the module will be called nct6775. +config SENSORS_NCT7802 + tristate "Nuvoton NCT7802Y" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7802Y + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called nct7802. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C @@ -1454,7 +1466,7 @@ config SENSORS_TMP401 depends on I2C help If you say yes here you get support for Texas Instruments TMP401, - TMP411, TMP431, and TMP432 temperature sensor chips. + TMP411, TMP431, TMP432 and TMP435 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c90a7611efaa..67280643bcf0 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o +obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 6aac695b1688..9b55e673b67c 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -1084,10 +1084,8 @@ static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) goto clock_dis; - data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, - data, - g762_groups); + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, g762_groups); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); goto clock_dis; diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 4efa1734bdad..36abf814b8c7 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -79,7 +79,7 @@ static ssize_t show_fan_alarm(struct device *dev, { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); struct gpio_fan_alarm *alarm = fan_data->alarm; - int value = gpio_get_value(alarm->gpio); + int value = gpio_get_value_cansleep(alarm->gpio); if (alarm->active_low) value = !value; @@ -131,7 +131,7 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) int i; for (i = 0; i < fan_data->num_ctrl; i++) - gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); + gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1); } static int __get_fan_ctrl(struct gpio_fan_data *fan_data) @@ -142,7 +142,7 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data) for (i = 0; i < fan_data->num_ctrl; i++) { int value; - value = gpio_get_value(fan_data->ctrl[i]); + value = gpio_get_value_cansleep(fan_data->ctrl[i]); ctrl_val |= (value << i); } return ctrl_val; @@ -369,7 +369,8 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, if (err) return err; - err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + err = gpio_direction_output(ctrl[i], + gpio_get_value_cansleep(ctrl[i])); if (err) return err; } @@ -549,6 +550,14 @@ static int gpio_fan_probe(struct platform_device *pdev) return 0; } +static void gpio_fan_shutdown(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, 0); +} + #ifdef CONFIG_PM_SLEEP static int gpio_fan_suspend(struct device *dev) { @@ -580,6 +589,7 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, + .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", .pm = GPIO_FAN_PM, diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 6a30eeea94be..7c2c7be182f2 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -74,9 +74,6 @@ struct platform_data { u32 sensors_count; /* Total count of sensors from each group */ }; -/* Platform device representing all the ibmpowernv sensors */ -static struct platform_device *pdevice; - static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -99,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%u\n", x); } -static int __init get_sensor_index_attr(const char *name, u32 *index, +static int get_sensor_index_attr(const char *name, u32 *index, char *attr) { char *hash_pos = strchr(name, '#'); @@ -136,7 +133,7 @@ static int __init get_sensor_index_attr(const char *name, u32 *index, * which need to be mapped as fan2_input, temp1_max respectively before * populating them inside hwmon device class. */ -static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, +static int create_hwmon_attr_name(struct device *dev, enum sensors type, const char *node_name, char *hwmon_attr_name) { @@ -172,7 +169,7 @@ static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, return 0; } -static int __init populate_attr_groups(struct platform_device *pdev) +static int populate_attr_groups(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; @@ -180,11 +177,6 @@ static int __init populate_attr_groups(struct platform_device *pdev) enum sensors type; opal = of_find_node_by_path("/ibm,opal/sensors"); - if (!opal) { - dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n"); - return -ENODEV; - } - for_each_child_of_node(opal, np) { if (np->name == NULL) continue; @@ -221,7 +213,7 @@ static int __init populate_attr_groups(struct platform_device *pdev) * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max * etc.. */ -static int __init create_device_attrs(struct platform_device *pdev) +static int create_device_attrs(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; @@ -280,7 +272,7 @@ exit_put_node: return err; } -static int __init ibmpowernv_probe(struct platform_device *pdev) +static int ibmpowernv_probe(struct platform_device *pdev) { struct platform_data *pdata; struct device *hwmon_dev; @@ -309,57 +301,25 @@ static int __init ibmpowernv_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } -static struct platform_driver ibmpowernv_driver = { - .driver = { - .owner = THIS_MODULE, - .name = DRVNAME, +static const struct platform_device_id opal_sensor_driver_ids[] = { + { + .name = "opal-sensor", }, + { } }; +MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); -static int __init ibmpowernv_init(void) -{ - int err; - - pdevice = platform_device_alloc(DRVNAME, 0); - if (!pdevice) { - pr_err("Device allocation failed\n"); - err = -ENOMEM; - goto exit; - } - - err = platform_device_add(pdevice); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_put; - } - - err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe); - if (err) { - if (err != -ENODEV) - pr_err("Platform driver probe failed (%d)\n", err); - - goto exit_device_del; - } - - return 0; - -exit_device_del: - platform_device_del(pdevice); -exit_device_put: - platform_device_put(pdevice); -exit: - return err; -} +static struct platform_driver ibmpowernv_driver = { + .probe = ibmpowernv_probe, + .id_table = opal_sensor_driver_ids, + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, +}; -static void __exit ibmpowernv_exit(void) -{ - platform_driver_unregister(&ibmpowernv_driver); - platform_device_unregister(pdevice); -} +module_platform_driver(ibmpowernv_driver); MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); MODULE_DESCRIPTION("IBM POWERNV platform sensors"); MODULE_LICENSE("GPL"); - -module_init(ibmpowernv_init); -module_exit(ibmpowernv_exit); diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 14c82daab019..980175628563 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -63,7 +63,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) struct iio_hwmon_state *st; struct sensor_device_attribute *a; int ret, i; - int in_i = 1, temp_i = 1, curr_i = 1; + int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; enum iio_chan_type type; struct iio_channel *channels; const char *name = "iio_hwmon"; @@ -123,6 +123,11 @@ static int iio_hwmon_probe(struct platform_device *pdev) "curr%d_input", curr_i++); break; + case IIO_HUMIDITYRELATIVE: + a->dev_attr.attr.name = kasprintf(GFP_KERNEL, + "humidity%d_input", + humidity_i++); + break; default: ret = -EINVAL; goto error_release_channels; diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index bfd3f3eeabcd..e01feba909c3 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -223,6 +223,7 @@ static int ina2xx_probe(struct i2c_client *client, struct device *hwmon_dev; long shunt = 10000; /* default shunt value 10mOhms */ u32 val; + int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -247,12 +248,25 @@ static int ina2xx_probe(struct i2c_client *client, data->config = &ina2xx_config[data->kind]; /* device configuration */ - i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->config->config_default); - /* set current LSB to 1mA, shunt is in uOhms */ - /* (equation 13 in datasheet) */ - i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, - data->config->calibration_factor / shunt); + ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, + data->config->config_default); + if (ret < 0) { + dev_err(dev, + "error writing to the config register: %d", ret); + return -ENODEV; + } + + /* + * Set current LSB to 1mA, shunt is in uOhms + * (equation 13 in datasheet). + */ + ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, + data->config->calibration_factor / shunt); + if (ret < 0) { + dev_err(dev, + "error writing to the calibration register: %d", ret); + return -ENODEV; + } data->client = client; mutex_init(&data->update_lock); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index d16dbb33a531..6753fd940c76 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -44,6 +44,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ g751, lm75, lm75a, + lm75b, max6625, max6626, mcp980x, @@ -233,6 +234,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->resolution = 9; data->sample_time = HZ / 2; break; + case lm75b: + data->resolution = 11; + data->sample_time = HZ / 4; + break; case max6625: data->resolution = 9; data->sample_time = HZ / 4; @@ -322,6 +327,7 @@ static const struct i2c_device_id lm75_ids[] = { { "g751", g751, }, { "lm75", lm75, }, { "lm75a", lm75a, }, + { "lm75b", lm75b, }, { "max6625", max6625, }, { "max6626", max6626, }, { "mcp980x", mcp980x, }, @@ -409,6 +415,12 @@ static int lm75_detect(struct i2c_client *new_client, || i2c_smbus_read_byte_data(new_client, 7) != os) return -ENODEV; } + /* + * It is very unlikely that this is a LM75 if both + * hysteresis and temperature limit registers are 0. + */ + if (hyst == 0 && os == 0) + return -ENODEV; /* Addresses cycling */ for (i = 8; i <= 248; i += 40) { diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 411202bdaf6b..8796de39ff9b 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -1,7 +1,7 @@ /* * Driver for Texas Instruments / National Semiconductor LM95234 * - * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net> + * Copyright (c) 2013, 2014 Guenter Roeck <linux@roeck-us.net> * * Derived from lm95241.c * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> @@ -30,7 +30,10 @@ #define DRVNAME "lm95234" -static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; +enum chips { lm95233, lm95234 }; + +static const unsigned short normal_i2c[] = { + 0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END }; /* LM95234 registers */ #define LM95234_REG_MAN_ID 0xFE @@ -53,11 +56,13 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; #define LM95234_REG_TCRIT_HYST 0x5a #define NATSEMI_MAN_ID 0x01 +#define LM95233_CHIP_ID 0x89 #define LM95234_CHIP_ID 0x79 /* Client data (each client gets its own) */ struct lm95234_data { struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; unsigned long last_updated, interval; /* in jiffies */ bool valid; /* false until following fields are valid */ @@ -564,35 +569,23 @@ static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95234_attrs[] = { +static struct attribute *lm95234_common_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp3_type.dev_attr.attr, - &sensor_dev_attr_temp4_type.dev_attr.attr, - &sensor_dev_attr_temp5_type.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_crit.dev_attr.attr, &sensor_dev_attr_temp3_crit.dev_attr.attr, &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, @@ -601,18 +594,44 @@ static struct attribute *lm95234_attrs[] = { &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp3_offset.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static const struct attribute_group lm95234_common_group = { + .attrs = lm95234_common_attrs, +}; + +static struct attribute *lm95234_attrs[] = { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp4_type.dev_attr.attr, + &sensor_dev_attr_temp5_type.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, &sensor_dev_attr_temp4_offset.dev_attr.attr, &sensor_dev_attr_temp5_offset.dev_attr.attr, - &dev_attr_update_interval.attr, NULL }; -ATTRIBUTE_GROUPS(lm95234); + +static const struct attribute_group lm95234_group = { + .attrs = lm95234_attrs, +}; static int lm95234_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int address = client->addr; + u8 config_mask, model_mask; int mfg_id, chip_id, val; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -622,15 +641,31 @@ static int lm95234_detect(struct i2c_client *client, return -ENODEV; chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID); - if (chip_id != LM95234_CHIP_ID) + switch (chip_id) { + case LM95233_CHIP_ID: + if (address != 0x18 && address != 0x2a && address != 0x2b) + return -ENODEV; + config_mask = 0xbf; + model_mask = 0xf9; + name = "lm95233"; + break; + case LM95234_CHIP_ID: + if (address != 0x18 && address != 0x4d && address != 0x4e) + return -ENODEV; + config_mask = 0xbc; + model_mask = 0xe1; + name = "lm95234"; + break; + default: return -ENODEV; + } val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS); if (val & 0x30) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); - if (val & 0xbc) + if (val & config_mask) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); @@ -638,14 +673,14 @@ static int lm95234_detect(struct i2c_client *client, return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); - if (val & 0xe1) + if (val & model_mask) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); - if (val & 0xe1) + if (val & model_mask) return -ENODEV; - strlcpy(info->type, "lm95234", I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -698,15 +733,19 @@ static int lm95234_probe(struct i2c_client *client, if (err < 0) return err; + data->groups[0] = &lm95234_common_group; + if (id->driver_data == lm95234) + data->groups[1] = &lm95234_group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - lm95234_groups); + data, data->groups); return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ static const struct i2c_device_id lm95234_id[] = { - { "lm95234", 0 }, + { "lm95233", lm95233 }, + { "lm95234", lm95234 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95234_id); @@ -725,5 +764,5 @@ static struct i2c_driver lm95234_driver = { module_i2c_driver(lm95234_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("LM95234 sensor driver"); +MODULE_DESCRIPTION("LM95233/LM95234 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 0ae0dfdafdff..e7aef4561c83 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -1,10 +1,8 @@ /* * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com> * - * The LM95245 is a sensor chip made by National Semiconductors. + * The LM95245 is a sensor chip made by TI / National Semiconductor. * It reports up to two temperatures (its own plus an external one). - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/ds.cgi/LM/LM95245.pdf * * This driver is based on lm95241.c * @@ -34,8 +32,6 @@ #include <linux/mutex.h> #include <linux/sysfs.h> -#define DEVNAME "lm95245" - static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; @@ -98,7 +94,8 @@ static const unsigned short normal_i2c[] = { #define STATUS1_LOC 0x01 #define MANUFACTURER_ID 0x01 -#define DEFAULT_REVISION 0xB3 +#define LM95235_REVISION 0xB1 +#define LM95245_REVISION 0xB3 static const u8 lm95245_reg_address[] = { LM95245_REG_R_LOCAL_TEMPH_S, @@ -427,17 +424,32 @@ static int lm95245_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + int address = new_client->addr; + const char *name; + int rev, id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID) - != MANUFACTURER_ID - || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID) - != DEFAULT_REVISION) + id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID); + if (id != MANUFACTURER_ID) return -ENODEV; - strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); + rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID); + switch (rev) { + case LM95235_REVISION: + if (address != 0x18 && address != 0x29 && address != 0x4c) + return -ENODEV; + name = "lm95235"; + break; + case LM95245_REVISION: + name = "lm95245"; + break; + default: + return -ENODEV; + } + + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -484,7 +496,8 @@ static int lm95245_probe(struct i2c_client *client, /* Driver data (common to all clients) */ static const struct i2c_device_id lm95245_id[] = { - { DEVNAME, 0 }, + { "lm95235", 0 }, + { "lm95245", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95245_id); @@ -492,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, lm95245_id); static struct i2c_driver lm95245_driver = { .class = I2C_CLASS_HWMON, .driver = { - .name = DEVNAME, + .name = "lm95245", }, .probe = lm95245_probe, .id_table = lm95245_id, @@ -503,5 +516,5 @@ static struct i2c_driver lm95245_driver = { module_i2c_driver(lm95245_driver); MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>"); -MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_DESCRIPTION("LM95235/LM95245 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 504cbddbdd90..dc0df57200cd 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -38,6 +38,7 @@ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 + * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -61,7 +62,7 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -70,6 +71,7 @@ static const char * const nct6775_device_names[] = { "nct6776", "nct6779", "nct6791", + "nct6792", }; static unsigned short force_id; @@ -100,6 +102,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 #define SIO_NCT6791_ID 0xc800 +#define SIO_NCT6792_ID 0xc910 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -529,6 +532,12 @@ static const s8 NCT6791_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ +/* NCT6792 specific data */ + +static const u16 NCT6792_REG_TEMP_MON[] = { + 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d }; +static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = { + 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; /* NCT6102D/NCT6106D specific data */ @@ -1043,13 +1052,14 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) reg == 0x73 || reg == 0x75 || reg == 0x77; case nct6779: case nct6791: + case nct6792: return reg == 0x150 || reg == 0x153 || reg == 0x155 || ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || reg == 0x63a || reg == 0x63c || reg == 0x63e || reg == 0x640 || reg == 0x642 || reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || - reg == 0x7b; + reg == 0x7b || reg == 0x7d; } return false; } @@ -1063,6 +1073,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) { u8 bank = reg >> 8; + if (data->bank != bank) { outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); outb_p(bank, data->addr + DATA_REG_OFFSET); @@ -1300,6 +1311,7 @@ static void nct6775_update_pwm(struct device *dev) if (!data->target_speed_tolerance[i] || data->pwm_enable[i] == speed_cruise) { u8 t = fanmodecfg & 0x0f; + if (data->REG_TOLERANCE_H) { t |= (nct6775_read_value(data, data->REG_TOLERANCE_H[i]) & 0x70) >> 1; @@ -1391,6 +1403,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6106: case nct6779: case nct6791: + case nct6792: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -1473,6 +1486,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->alarms = 0; for (i = 0; i < NUM_REG_ALARM; i++) { u8 alarm; + if (!data->REG_ALARM[i]) continue; alarm = nct6775_read_value(data, data->REG_ALARM[i]); @@ -1482,6 +1496,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->beeps = 0; for (i = 0; i < NUM_REG_BEEP; i++) { u8 beep; + if (!data->REG_BEEP[i]) continue; beep = nct6775_read_value(data, data->REG_BEEP[i]); @@ -1504,8 +1519,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); } @@ -1515,10 +1531,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, { struct nct6775_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; unsigned long val; - int err = kstrtoul(buf, 10, &val); + int err; + + err = kstrtoul(buf, 10, &val); if (err < 0) return err; mutex_lock(&data->update_lock); @@ -1535,6 +1553,7 @@ show_alarm(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = data->ALARM_BITS[sattr->index]; + return sprintf(buf, "%u\n", (unsigned int)((data->alarms >> nr) & 0x01)); } @@ -1570,6 +1589,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_alarms); if (nr >= 0) { int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE]; + alarm = (data->alarms >> bit) & 0x01; } return sprintf(buf, "%u\n", alarm); @@ -1595,8 +1615,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf, int nr = data->BEEP_BITS[sattr->index]; int regindex = nr >> 3; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1629,6 +1650,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_beeps); if (nr >= 0) { int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + beep = (data->beeps >> bit) & 0x01; } return sprintf(buf, "%u\n", beep); @@ -1642,8 +1664,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, struct nct6775_data *data = dev_get_drvdata(dev); int nr, bit, regindex; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1715,6 +1738,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); } @@ -1724,6 +1748,7 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->fan_from_reg_min(data->fan_min[nr], data->fan_div[nr])); @@ -1735,6 +1760,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); } @@ -1746,9 +1772,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; unsigned long val; - int err; unsigned int reg; u8 new_div; + int err; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -1932,6 +1958,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } @@ -2008,6 +2035,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); } @@ -2790,6 +2818,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6106: case nct6779: case nct6791: + case nct6792: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -2997,6 +3026,7 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } @@ -3202,7 +3232,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = false; pwm5pin = false; pwm6pin = false; - } else { /* NCT6779D or NCT6791D */ + } else { /* NCT6779D, NCT6791D, or NCT6792D */ regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); @@ -3215,7 +3245,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792) { regval = superio_inb(sioreg, 0x2d); fan6pin = (regval & (1 << 1)); pwm6pin = (regval & (1 << 0)); @@ -3588,6 +3618,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: + case nct6792: data->in_num = 15; data->pwm_num = 6; data->auto_pwm_num = 4; @@ -3650,12 +3681,20 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL; data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6791_REG_ALARM; - data->REG_BEEP = NCT6776_REG_BEEP; + if (data->kind == nct6791) + data->REG_BEEP = NCT6776_REG_BEEP; + else + data->REG_BEEP = NCT6792_REG_BEEP; reg_temp = NCT6779_REG_TEMP; - reg_temp_mon = NCT6779_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); - num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + if (data->kind == nct6791) { + reg_temp_mon = NCT6779_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + } else { + reg_temp_mon = NCT6792_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON); + } reg_temp_over = NCT6779_REG_TEMP_OVER; reg_temp_hyst = NCT6779_REG_TEMP_HYST; reg_temp_config = NCT6779_REG_TEMP_CONFIG; @@ -3854,6 +3893,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6106: case nct6779: case nct6791: + case nct6792: break; } @@ -3885,6 +3925,7 @@ static int nct6775_probe(struct platform_device *pdev) tmp |= 0x3e; break; case nct6791: + case nct6792: tmp |= 0x7e; break; } @@ -3972,7 +4013,7 @@ static int nct6775_resume(struct device *dev) mutex_lock(&data->update_lock); data->bank = 0xff; /* Force initial bank selection */ - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792) { err = superio_enter(data->sioreg); if (err) goto abort; @@ -4052,6 +4093,7 @@ static const char * const nct6775_sio_names[] __initconst = { "NCT6776D/F", "NCT6779D", "NCT6791D", + "NCT6792D", }; /* nct6775_find() looks for a '627 in the Super-I/O config space */ @@ -4086,6 +4128,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6791_ID: sio_data->kind = nct6791; break; + case SIO_NCT6792_ID: + sio_data->kind = nct6792; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4111,7 +4156,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } - if (sio_data->kind == nct6791) + if (sio_data->kind == nct6791 || sio_data->kind == nct6792) nct6791_enable_io_mapping(sioaddr); superio_exit(sioaddr); @@ -4221,7 +4266,7 @@ static void __exit sensors_nct6775_exit(void) } MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D driver"); +MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver"); MODULE_LICENSE("GPL"); module_init(sensors_nct6775_init); diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c new file mode 100644 index 000000000000..ec5678289e4a --- /dev/null +++ b/drivers/hwmon/nct7802.c @@ -0,0 +1,860 @@ +/* + * nct7802 - Driver for Nuvoton NCT7802Y + * + * Copyright (C) 2014 Guenter Roeck <linux@roeck-us.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define DRVNAME "nct7802" + +static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e }; + +static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = { + { 0x40, 0x00, 0x42, 0x44, 0x46 }, + { 0x3f, 0x00, 0x41, 0x43, 0x45 }, +}; + +static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 }; + +static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { + { 0, 0, 4, 0, 4 }, + { 2, 0, 6, 2, 6 }, +}; + +#define REG_BANK 0x00 +#define REG_TEMP_LSB 0x05 +#define REG_TEMP_PECI_LSB 0x08 +#define REG_VOLTAGE_LOW 0x0f +#define REG_FANCOUNT_LOW 0x13 +#define REG_START 0x21 +#define REG_MODE 0x22 +#define REG_PECI_ENABLE 0x23 +#define REG_FAN_ENABLE 0x24 +#define REG_VMON_ENABLE 0x25 +#define REG_VENDOR_ID 0xfd +#define REG_CHIP_ID 0xfe +#define REG_VERSION_ID 0xff + +/* + * Data structures and manipulation thereof + */ + +struct nct7802_data { + struct regmap *regmap; + struct mutex access_lock; /* for multi-byte read and write operations */ +}; + +static int nct7802_read_temp(struct nct7802_data *data, + u8 reg_temp, u8 reg_temp_low, int *temp) +{ + unsigned int t1, t2 = 0; + int err; + + *temp = 0; + + mutex_lock(&data->access_lock); + err = regmap_read(data->regmap, reg_temp, &t1); + if (err < 0) + goto abort; + t1 <<= 8; + if (reg_temp_low) { /* 11 bit data */ + err = regmap_read(data->regmap, reg_temp_low, &t2); + if (err < 0) + goto abort; + } + t1 |= t2 & 0xe0; + *temp = (s16)t1 / 32 * 125; +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2); + if (ret < 0) + goto abort; + ret = (f1 << 5) | (f2 >> 3); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume fan is stopped */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, + u8 reg_fan_high) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan_low, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, reg_fan_high, &f2); + if (ret < 0) + goto abort; + ret = f1 | ((f2 & 0xf8) << 5); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume no limit */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, + u8 reg_fan_high, unsigned int limit) +{ + int err; + + if (limit) + limit = DIV_ROUND_CLOSEST(1350000U, limit); + else + limit = 0x1fff; + limit = clamp_val(limit, 0, 0x1fff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, reg_fan_low, limit & 0xff); + if (err < 0) + goto abort; + + err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 }; + +static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) +{ + unsigned int v1, v2; + int ret; + + mutex_lock(&data->access_lock); + if (index == 0) { /* voltage */ + ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); + if (ret < 0) + goto abort; + ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr]; + } else { /* limit */ + int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + + ret = regmap_read(data->regmap, + REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], + &v2); + if (ret < 0) + goto abort; + ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr]; + } +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, + unsigned int voltage) +{ + int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + int err; + + voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); + voltage = clamp_val(voltage, 0, 0x3ff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, + REG_VOLTAGE_LIMIT_LSB[index - 1][nr], + voltage & 0xff); + if (err < 0) + goto abort; + + err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], + 0x0300 >> shift, (voltage & 0x0300) >> shift); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int voltage; + + voltage = nct7802_read_voltage(data, sattr->nr, sattr->index); + if (voltage < 0) + return voltage; + + return sprintf(buf, "%d\n", voltage); +} + +static ssize_t store_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int index = sattr->index; + int nr = sattr->nr; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7802_write_voltage(data, nr, index, val); + return err ? : count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int err, temp; + + err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t store_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int nr = sattr->nr; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + + err = regmap_write(data->regmap, nr, val & 0xff); + return err ? : count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7802_read_fan(data, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7802_read_fan_min(data, sattr->nr, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val); + return err ? : count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int bit = sattr->index; + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, sattr->nr, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", !!(val & (1 << bit))); +} + +static ssize_t +show_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + err = regmap_read(data->regmap, sattr->nr, ®val); + if (err) + return err; + + return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index))); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index, + val ? 1 << sattr->index : 0); + return err ? : count; +} + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x31, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x30, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3a, 0); + +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x33, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x32, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3b, 0); + +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x35, 0); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x34, 0); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3c, 0); + +static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 0x04, 0); +static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x37, 0); +static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x36, 0); +static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3d, 0); + +static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 0x06, + REG_TEMP_PECI_LSB); +static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x39, 0); +static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x38, 0); +static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3e, 0); + +static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 0x07, + REG_TEMP_PECI_LSB); + +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 0); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 1); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 2); +static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 3); +static SENSOR_DEVICE_ATTR_2(temp5_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 1); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 2); +static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 3); +static SENSOR_DEVICE_ATTR_2(temp5_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 1); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 2); +static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 3); +static SENSOR_DEVICE_ATTR_2(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, 0x17, 0); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, 0x17, 1); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, 0x17, 2); + +static SENSOR_DEVICE_ATTR_2(temp1_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 0); +static SENSOR_DEVICE_ATTR_2(temp2_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 1); +static SENSOR_DEVICE_ATTR_2(temp3_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 2); +static SENSOR_DEVICE_ATTR_2(temp4_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 3); +static SENSOR_DEVICE_ATTR_2(temp5_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 4); +static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 5); + +static struct attribute *nct7802_temp_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp1_beep.dev_attr.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */ + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + + &sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */ + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, + + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */ + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_beep.dev_attr.attr, + + &sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */ + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_beep.dev_attr.attr, + + &sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */ + &sensor_dev_attr_temp6_beep.dev_attr.attr, + + NULL +}; + +static umode_t nct7802_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index < 9 && + (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */ + return 0; + if (index >= 9 && index < 18 && + (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */ + return 0; + if (index >= 18 && index < 27 && (reg & 0x30) != 0x10) /* RD3 */ + return 0; + if (index >= 27 && index < 35) /* local */ + return attr->mode; + + err = regmap_read(data->regmap, REG_PECI_ENABLE, ®); + if (err < 0) + return 0; + + if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */ + return 0; + + if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */ + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_temp_group = { + .attrs = nct7802_temp_attrs, + .is_visible = nct7802_temp_is_visible, +}; + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, store_in, + 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, store_in, + 0, 2); +static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 3); +static SENSOR_DEVICE_ATTR_2(in0_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 3); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, store_in, + 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, store_in, + 2, 2); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 0); +static SENSOR_DEVICE_ATTR_2(in2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 0); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, store_in, + 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, store_in, + 3, 2); +static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 1); +static SENSOR_DEVICE_ATTR_2(in3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 1); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, store_in, + 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, store_in, + 4, 2); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 2); +static SENSOR_DEVICE_ATTR_2(in4_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 2); + +static struct attribute *nct7802_in_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in0_beep.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */ + + &sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */ + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in2_beep.dev_attr.attr, + + &sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */ + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in3_beep.dev_attr.attr, + + &sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */ + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in4_beep.dev_attr.attr, + + NULL, +}; + +static umode_t nct7802_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + if (index < 6) /* VCC, VCORE */ + return attr->mode; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */ + return 0; + if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */ + return 0; + if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */ + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_in_group = { + .attrs = nct7802_in_attrs, + .is_visible = nct7802_in_is_visible, +}; + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0x10); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x49, 0x4c); +static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 0); +static SENSOR_DEVICE_ATTR_2(fan1_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 0x11); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x4a, 0x4d); +static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 1); +static SENSOR_DEVICE_ATTR_2(fan2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 0x12); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x4b, 0x4e); +static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2); +static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 2); + +static struct attribute *nct7802_fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_beep.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_beep.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_beep.dev_attr.attr, + + NULL +}; + +static umode_t nct7802_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + int fan = index / 4; /* 4 attributes per fan */ + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_FAN_ENABLE, ®); + if (err < 0 || !(reg & (1 << fan))) + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_fan_group = { + .attrs = nct7802_fan_attrs, + .is_visible = nct7802_fan_is_visible, +}; + +static const struct attribute_group *nct7802_groups[] = { + &nct7802_temp_group, + &nct7802_in_group, + &nct7802_fan_group, + NULL +}; + +static int nct7802_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int reg; + + /* + * Chip identification registers are only available in bank 0, + * so only attempt chip detection if bank 0 is selected + */ + reg = i2c_smbus_read_byte_data(client, REG_BANK); + if (reg != 0x00) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID); + if (reg != 0x50) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID); + if (reg != 0xc3) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID); + if (reg < 0 || (reg & 0xf0) != 0x20) + return -ENODEV; + + /* Also validate lower bits of voltage and temperature registers */ + reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB); + if (reg < 0 || (reg & 0x1f)) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB); + if (reg < 0 || (reg & 0x3f)) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW); + if (reg < 0 || (reg & 0x3f)) + return -ENODEV; + + strlcpy(info->type, "nct7802", I2C_NAME_SIZE); + return 0; +} + +static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg != REG_BANK && reg <= 0x20; +} + +static struct regmap_config nct7802_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = nct7802_regmap_is_volatile, +}; + +static int nct7802_init_chip(struct nct7802_data *data) +{ + int err; + + /* Enable ADC */ + err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01); + if (err) + return err; + + /* Enable local temperature sensor */ + err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40); + if (err) + return err; + + /* Enable Vcore and VCC voltage monitoring */ + return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03); +} + +static int nct7802_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct nct7802_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + mutex_init(&data->access_lock); + + ret = nct7802_init_chip(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + nct7802_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const unsigned short nct7802_address_list[] = { + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; + +static const struct i2c_device_id nct7802_idtable[] = { + { "nct7802", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct7802_idtable); + +static struct i2c_driver nct7802_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .detect = nct7802_detect, + .probe = nct7802_probe, + .id_table = nct7802_idtable, + .address_list = nct7802_address_list, +}; + +module_i2c_driver(nct7802_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 6e1e4935fc62..a674cd83a4e2 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -47,15 +47,22 @@ config SENSORS_LM25066 be called lm25066. config SENSORS_LTC2978 - tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883" + tristate "Linear Technologies LTC2978 and compatibles" default n help If you say yes here you get hardware monitoring support for Linear - Technology LTC2974, LTC2978, LTC3880, and LTC3883. + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. This driver can also be built as a module. If so, the module will be called ltc2978. +config SENSORS_LTC2978_REGULATOR + boolean "Regulator support for LTC2978 and compatibles" + depends on SENSORS_LTC2978 && REGULATOR + help + If you say yes here you get regulator support for Linear + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index e24ed521051a..0835050ec245 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -22,6 +22,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/regulator/driver.h> #include "pmbus.h" enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; @@ -374,6 +375,19 @@ static const struct i2c_device_id ltc2978_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); +#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) +static const struct regulator_desc ltc2978_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), + PMBUS_REGULATOR("vout", 1), + PMBUS_REGULATOR("vout", 2), + PMBUS_REGULATOR("vout", 3), + PMBUS_REGULATOR("vout", 4), + PMBUS_REGULATOR("vout", 5), + PMBUS_REGULATOR("vout", 6), + PMBUS_REGULATOR("vout", 7), +}; +#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */ + static int ltc2978_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -487,13 +501,36 @@ static int ltc2978_probe(struct i2c_client *client, default: return -ENODEV; } + +#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) + info->num_regulators = info->pages; + info->reg_desc = ltc2978_reg_desc; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { + dev_err(&client->dev, "num_regulators too large!"); + info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + } +#endif + return pmbus_do_probe(client, id, info); } -/* This is the driver that will be inserted */ +#ifdef CONFIG_OF +static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,ltc2974" }, + { .compatible = "lltc,ltc2977" }, + { .compatible = "lltc,ltc2978" }, + { .compatible = "lltc,ltc3880" }, + { .compatible = "lltc,ltc3883" }, + { .compatible = "lltc,ltm4676" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2978_of_match); +#endif + static struct i2c_driver ltc2978_driver = { .driver = { .name = "ltc2978", + .of_match_table = of_match_ptr(ltc2978_of_match), }, .probe = ltc2978_probe, .remove = pmbus_do_remove, diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index fa9beb3eb60c..89a23ff836e7 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -19,6 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/regulator/driver.h> + #ifndef PMBUS_H #define PMBUS_H @@ -186,6 +188,11 @@ #define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) /* + * OPERATION + */ +#define PB_OPERATION_CONTROL_ON (1<<7) + +/* * CAPABILITY */ #define PB_CAPABILITY_SMBALERT (1<<4) @@ -365,8 +372,27 @@ struct pmbus_driver_info { */ int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info); + + /* Regulator functionality, if supported by this chip driver. */ + int num_regulators; + const struct regulator_desc *reg_desc; }; +/* Regulator ops */ + +extern struct regulator_ops pmbus_regulator_ops; + +/* Macro for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR(_name, _id) \ + [_id] = { \ + .name = (_name # _id), \ + .id = (_id), \ + .of_match = of_match_ptr(_name # _id), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pmbus_regulator_ops, \ + .owner = THIS_MODULE, \ + } + /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); @@ -375,6 +401,10 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + u8 value); +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value); void pmbus_clear_faults(struct i2c_client *client); bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 291d11fe93e7..f2e47c7dd808 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -29,6 +29,8 @@ #include <linux/hwmon-sysfs.h> #include <linux/jiffies.h> #include <linux/i2c/pmbus.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> #include "pmbus.h" /* @@ -253,6 +255,37 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) } EXPORT_SYMBOL_GPL(pmbus_read_byte_data); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_write_byte_data(client, reg, value); +} +EXPORT_SYMBOL_GPL(pmbus_write_byte_data); + +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value) +{ + unsigned int tmp; + int rv; + + rv = pmbus_read_byte_data(client, page, reg); + if (rv < 0) + return rv; + + tmp = (rv & ~mask) | (value & mask); + + if (tmp != rv) + rv = pmbus_write_byte_data(client, page, reg, tmp); + + return rv; +} +EXPORT_SYMBOL_GPL(pmbus_update_byte_data); + /* * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if * a device specific mapping function exists and calls it if necessary. @@ -1727,6 +1760,84 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return 0; } +#if IS_ENABLED(CONFIG_REGULATOR) +static int pmbus_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + u8 page = rdev_get_id(rdev); + int ret; + + ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + if (ret < 0) + return ret; + + return !!(ret & PB_OPERATION_CONTROL_ON); +} + +static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + u8 page = rdev_get_id(rdev); + + return pmbus_update_byte_data(client, page, PMBUS_OPERATION, + PB_OPERATION_CONTROL_ON, + enable ? PB_OPERATION_CONTROL_ON : 0); +} + +static int pmbus_regulator_enable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 1); +} + +static int pmbus_regulator_disable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 0); +} + +struct regulator_ops pmbus_regulator_ops = { + .enable = pmbus_regulator_enable, + .disable = pmbus_regulator_disable, + .is_enabled = pmbus_regulator_is_enabled, +}; +EXPORT_SYMBOL_GPL(pmbus_regulator_ops); + +static int pmbus_regulator_register(struct pmbus_data *data) +{ + struct device *dev = data->dev; + const struct pmbus_driver_info *info = data->info; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct regulator_dev *rdev; + int i; + + for (i = 0; i < info->num_regulators; i++) { + struct regulator_config config = { }; + + config.dev = dev; + config.driver_data = data; + + if (pdata && pdata->reg_init_data) + config.init_data = &pdata->reg_init_data[i]; + + rdev = devm_regulator_register(dev, &info->reg_desc[i], + &config); + if (IS_ERR(rdev)) { + dev_err(dev, "Failed to register %s regulator\n", + info->reg_desc[i].name); + return PTR_ERR(rdev); + } + } + + return 0; +} +#else +static int pmbus_regulator_register(struct pmbus_data *data) +{ + return 0; +} +#endif + int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info) { @@ -1781,8 +1892,15 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, dev_err(dev, "Failed to register hwmon device\n"); goto out_kfree; } + + ret = pmbus_regulator_register(data); + if (ret) + goto out_unregister; + return 0; +out_unregister: + hwmon_device_unregister(data->hwmon_dev); out_kfree: kfree(data->group.attrs); return ret; diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 7fa6e7d0b9b6..99664ebc738d 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -44,9 +44,10 @@ #include <linux/sysfs.h> /* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d, + 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { tmp401, tmp411, tmp431, tmp432 }; +enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; /* * The TMP401 registers, note some registers have different addresses for @@ -136,6 +137,7 @@ static const u8 TMP432_STATUS_REG[] = { #define TMP411C_DEVICE_ID 0x10 #define TMP431_DEVICE_ID 0x31 #define TMP432_DEVICE_ID 0x32 +#define TMP435_DEVICE_ID 0x35 /* * Driver data (common to all clients) @@ -146,6 +148,7 @@ static const struct i2c_device_id tmp401_id[] = { { "tmp411", tmp411 }, { "tmp431", tmp431 }, { "tmp432", tmp432 }, + { "tmp435", tmp435 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -613,10 +616,10 @@ static const struct attribute_group tmp432_group = { * Begin non sysfs callback code (aka Real code) */ -static void tmp401_init_client(struct tmp401_data *data, - struct i2c_client *client) +static int tmp401_init_client(struct tmp401_data *data, + struct i2c_client *client) { - int config, config_orig; + int config, config_orig, status = 0; /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); @@ -624,16 +627,18 @@ static void tmp401_init_client(struct tmp401_data *data, /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (config < 0) { - dev_warn(&client->dev, "Initialization failed!\n"); - return; - } + if (config < 0) + return config; config_orig = config; config &= ~TMP401_CONFIG_SHUTDOWN; if (config != config_orig) - i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config); + status = i2c_smbus_write_byte_data(client, + TMP401_CONFIG_WRITE, + config); + + return status; } static int tmp401_detect(struct i2c_client *client, @@ -675,15 +680,18 @@ static int tmp401_detect(struct i2c_client *client, kind = tmp411; break; case TMP431_DEVICE_ID: - if (client->addr == 0x4e) + if (client->addr != 0x4c && client->addr != 0x4d) return -ENODEV; kind = tmp431; break; case TMP432_DEVICE_ID: - if (client->addr == 0x4e) + if (client->addr != 0x4c && client->addr != 0x4d) return -ENODEV; kind = tmp432; break; + case TMP435_DEVICE_ID: + kind = tmp435; + break; default: return -ENODEV; } @@ -705,11 +713,13 @@ static int tmp401_detect(struct i2c_client *client, static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; + static const char * const names[] = { + "TMP401", "TMP411", "TMP431", "TMP432", "TMP435" + }; struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp401_data *data; - int groups = 0; + int groups = 0, status; data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) @@ -720,7 +730,9 @@ static int tmp401_probe(struct i2c_client *client, data->kind = id->driver_data; /* Initialize the TMP401 chip */ - tmp401_init_client(data, client); + status = tmp401_init_client(data, client); + if (status < 0) + return status; /* Register sysfs hooks */ data->groups[groups++] = &tmp401_group; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 917c3585f45b..b4d135cc2f39 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -881,6 +881,16 @@ config I2C_DIOLAN_U2C This driver can also be built as a module. If so, the module will be called i2c-diolan-u2c. +config I2C_DLN2 + tristate "Diolan DLN-2 USB I2C adapter" + depends on MFD_DLN2 + help + If you say yes to this option, support will be included for Diolan + DLN2, a USB to I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-dln2. + config I2C_PARPORT tristate "Parallel port adapter" depends on PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d56c54ba2b..cdac7f15eab5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o +obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 63f3f03ecc9b..c604f4c3ac0d 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -111,6 +111,8 @@ #define CDNS_I2C_DIVA_MAX 4 #define CDNS_I2C_DIVB_MAX 64 +#define CDNS_I2C_TIMEOUT_MAX 0xFF + #define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset) #define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset) @@ -852,6 +854,15 @@ static int cdns_i2c_probe(struct platform_device *pdev) goto err_clk_dis; } + /* + * Cadence I2C controller has a bug wherein it generates + * invalid read transaction after HW timeout in master receiver mode. + * HW timeout is not used by this driver and the interrupt is disabled. + * But the feature itself cannot be disabled. Hence maximum value + * is written to this register to reduce the chances of error. + */ + cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET); + dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n", id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq); diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index d15b7c9b9219..01f0cd87a4a5 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -407,11 +407,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) if (dev->cmd_err & DAVINCI_I2C_STR_NACK) { if (msg->flags & I2C_M_IGNORE_NAK) return msg->len; - if (stop) { - w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - w |= DAVINCI_I2C_MDR_STP; - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); - } + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); return -EREMOTEIO; } return -EIO; diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index edca99dbba23..23628b7bfb8d 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -359,7 +359,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev) } /* Configure Tx/Rx FIFO threshold levels */ - dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL); + dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL); dw_writel(dev, 0, DW_IC_RX_TL); /* configure the i2c master */ diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c new file mode 100644 index 000000000000..b3fb86af4cbb --- /dev/null +++ b/drivers/i2c/busses/i2c-dln2.c @@ -0,0 +1,262 @@ +/* + * Driver for the Diolan DLN-2 USB-I2C adapter + * + * Copyright (c) 2014 Intel Corporation + * + * Derived from: + * i2c-diolan-u2c.c + * Copyright (c) 2010-2011 Ericsson AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#define DLN2_I2C_MODULE_ID 0x03 +#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) + +/* I2C commands */ +#define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00) +#define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01) +#define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02) +#define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03) +#define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06) +#define DLN2_I2C_READ DLN2_I2C_CMD(0x07) +#define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08) +#define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09) +#define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A) +#define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B) +#define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C) +#define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D) +#define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E) + +#define DLN2_I2C_MAX_XFER_SIZE 256 +#define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16) + +struct dln2_i2c { + struct platform_device *pdev; + struct i2c_adapter adapter; + u8 port; + /* + * Buffer to hold the packet for read or write transfers. One is enough + * since we can't have multiple transfers in parallel on the i2c bus. + */ + void *buf; +}; + +static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) +{ + u16 cmd; + struct { + u8 port; + } tx; + + tx.port = dln2->port; + + if (enable) + cmd = DLN2_I2C_ENABLE; + else + cmd = DLN2_I2C_DISABLE; + + return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); +} + +static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, + u8 *data, u16 data_len) +{ + int ret; + struct { + u8 port; + u8 addr; + u8 mem_addr_len; + __le32 mem_addr; + __le16 buf_len; + u8 buf[DLN2_I2C_MAX_XFER_SIZE]; + } __packed *tx = dln2->buf; + unsigned len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); + + tx->port = dln2->port; + tx->addr = addr; + tx->mem_addr_len = 0; + tx->mem_addr = 0; + tx->buf_len = cpu_to_le16(data_len); + memcpy(tx->buf, data, data_len); + + len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; + ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); + if (ret < 0) + return ret; + + return data_len; +} + +static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, + u16 data_len) +{ + int ret; + struct { + u8 port; + u8 addr; + u8 mem_addr_len; + __le32 mem_addr; + __le16 buf_len; + } __packed tx; + struct { + __le16 buf_len; + u8 buf[DLN2_I2C_MAX_XFER_SIZE]; + } __packed *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + + BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); + + tx.port = dln2->port; + tx.addr = addr; + tx.mem_addr_len = 0; + tx.mem_addr = 0; + tx.buf_len = cpu_to_le16(data_len); + + ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->buf_len) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->buf_len) != data_len) + return -EPROTO; + + memcpy(data, rx->buf, data_len); + + return data_len; +} + +static int dln2_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + struct device *dev = &dln2->adapter.dev; + int i; + + for (i = 0; i < num; i++) { + int ret; + + pmsg = &msgs[i]; + + if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) { + dev_warn(dev, "maximum transfer size exceeded\n"); + return -EOPNOTSUPP; + } + + if (pmsg->flags & I2C_M_RD) { + ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, + pmsg->len); + if (ret < 0) + return ret; + + pmsg->len = ret; + } else { + ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, + pmsg->len); + if (ret != pmsg->len) + return -EPROTO; + } + } + + return num; +} + +static u32 dln2_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm dln2_i2c_usb_algorithm = { + .master_xfer = dln2_i2c_xfer, + .functionality = dln2_i2c_func, +}; + +static int dln2_i2c_probe(struct platform_device *pdev) +{ + int ret; + struct dln2_i2c *dln2; + struct device *dev = &pdev->dev; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + + dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); + if (!dln2->buf) + return -ENOMEM; + + dln2->pdev = pdev; + dln2->port = pdata->port; + + /* setup i2c adapter description */ + dln2->adapter.owner = THIS_MODULE; + dln2->adapter.class = I2C_CLASS_HWMON; + dln2->adapter.algo = &dln2_i2c_usb_algorithm; + dln2->adapter.dev.parent = dev; + i2c_set_adapdata(&dln2->adapter, dln2); + snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", + "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); + + platform_set_drvdata(pdev, dln2); + + /* initialize the i2c interface */ + ret = dln2_i2c_enable(dln2, true); + if (ret < 0) { + dev_err(dev, "failed to initialize adapter: %d\n", ret); + return ret; + } + + /* and finally attach to i2c layer */ + ret = i2c_add_adapter(&dln2->adapter); + if (ret < 0) { + dev_err(dev, "failed to add I2C adapter: %d\n", ret); + goto out_disable; + } + + return 0; + +out_disable: + dln2_i2c_enable(dln2, false); + + return ret; +} + +static int dln2_i2c_remove(struct platform_device *pdev) +{ + struct dln2_i2c *dln2 = platform_get_drvdata(pdev); + + i2c_del_adapter(&dln2->adapter); + dln2_i2c_enable(dln2, false); + + return 0; +} + +static struct platform_driver dln2_i2c_driver = { + .driver.name = "dln2-i2c", + .probe = dln2_i2c_probe, + .remove = dln2_i2c_remove, +}; + +module_platform_driver(dln2_i2c_driver); + +MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); +MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-i2c"); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 26942c159de1..277a2288d4a8 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -922,14 +922,12 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (stat & OMAP_I2C_STAT_NACK) { err |= OMAP_I2C_STAT_NACK; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK); - break; } if (stat & OMAP_I2C_STAT_AL) { dev_err(dev->dev, "Arbitration lost\n"); err |= OMAP_I2C_STAT_AL; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL); - break; } /* @@ -954,11 +952,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (dev->fifo_size) num_bytes = dev->buf_len; - omap_i2c_receive_data(dev, num_bytes, true); - - if (dev->errata & I2C_OMAP_ERRATA_I207) + if (dev->errata & I2C_OMAP_ERRATA_I207) { i2c_omap_errata_i207(dev, stat); + num_bytes = (omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F; + } + omap_i2c_receive_data(dev, num_bytes, true); omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); continue; } diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 22c096ce39ad..513bd6d14293 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -44,6 +44,9 @@ #define BMC150_ACCEL_REG_INT_STATUS_2 0x0B #define BMC150_ACCEL_ANY_MOTION_MASK 0x07 +#define BMC150_ACCEL_ANY_MOTION_BIT_X BIT(0) +#define BMC150_ACCEL_ANY_MOTION_BIT_Y BIT(1) +#define BMC150_ACCEL_ANY_MOTION_BIT_Z BIT(2) #define BMC150_ACCEL_ANY_MOTION_BIT_SIGN BIT(3) #define BMC150_ACCEL_REG_PMU_LPW 0x11 @@ -92,9 +95,9 @@ #define BMC150_ACCEL_SLOPE_THRES_MASK 0xFF /* Slope duration in terms of number of samples */ -#define BMC150_ACCEL_DEF_SLOPE_DURATION 2 +#define BMC150_ACCEL_DEF_SLOPE_DURATION 1 /* in terms of multiples of g's/LSB, based on range */ -#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 5 +#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 1 #define BMC150_ACCEL_REG_XOUT_L 0x02 @@ -536,6 +539,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: bmc150_accel_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); + return ret; } @@ -811,6 +817,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev, ret = bmc150_accel_setup_any_motion_interrupt(data, state); if (ret < 0) { + bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -846,7 +853,7 @@ static const struct attribute_group bmc150_accel_attrs_group = { static const struct iio_event_spec bmc150_accel_event = { .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) @@ -1054,6 +1061,7 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = bmc150_accel_setup_new_data_interrupt(data, state); if (ret < 0) { + bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -1092,12 +1100,26 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private) else dir = IIO_EV_DIR_RISING; - if (ret & BMC150_ACCEL_ANY_MOTION_MASK) + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, - IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_Y, IIO_EV_TYPE_ROC, - IIO_EV_DIR_EITHER), + dir), + data->timestamp); + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_ROC, + dir), data->timestamp); ack_intr_status: if (!data->dready_trigger_on) @@ -1354,10 +1376,14 @@ static int bmc150_accel_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret; dev_dbg(&data->client->dev, __func__); + ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0); + if (ret < 0) + return -EAGAIN; - return bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0); + return 0; } static int bmc150_accel_runtime_resume(struct device *dev) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index a23e58c4ed99..320aa72c0349 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -269,6 +269,8 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) return ret; } + ret &= ~(KXCJK1013_REG_CTRL1_BIT_GSEL0 | + KXCJK1013_REG_CTRL1_BIT_GSEL1); ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 88bdc8f612e2..bc4e787096e8 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -127,6 +127,14 @@ config AT91_ADC help Say yes here to build support for Atmel AT91 ADC. +config AXP288_ADC + tristate "X-Powers AXP288 ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index cb88a6a23b8f..f30093f5b67a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1027) += max1027.o diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c new file mode 100644 index 000000000000..08bcfb061ca5 --- /dev/null +++ b/drivers/iio/adc/axp288_adc.c @@ -0,0 +1,261 @@ +/* + * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver + * + * Copyright (C) 2014 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/mfd/axp20x.h> +#include <linux/platform_device.h> + +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> + +#define AXP288_ADC_EN_MASK 0xF1 +#define AXP288_ADC_TS_PIN_GPADC 0xF2 +#define AXP288_ADC_TS_PIN_ON 0xF3 + +enum axp288_adc_id { + AXP288_ADC_TS, + AXP288_ADC_PMIC, + AXP288_ADC_GP, + AXP288_ADC_BATT_CHRG_I, + AXP288_ADC_BATT_DISCHRG_I, + AXP288_ADC_BATT_V, + AXP288_ADC_NR_CHAN, +}; + +struct axp288_adc_info { + int irq; + struct regmap *regmap; +}; + +static const struct iio_chan_spec const axp288_adc_channels[] = { + { + .indexed = 1, + .type = IIO_TEMP, + .channel = 0, + .address = AXP288_TS_ADC_H, + .datasheet_name = "TS_PIN", + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 1, + .address = AXP288_PMIC_ADC_H, + .datasheet_name = "PMIC_TEMP", + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 2, + .address = AXP288_GP_ADC_H, + .datasheet_name = "GPADC", + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 3, + .address = AXP20X_BATT_CHRG_I_H, + .datasheet_name = "BATT_CHG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 4, + .address = AXP20X_BATT_DISCHRG_I_H, + .datasheet_name = "BATT_DISCHRG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_VOLTAGE, + .channel = 5, + .address = AXP20X_BATT_V_H, + .datasheet_name = "BATT_V", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \ + _consumer_channel) \ + { \ + .adc_channel_label = _adc_channel_label, \ + .consumer_dev_name = _consumer_dev_name, \ + .consumer_channel = _consumer_channel, \ + } + +/* for consumer drivers */ +static struct iio_map axp288_adc_default_maps[] = { + AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), + AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), + AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), + AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), + AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), + AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), + {}, +}; + +static int axp288_adc_read_channel(int *val, unsigned long address, + struct regmap *regmap) +{ + u8 buf[2]; + + if (regmap_bulk_read(regmap, address, buf, 2)) + return -EIO; + *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); + + return IIO_VAL_INT; +} + +static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, + unsigned long address) +{ + /* channels other than GPADC do not need to switch TS pin */ + if (address != AXP288_GP_ADC_H) + return 0; + + return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); +} + +static int axp288_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct axp288_adc_info *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, + chan->address)) { + dev_err(&indio_dev->dev, "GPADC mode\n"); + ret = -EINVAL; + break; + } + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, + chan->address)) + dev_err(&indio_dev->dev, "TS pin restore\n"); + break; + case IIO_CHAN_INFO_PROCESSED: + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int axp288_adc_set_state(struct regmap *regmap) +{ + /* ADC should be always enabled for internal FG to function */ + if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) + return -EIO; + + return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); +} + +static const struct iio_info axp288_adc_iio_info = { + .read_raw = &axp288_adc_read_raw, + .driver_module = THIS_MODULE, +}; + +static int axp288_adc_probe(struct platform_device *pdev) +{ + int ret; + struct axp288_adc_info *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return info->irq; + } + platform_set_drvdata(pdev, indio_dev); + info->regmap = axp20x->regmap; + /* + * Set ADC to enabled state at all time, including system suspend. + * otherwise internal fuel gauge functionality may be affected. + */ + ret = axp288_adc_set_state(axp20x->regmap); + if (ret) { + dev_err(&pdev->dev, "unable to enable ADC device\n"); + return ret; + } + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = pdev->name; + indio_dev->channels = axp288_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); + indio_dev->info = &axp288_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register iio device\n"); + goto err_array_unregister; + } + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + + return ret; +} + +static int axp288_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static struct platform_device_id axp288_adc_id_table[] = { + { .name = "axp288_adc" }, + {}, +}; + +static struct platform_driver axp288_adc_driver = { + .probe = axp288_adc_probe, + .remove = axp288_adc_remove, + .id_table = axp288_adc_id_table, + .driver = { + .name = "axp288_adc", + }, +}; + +MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); + +module_platform_driver(axp288_adc_driver); + +MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>"); +MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index b58d6302521f..d095efe1ba14 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -152,6 +152,7 @@ static void men_z188_remove(struct mcb_device *dev) static const struct mcb_device_id men_z188_ids[] = { { .device = 0xbc }, + { } }; MODULE_DEVICE_TABLE(mcb, men_z188_ids); diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 1f967e0d688e..d2fa526740ca 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -67,6 +67,9 @@ #define BMG160_REG_INT_EN_0 0x15 #define BMG160_DATA_ENABLE_INT BIT(7) +#define BMG160_REG_INT_EN_1 0x16 +#define BMG160_INT1_BIT_OD BIT(1) + #define BMG160_REG_XOUT_L 0x02 #define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) @@ -82,6 +85,9 @@ #define BMG160_REG_INT_STATUS_2 0x0B #define BMG160_ANY_MOTION_MASK 0x07 +#define BMG160_ANY_MOTION_BIT_X BIT(0) +#define BMG160_ANY_MOTION_BIT_Y BIT(1) +#define BMG160_ANY_MOTION_BIT_Z BIT(2) #define BMG160_REG_TEMP 0x08 #define BMG160_TEMP_CENTER_VAL 23 @@ -222,6 +228,19 @@ static int bmg160_chip_init(struct bmg160_data *data) data->slope_thres = ret; /* Set default interrupt mode */ + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_en_1\n"); + return ret; + } + ret &= ~BMG160_INT1_BIT_OD; + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_en_1\n"); + return ret; + } + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_INT_RST_LATCH, BMG160_INT_MODE_LATCH_INT | @@ -250,6 +269,9 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: bmg160_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); + return ret; } #endif @@ -705,6 +727,7 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev, ret = bmg160_setup_any_motion_interrupt(data, state); if (ret < 0) { + bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -743,7 +766,7 @@ static const struct attribute_group bmg160_attrs_group = { static const struct iio_event_spec bmg160_event = { .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) }; @@ -871,6 +894,7 @@ static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = bmg160_setup_new_data_interrupt(data, state); if (ret < 0) { + bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -908,10 +932,24 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) else dir = IIO_EV_DIR_FALLING; - if (ret & BMG160_ANY_MOTION_MASK) + if (ret & BMG160_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, 0, - IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMG160_ANY_MOTION_BIT_Y) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMG160_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Z, IIO_EV_TYPE_ROC, dir), data->timestamp); @@ -1169,8 +1207,15 @@ static int bmg160_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND); + if (ret < 0) { + dev_err(&data->client->dev, "set mode failed\n"); + return -EAGAIN; + } - return bmg160_set_mode(data, BMG160_MODE_SUSPEND); + return 0; } static int bmg160_runtime_resume(struct device *dev) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index f42ab14105ac..20ca6a619476 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -911,7 +911,7 @@ static struct scsi_host_template iscsi_iser_sht = { .module = THIS_MODULE, .name = "iSCSI Initiator over iSER", .queuecommand = iscsi_queuecommand, - .change_queue_depth = iscsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .sg_tablesize = ISCSI_ISER_SG_TABLESIZE, .max_sectors = 1024, .cmd_per_lun = ISER_DEF_CMD_PER_LUN, @@ -922,6 +922,7 @@ static struct scsi_host_template iscsi_iser_sht = { .use_clustering = DISABLE_CLUSTERING, .proc_name = "iscsi_iser", .this_id = -1, + .track_queue_depth = 1, }; static struct iscsi_transport iscsi_iser_transport = { diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 62d2a18e1b41..5461924c9f10 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -123,10 +123,15 @@ MODULE_PARM_DESC(dev_loss_tmo, " if fast_io_fail_tmo has not been set. \"off\" means that" " this functionality is disabled."); +static unsigned ch_count; +module_param(ch_count, uint, 0444); +MODULE_PARM_DESC(ch_count, + "Number of RDMA channels to use for communication with an SRP target. Using more than one channel improves performance if the HCA supports multiple completion vectors. The default value is the minimum of four times the number of online CPU sockets and the number of completion vectors supported by the HCA."); + static void srp_add_one(struct ib_device *device); static void srp_remove_one(struct ib_device *device); -static void srp_recv_completion(struct ib_cq *cq, void *target_ptr); -static void srp_send_completion(struct ib_cq *cq, void *target_ptr); +static void srp_recv_completion(struct ib_cq *cq, void *ch_ptr); +static void srp_send_completion(struct ib_cq *cq, void *ch_ptr); static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event); static struct scsi_transport_template *ib_srp_transport_template; @@ -262,7 +267,7 @@ static int srp_init_qp(struct srp_target_port *target, ret = ib_find_pkey(target->srp_host->srp_dev->dev, target->srp_host->port, - be16_to_cpu(target->path.pkey), + be16_to_cpu(target->pkey), &attr->pkey_index); if (ret) goto out; @@ -283,18 +288,23 @@ out: return ret; } -static int srp_new_cm_id(struct srp_target_port *target) +static int srp_new_cm_id(struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; struct ib_cm_id *new_cm_id; new_cm_id = ib_create_cm_id(target->srp_host->srp_dev->dev, - srp_cm_handler, target); + srp_cm_handler, ch); if (IS_ERR(new_cm_id)) return PTR_ERR(new_cm_id); - if (target->cm_id) - ib_destroy_cm_id(target->cm_id); - target->cm_id = new_cm_id; + if (ch->cm_id) + ib_destroy_cm_id(ch->cm_id); + ch->cm_id = new_cm_id; + ch->path.sgid = target->sgid; + ch->path.dgid = target->orig_dgid; + ch->path.pkey = target->pkey; + ch->path.service_id = target->service_id; return 0; } @@ -443,8 +453,44 @@ static struct srp_fr_pool *srp_alloc_fr_pool(struct srp_target_port *target) dev->max_pages_per_mr); } -static int srp_create_target_ib(struct srp_target_port *target) +/** + * srp_destroy_qp() - destroy an RDMA queue pair + * @ch: SRP RDMA channel. + * + * Change a queue pair into the error state and wait until all receive + * completions have been processed before destroying it. This avoids that + * the receive completion handler can access the queue pair while it is + * being destroyed. + */ +static void srp_destroy_qp(struct srp_rdma_ch *ch) +{ + struct srp_target_port *target = ch->target; + static struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR }; + static struct ib_recv_wr wr = { .wr_id = SRP_LAST_WR_ID }; + struct ib_recv_wr *bad_wr; + int ret; + + /* Destroying a QP and reusing ch->done is only safe if not connected */ + WARN_ON_ONCE(target->connected); + + ret = ib_modify_qp(ch->qp, &attr, IB_QP_STATE); + WARN_ONCE(ret, "ib_cm_init_qp_attr() returned %d\n", ret); + if (ret) + goto out; + + init_completion(&ch->done); + ret = ib_post_recv(ch->qp, &wr, &bad_wr); + WARN_ONCE(ret, "ib_post_recv() returned %d\n", ret); + if (ret == 0) + wait_for_completion(&ch->done); + +out: + ib_destroy_qp(ch->qp); +} + +static int srp_create_ch_ib(struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; struct srp_device *dev = target->srp_host->srp_dev; struct ib_qp_init_attr *init_attr; struct ib_cq *recv_cq, *send_cq; @@ -458,15 +504,16 @@ static int srp_create_target_ib(struct srp_target_port *target) if (!init_attr) return -ENOMEM; - recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, target, - target->queue_size, target->comp_vector); + /* + 1 for SRP_LAST_WR_ID */ + recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, ch, + target->queue_size + 1, ch->comp_vector); if (IS_ERR(recv_cq)) { ret = PTR_ERR(recv_cq); goto err; } - send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, target, - m * target->queue_size, target->comp_vector); + send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, ch, + m * target->queue_size, ch->comp_vector); if (IS_ERR(send_cq)) { ret = PTR_ERR(send_cq); goto err_recv_cq; @@ -476,7 +523,7 @@ static int srp_create_target_ib(struct srp_target_port *target) init_attr->event_handler = srp_qp_event; init_attr->cap.max_send_wr = m * target->queue_size; - init_attr->cap.max_recv_wr = target->queue_size; + init_attr->cap.max_recv_wr = target->queue_size + 1; init_attr->cap.max_recv_sge = 1; init_attr->cap.max_send_sge = 1; init_attr->sq_sig_type = IB_SIGNAL_REQ_WR; @@ -502,9 +549,9 @@ static int srp_create_target_ib(struct srp_target_port *target) "FR pool allocation failed (%d)\n", ret); goto err_qp; } - if (target->fr_pool) - srp_destroy_fr_pool(target->fr_pool); - target->fr_pool = fr_pool; + if (ch->fr_pool) + srp_destroy_fr_pool(ch->fr_pool); + ch->fr_pool = fr_pool; } else if (!dev->use_fast_reg && dev->has_fmr) { fmr_pool = srp_alloc_fmr_pool(target); if (IS_ERR(fmr_pool)) { @@ -513,21 +560,21 @@ static int srp_create_target_ib(struct srp_target_port *target) "FMR pool allocation failed (%d)\n", ret); goto err_qp; } - if (target->fmr_pool) - ib_destroy_fmr_pool(target->fmr_pool); - target->fmr_pool = fmr_pool; + if (ch->fmr_pool) + ib_destroy_fmr_pool(ch->fmr_pool); + ch->fmr_pool = fmr_pool; } - if (target->qp) - ib_destroy_qp(target->qp); - if (target->recv_cq) - ib_destroy_cq(target->recv_cq); - if (target->send_cq) - ib_destroy_cq(target->send_cq); + if (ch->qp) + srp_destroy_qp(ch); + if (ch->recv_cq) + ib_destroy_cq(ch->recv_cq); + if (ch->send_cq) + ib_destroy_cq(ch->send_cq); - target->qp = qp; - target->recv_cq = recv_cq; - target->send_cq = send_cq; + ch->qp = qp; + ch->recv_cq = recv_cq; + ch->send_cq = send_cq; kfree(init_attr); return 0; @@ -548,93 +595,117 @@ err: /* * Note: this function may be called without srp_alloc_iu_bufs() having been - * invoked. Hence the target->[rt]x_ring checks. + * invoked. Hence the ch->[rt]x_ring checks. */ -static void srp_free_target_ib(struct srp_target_port *target) +static void srp_free_ch_ib(struct srp_target_port *target, + struct srp_rdma_ch *ch) { struct srp_device *dev = target->srp_host->srp_dev; int i; + if (!ch->target) + return; + + if (ch->cm_id) { + ib_destroy_cm_id(ch->cm_id); + ch->cm_id = NULL; + } + + /* If srp_new_cm_id() succeeded but srp_create_ch_ib() not, return. */ + if (!ch->qp) + return; + if (dev->use_fast_reg) { - if (target->fr_pool) - srp_destroy_fr_pool(target->fr_pool); + if (ch->fr_pool) + srp_destroy_fr_pool(ch->fr_pool); } else { - if (target->fmr_pool) - ib_destroy_fmr_pool(target->fmr_pool); + if (ch->fmr_pool) + ib_destroy_fmr_pool(ch->fmr_pool); } - ib_destroy_qp(target->qp); - ib_destroy_cq(target->send_cq); - ib_destroy_cq(target->recv_cq); + srp_destroy_qp(ch); + ib_destroy_cq(ch->send_cq); + ib_destroy_cq(ch->recv_cq); + + /* + * Avoid that the SCSI error handler tries to use this channel after + * it has been freed. The SCSI error handler can namely continue + * trying to perform recovery actions after scsi_remove_host() + * returned. + */ + ch->target = NULL; - target->qp = NULL; - target->send_cq = target->recv_cq = NULL; + ch->qp = NULL; + ch->send_cq = ch->recv_cq = NULL; - if (target->rx_ring) { + if (ch->rx_ring) { for (i = 0; i < target->queue_size; ++i) - srp_free_iu(target->srp_host, target->rx_ring[i]); - kfree(target->rx_ring); - target->rx_ring = NULL; + srp_free_iu(target->srp_host, ch->rx_ring[i]); + kfree(ch->rx_ring); + ch->rx_ring = NULL; } - if (target->tx_ring) { + if (ch->tx_ring) { for (i = 0; i < target->queue_size; ++i) - srp_free_iu(target->srp_host, target->tx_ring[i]); - kfree(target->tx_ring); - target->tx_ring = NULL; + srp_free_iu(target->srp_host, ch->tx_ring[i]); + kfree(ch->tx_ring); + ch->tx_ring = NULL; } } static void srp_path_rec_completion(int status, struct ib_sa_path_rec *pathrec, - void *target_ptr) + void *ch_ptr) { - struct srp_target_port *target = target_ptr; + struct srp_rdma_ch *ch = ch_ptr; + struct srp_target_port *target = ch->target; - target->status = status; + ch->status = status; if (status) shost_printk(KERN_ERR, target->scsi_host, PFX "Got failed path rec status %d\n", status); else - target->path = *pathrec; - complete(&target->done); + ch->path = *pathrec; + complete(&ch->done); } -static int srp_lookup_path(struct srp_target_port *target) +static int srp_lookup_path(struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; int ret; - target->path.numb_path = 1; - - init_completion(&target->done); - - target->path_query_id = ib_sa_path_rec_get(&srp_sa_client, - target->srp_host->srp_dev->dev, - target->srp_host->port, - &target->path, - IB_SA_PATH_REC_SERVICE_ID | - IB_SA_PATH_REC_DGID | - IB_SA_PATH_REC_SGID | - IB_SA_PATH_REC_NUMB_PATH | - IB_SA_PATH_REC_PKEY, - SRP_PATH_REC_TIMEOUT_MS, - GFP_KERNEL, - srp_path_rec_completion, - target, &target->path_query); - if (target->path_query_id < 0) - return target->path_query_id; - - ret = wait_for_completion_interruptible(&target->done); + ch->path.numb_path = 1; + + init_completion(&ch->done); + + ch->path_query_id = ib_sa_path_rec_get(&srp_sa_client, + target->srp_host->srp_dev->dev, + target->srp_host->port, + &ch->path, + IB_SA_PATH_REC_SERVICE_ID | + IB_SA_PATH_REC_DGID | + IB_SA_PATH_REC_SGID | + IB_SA_PATH_REC_NUMB_PATH | + IB_SA_PATH_REC_PKEY, + SRP_PATH_REC_TIMEOUT_MS, + GFP_KERNEL, + srp_path_rec_completion, + ch, &ch->path_query); + if (ch->path_query_id < 0) + return ch->path_query_id; + + ret = wait_for_completion_interruptible(&ch->done); if (ret < 0) return ret; - if (target->status < 0) + if (ch->status < 0) shost_printk(KERN_WARNING, target->scsi_host, PFX "Path record query failed\n"); - return target->status; + return ch->status; } -static int srp_send_req(struct srp_target_port *target) +static int srp_send_req(struct srp_rdma_ch *ch, bool multich) { + struct srp_target_port *target = ch->target; struct { struct ib_cm_req_param param; struct srp_login_req priv; @@ -645,11 +716,11 @@ static int srp_send_req(struct srp_target_port *target) if (!req) return -ENOMEM; - req->param.primary_path = &target->path; + req->param.primary_path = &ch->path; req->param.alternate_path = NULL; req->param.service_id = target->service_id; - req->param.qp_num = target->qp->qp_num; - req->param.qp_type = target->qp->qp_type; + req->param.qp_num = ch->qp->qp_num; + req->param.qp_type = ch->qp->qp_type; req->param.private_data = &req->priv; req->param.private_data_len = sizeof req->priv; req->param.flow_control = 1; @@ -673,6 +744,8 @@ static int srp_send_req(struct srp_target_port *target) req->priv.req_it_iu_len = cpu_to_be32(target->max_iu_len); req->priv.req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); + req->priv.req_flags = (multich ? SRP_MULTICHAN_MULTI : + SRP_MULTICHAN_SINGLE); /* * In the published SRP specification (draft rev. 16a), the * port identifier format is 8 bytes of ID extension followed @@ -684,7 +757,7 @@ static int srp_send_req(struct srp_target_port *target) */ if (target->io_class == SRP_REV10_IB_IO_CLASS) { memcpy(req->priv.initiator_port_id, - &target->path.sgid.global.interface_id, 8); + &target->sgid.global.interface_id, 8); memcpy(req->priv.initiator_port_id + 8, &target->initiator_ext, 8); memcpy(req->priv.target_port_id, &target->ioc_guid, 8); @@ -693,7 +766,7 @@ static int srp_send_req(struct srp_target_port *target) memcpy(req->priv.initiator_port_id, &target->initiator_ext, 8); memcpy(req->priv.initiator_port_id + 8, - &target->path.sgid.global.interface_id, 8); + &target->sgid.global.interface_id, 8); memcpy(req->priv.target_port_id, &target->id_ext, 8); memcpy(req->priv.target_port_id + 8, &target->ioc_guid, 8); } @@ -713,7 +786,7 @@ static int srp_send_req(struct srp_target_port *target) &target->srp_host->srp_dev->dev->node_guid, 8); } - status = ib_send_cm_req(target->cm_id, &req->param); + status = ib_send_cm_req(ch->cm_id, &req->param); kfree(req); @@ -754,28 +827,35 @@ static bool srp_change_conn_state(struct srp_target_port *target, static void srp_disconnect_target(struct srp_target_port *target) { + struct srp_rdma_ch *ch; + int i; + if (srp_change_conn_state(target, false)) { /* XXX should send SRP_I_LOGOUT request */ - if (ib_send_cm_dreq(target->cm_id, NULL, 0)) { - shost_printk(KERN_DEBUG, target->scsi_host, - PFX "Sending CM DREQ failed\n"); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) { + shost_printk(KERN_DEBUG, target->scsi_host, + PFX "Sending CM DREQ failed\n"); + } } } } -static void srp_free_req_data(struct srp_target_port *target) +static void srp_free_req_data(struct srp_target_port *target, + struct srp_rdma_ch *ch) { struct srp_device *dev = target->srp_host->srp_dev; struct ib_device *ibdev = dev->dev; struct srp_request *req; int i; - if (!target->req_ring) + if (!ch->target || !ch->req_ring) return; for (i = 0; i < target->req_ring_size; ++i) { - req = &target->req_ring[i]; + req = &ch->req_ring[i]; if (dev->use_fast_reg) kfree(req->fr_list); else @@ -789,12 +869,13 @@ static void srp_free_req_data(struct srp_target_port *target) kfree(req->indirect_desc); } - kfree(target->req_ring); - target->req_ring = NULL; + kfree(ch->req_ring); + ch->req_ring = NULL; } -static int srp_alloc_req_data(struct srp_target_port *target) +static int srp_alloc_req_data(struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; struct srp_device *srp_dev = target->srp_host->srp_dev; struct ib_device *ibdev = srp_dev->dev; struct srp_request *req; @@ -802,15 +883,13 @@ static int srp_alloc_req_data(struct srp_target_port *target) dma_addr_t dma_addr; int i, ret = -ENOMEM; - INIT_LIST_HEAD(&target->free_reqs); - - target->req_ring = kzalloc(target->req_ring_size * - sizeof(*target->req_ring), GFP_KERNEL); - if (!target->req_ring) + ch->req_ring = kcalloc(target->req_ring_size, sizeof(*ch->req_ring), + GFP_KERNEL); + if (!ch->req_ring) goto out; for (i = 0; i < target->req_ring_size; ++i) { - req = &target->req_ring[i]; + req = &ch->req_ring[i]; mr_list = kmalloc(target->cmd_sg_cnt * sizeof(void *), GFP_KERNEL); if (!mr_list) @@ -834,8 +913,6 @@ static int srp_alloc_req_data(struct srp_target_port *target) goto out; req->indirect_dma_addr = dma_addr; - req->index = i; - list_add_tail(&req->list, &target->free_reqs); } ret = 0; @@ -860,6 +937,9 @@ static void srp_del_scsi_host_attr(struct Scsi_Host *shost) static void srp_remove_target(struct srp_target_port *target) { + struct srp_rdma_ch *ch; + int i; + WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED); srp_del_scsi_host_attr(target->scsi_host); @@ -868,11 +948,18 @@ static void srp_remove_target(struct srp_target_port *target) scsi_remove_host(target->scsi_host); srp_stop_rport_timers(target->rport); srp_disconnect_target(target); - ib_destroy_cm_id(target->cm_id); - srp_free_target_ib(target); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + srp_free_ch_ib(target, ch); + } cancel_work_sync(&target->tl_err_work); srp_rport_put(target->rport); - srp_free_req_data(target); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + srp_free_req_data(target, ch); + } + kfree(target->ch); + target->ch = NULL; spin_lock(&target->srp_host->target_lock); list_del(&target->list); @@ -898,25 +985,25 @@ static void srp_rport_delete(struct srp_rport *rport) srp_queue_remove_work(target); } -static int srp_connect_target(struct srp_target_port *target) +static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich) { - int retries = 3; + struct srp_target_port *target = ch->target; int ret; - WARN_ON_ONCE(target->connected); + WARN_ON_ONCE(!multich && target->connected); target->qp_in_error = false; - ret = srp_lookup_path(target); + ret = srp_lookup_path(ch); if (ret) return ret; while (1) { - init_completion(&target->done); - ret = srp_send_req(target); + init_completion(&ch->done); + ret = srp_send_req(ch, multich); if (ret) return ret; - ret = wait_for_completion_interruptible(&target->done); + ret = wait_for_completion_interruptible(&ch->done); if (ret < 0) return ret; @@ -926,13 +1013,13 @@ static int srp_connect_target(struct srp_target_port *target) * back, or SRP_DLID_REDIRECT if we get a lid/qp * redirect REJ back. */ - switch (target->status) { + switch (ch->status) { case 0: srp_change_conn_state(target, true); return 0; case SRP_PORT_REDIRECT: - ret = srp_lookup_path(target); + ret = srp_lookup_path(ch); if (ret) return ret; break; @@ -941,27 +1028,18 @@ static int srp_connect_target(struct srp_target_port *target) break; case SRP_STALE_CONN: - /* Our current CM id was stale, and is now in timewait. - * Try to reconnect with a new one. - */ - if (!retries-- || srp_new_cm_id(target)) { - shost_printk(KERN_ERR, target->scsi_host, PFX - "giving up on stale connection\n"); - target->status = -ECONNRESET; - return target->status; - } - shost_printk(KERN_ERR, target->scsi_host, PFX - "retrying stale connection\n"); - break; + "giving up on stale connection\n"); + ch->status = -ECONNRESET; + return ch->status; default: - return target->status; + return ch->status; } } } -static int srp_inv_rkey(struct srp_target_port *target, u32 rkey) +static int srp_inv_rkey(struct srp_rdma_ch *ch, u32 rkey) { struct ib_send_wr *bad_wr; struct ib_send_wr wr = { @@ -973,13 +1051,14 @@ static int srp_inv_rkey(struct srp_target_port *target, u32 rkey) .ex.invalidate_rkey = rkey, }; - return ib_post_send(target->qp, &wr, &bad_wr); + return ib_post_send(ch->qp, &wr, &bad_wr); } static void srp_unmap_data(struct scsi_cmnd *scmnd, - struct srp_target_port *target, + struct srp_rdma_ch *ch, struct srp_request *req) { + struct srp_target_port *target = ch->target; struct srp_device *dev = target->srp_host->srp_dev; struct ib_device *ibdev = dev->dev; int i, res; @@ -993,7 +1072,7 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd, struct srp_fr_desc **pfr; for (i = req->nmdesc, pfr = req->fr_list; i > 0; i--, pfr++) { - res = srp_inv_rkey(target, (*pfr)->mr->rkey); + res = srp_inv_rkey(ch, (*pfr)->mr->rkey); if (res < 0) { shost_printk(KERN_ERR, target->scsi_host, PFX "Queueing INV WR for rkey %#x failed (%d)\n", @@ -1003,7 +1082,7 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd, } } if (req->nmdesc) - srp_fr_pool_put(target->fr_pool, req->fr_list, + srp_fr_pool_put(ch->fr_pool, req->fr_list, req->nmdesc); } else { struct ib_pool_fmr **pfmr; @@ -1018,7 +1097,7 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd, /** * srp_claim_req - Take ownership of the scmnd associated with a request. - * @target: SRP target port. + * @ch: SRP RDMA channel. * @req: SRP request. * @sdev: If not NULL, only take ownership for this SCSI device. * @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take @@ -1027,14 +1106,14 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd, * Return value: * Either NULL or a pointer to the SCSI command the caller became owner of. */ -static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target, +static struct scsi_cmnd *srp_claim_req(struct srp_rdma_ch *ch, struct srp_request *req, struct scsi_device *sdev, struct scsi_cmnd *scmnd) { unsigned long flags; - spin_lock_irqsave(&target->lock, flags); + spin_lock_irqsave(&ch->lock, flags); if (req->scmnd && (!sdev || req->scmnd->device == sdev) && (!scmnd || req->scmnd == scmnd)) { @@ -1043,40 +1122,37 @@ static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target, } else { scmnd = NULL; } - spin_unlock_irqrestore(&target->lock, flags); + spin_unlock_irqrestore(&ch->lock, flags); return scmnd; } /** * srp_free_req() - Unmap data and add request to the free request list. - * @target: SRP target port. + * @ch: SRP RDMA channel. * @req: Request to be freed. * @scmnd: SCSI command associated with @req. * @req_lim_delta: Amount to be added to @target->req_lim. */ -static void srp_free_req(struct srp_target_port *target, - struct srp_request *req, struct scsi_cmnd *scmnd, - s32 req_lim_delta) +static void srp_free_req(struct srp_rdma_ch *ch, struct srp_request *req, + struct scsi_cmnd *scmnd, s32 req_lim_delta) { unsigned long flags; - srp_unmap_data(scmnd, target, req); + srp_unmap_data(scmnd, ch, req); - spin_lock_irqsave(&target->lock, flags); - target->req_lim += req_lim_delta; - list_add_tail(&req->list, &target->free_reqs); - spin_unlock_irqrestore(&target->lock, flags); + spin_lock_irqsave(&ch->lock, flags); + ch->req_lim += req_lim_delta; + spin_unlock_irqrestore(&ch->lock, flags); } -static void srp_finish_req(struct srp_target_port *target, - struct srp_request *req, struct scsi_device *sdev, - int result) +static void srp_finish_req(struct srp_rdma_ch *ch, struct srp_request *req, + struct scsi_device *sdev, int result) { - struct scsi_cmnd *scmnd = srp_claim_req(target, req, sdev, NULL); + struct scsi_cmnd *scmnd = srp_claim_req(ch, req, sdev, NULL); if (scmnd) { - srp_free_req(target, req, scmnd, 0); + srp_free_req(ch, req, scmnd, 0); scmnd->result = result; scmnd->scsi_done(scmnd); } @@ -1085,9 +1161,10 @@ static void srp_finish_req(struct srp_target_port *target, static void srp_terminate_io(struct srp_rport *rport) { struct srp_target_port *target = rport->lld_data; + struct srp_rdma_ch *ch; struct Scsi_Host *shost = target->scsi_host; struct scsi_device *sdev; - int i; + int i, j; /* * Invoking srp_terminate_io() while srp_queuecommand() is running @@ -1096,9 +1173,15 @@ static void srp_terminate_io(struct srp_rport *rport) shost_for_each_device(sdev, shost) WARN_ON_ONCE(sdev->request_queue->request_fn_active); - for (i = 0; i < target->req_ring_size; ++i) { - struct srp_request *req = &target->req_ring[i]; - srp_finish_req(target, req, NULL, DID_TRANSPORT_FAILFAST << 16); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + + for (j = 0; j < target->req_ring_size; ++j) { + struct srp_request *req = &ch->req_ring[j]; + + srp_finish_req(ch, req, NULL, + DID_TRANSPORT_FAILFAST << 16); + } } } @@ -1114,34 +1197,61 @@ static void srp_terminate_io(struct srp_rport *rport) static int srp_rport_reconnect(struct srp_rport *rport) { struct srp_target_port *target = rport->lld_data; - int i, ret; + struct srp_rdma_ch *ch; + int i, j, ret = 0; + bool multich = false; srp_disconnect_target(target); + + if (target->state == SRP_TARGET_SCANNING) + return -ENODEV; + /* * Now get a new local CM ID so that we avoid confusing the target in * case things are really fouled up. Doing so also ensures that all CM * callbacks will have finished before a new QP is allocated. */ - ret = srp_new_cm_id(target); - - for (i = 0; i < target->req_ring_size; ++i) { - struct srp_request *req = &target->req_ring[i]; - srp_finish_req(target, req, NULL, DID_RESET << 16); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + if (!ch->target) + break; + ret += srp_new_cm_id(ch); } + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + if (!ch->target) + break; + for (j = 0; j < target->req_ring_size; ++j) { + struct srp_request *req = &ch->req_ring[j]; - /* - * Whether or not creating a new CM ID succeeded, create a new - * QP. This guarantees that all callback functions for the old QP have - * finished before any send requests are posted on the new QP. - */ - ret += srp_create_target_ib(target); - - INIT_LIST_HEAD(&target->free_tx); - for (i = 0; i < target->queue_size; ++i) - list_add(&target->tx_ring[i]->list, &target->free_tx); + srp_finish_req(ch, req, NULL, DID_RESET << 16); + } + } + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + if (!ch->target) + break; + /* + * Whether or not creating a new CM ID succeeded, create a new + * QP. This guarantees that all completion callback function + * invocations have finished before request resetting starts. + */ + ret += srp_create_ch_ib(ch); - if (ret == 0) - ret = srp_connect_target(target); + INIT_LIST_HEAD(&ch->free_tx); + for (j = 0; j < target->queue_size; ++j) + list_add(&ch->tx_ring[j]->list, &ch->free_tx); + } + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + if (ret || !ch->target) { + if (i > 1) + ret = 0; + break; + } + ret = srp_connect_ch(ch, multich); + multich = true; + } if (ret == 0) shost_printk(KERN_INFO, target->scsi_host, @@ -1165,12 +1275,12 @@ static void srp_map_desc(struct srp_map_state *state, dma_addr_t dma_addr, } static int srp_map_finish_fmr(struct srp_map_state *state, - struct srp_target_port *target) + struct srp_rdma_ch *ch) { struct ib_pool_fmr *fmr; u64 io_addr = 0; - fmr = ib_fmr_pool_map_phys(target->fmr_pool, state->pages, + fmr = ib_fmr_pool_map_phys(ch->fmr_pool, state->pages, state->npages, io_addr); if (IS_ERR(fmr)) return PTR_ERR(fmr); @@ -1184,15 +1294,16 @@ static int srp_map_finish_fmr(struct srp_map_state *state, } static int srp_map_finish_fr(struct srp_map_state *state, - struct srp_target_port *target) + struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; struct srp_device *dev = target->srp_host->srp_dev; struct ib_send_wr *bad_wr; struct ib_send_wr wr; struct srp_fr_desc *desc; u32 rkey; - desc = srp_fr_pool_get(target->fr_pool); + desc = srp_fr_pool_get(ch->fr_pool); if (!desc) return -ENOMEM; @@ -1221,12 +1332,13 @@ static int srp_map_finish_fr(struct srp_map_state *state, srp_map_desc(state, state->base_dma_addr, state->dma_len, desc->mr->rkey); - return ib_post_send(target->qp, &wr, &bad_wr); + return ib_post_send(ch->qp, &wr, &bad_wr); } static int srp_finish_mapping(struct srp_map_state *state, - struct srp_target_port *target) + struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; int ret = 0; if (state->npages == 0) @@ -1237,8 +1349,8 @@ static int srp_finish_mapping(struct srp_map_state *state, target->rkey); else ret = target->srp_host->srp_dev->use_fast_reg ? - srp_map_finish_fr(state, target) : - srp_map_finish_fmr(state, target); + srp_map_finish_fr(state, ch) : + srp_map_finish_fmr(state, ch); if (ret == 0) { state->npages = 0; @@ -1258,10 +1370,11 @@ static void srp_map_update_start(struct srp_map_state *state, } static int srp_map_sg_entry(struct srp_map_state *state, - struct srp_target_port *target, + struct srp_rdma_ch *ch, struct scatterlist *sg, int sg_index, bool use_mr) { + struct srp_target_port *target = ch->target; struct srp_device *dev = target->srp_host->srp_dev; struct ib_device *ibdev = dev->dev; dma_addr_t dma_addr = ib_sg_dma_address(ibdev, sg); @@ -1290,7 +1403,7 @@ static int srp_map_sg_entry(struct srp_map_state *state, */ if ((!dev->use_fast_reg && dma_addr & ~dev->mr_page_mask) || dma_len > dev->mr_max_size) { - ret = srp_finish_mapping(state, target); + ret = srp_finish_mapping(state, ch); if (ret) return ret; @@ -1311,7 +1424,7 @@ static int srp_map_sg_entry(struct srp_map_state *state, while (dma_len) { unsigned offset = dma_addr & ~dev->mr_page_mask; if (state->npages == dev->max_pages_per_mr || offset != 0) { - ret = srp_finish_mapping(state, target); + ret = srp_finish_mapping(state, ch); if (ret) return ret; @@ -1335,17 +1448,18 @@ static int srp_map_sg_entry(struct srp_map_state *state, */ ret = 0; if (len != dev->mr_page_size) { - ret = srp_finish_mapping(state, target); + ret = srp_finish_mapping(state, ch); if (!ret) srp_map_update_start(state, NULL, 0, 0); } return ret; } -static int srp_map_sg(struct srp_map_state *state, - struct srp_target_port *target, struct srp_request *req, - struct scatterlist *scat, int count) +static int srp_map_sg(struct srp_map_state *state, struct srp_rdma_ch *ch, + struct srp_request *req, struct scatterlist *scat, + int count) { + struct srp_target_port *target = ch->target; struct srp_device *dev = target->srp_host->srp_dev; struct ib_device *ibdev = dev->dev; struct scatterlist *sg; @@ -1356,14 +1470,14 @@ static int srp_map_sg(struct srp_map_state *state, state->pages = req->map_page; if (dev->use_fast_reg) { state->next_fr = req->fr_list; - use_mr = !!target->fr_pool; + use_mr = !!ch->fr_pool; } else { state->next_fmr = req->fmr_list; - use_mr = !!target->fmr_pool; + use_mr = !!ch->fmr_pool; } for_each_sg(scat, sg, count, i) { - if (srp_map_sg_entry(state, target, sg, i, use_mr)) { + if (srp_map_sg_entry(state, ch, sg, i, use_mr)) { /* * Memory registration failed, so backtrack to the * first unmapped entry and continue on without using @@ -1385,7 +1499,7 @@ backtrack: } } - if (use_mr && srp_finish_mapping(state, target)) + if (use_mr && srp_finish_mapping(state, ch)) goto backtrack; req->nmdesc = state->nmdesc; @@ -1393,9 +1507,10 @@ backtrack: return 0; } -static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target, +static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch, struct srp_request *req) { + struct srp_target_port *target = ch->target; struct scatterlist *scat; struct srp_cmd *cmd = req->cmd->buf; int len, nents, count; @@ -1457,7 +1572,7 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target, target->indirect_size, DMA_TO_DEVICE); memset(&state, 0, sizeof(state)); - srp_map_sg(&state, target, req, scat, count); + srp_map_sg(&state, ch, req, scat, count); /* We've mapped the request, now pull as much of the indirect * descriptor table as we can into the command buffer. If this @@ -1518,20 +1633,20 @@ map_complete: /* * Return an IU and possible credit to the free pool */ -static void srp_put_tx_iu(struct srp_target_port *target, struct srp_iu *iu, +static void srp_put_tx_iu(struct srp_rdma_ch *ch, struct srp_iu *iu, enum srp_iu_type iu_type) { unsigned long flags; - spin_lock_irqsave(&target->lock, flags); - list_add(&iu->list, &target->free_tx); + spin_lock_irqsave(&ch->lock, flags); + list_add(&iu->list, &ch->free_tx); if (iu_type != SRP_IU_RSP) - ++target->req_lim; - spin_unlock_irqrestore(&target->lock, flags); + ++ch->req_lim; + spin_unlock_irqrestore(&ch->lock, flags); } /* - * Must be called with target->lock held to protect req_lim and free_tx. + * Must be called with ch->lock held to protect req_lim and free_tx. * If IU is not sent, it must be returned using srp_put_tx_iu(). * * Note: @@ -1543,35 +1658,36 @@ static void srp_put_tx_iu(struct srp_target_port *target, struct srp_iu *iu, * - SRP_IU_RSP: 1, since a conforming SRP target never sends more than * one unanswered SRP request to an initiator. */ -static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target, +static struct srp_iu *__srp_get_tx_iu(struct srp_rdma_ch *ch, enum srp_iu_type iu_type) { + struct srp_target_port *target = ch->target; s32 rsv = (iu_type == SRP_IU_TSK_MGMT) ? 0 : SRP_TSK_MGMT_SQ_SIZE; struct srp_iu *iu; - srp_send_completion(target->send_cq, target); + srp_send_completion(ch->send_cq, ch); - if (list_empty(&target->free_tx)) + if (list_empty(&ch->free_tx)) return NULL; /* Initiator responses to target requests do not consume credits */ if (iu_type != SRP_IU_RSP) { - if (target->req_lim <= rsv) { + if (ch->req_lim <= rsv) { ++target->zero_req_lim; return NULL; } - --target->req_lim; + --ch->req_lim; } - iu = list_first_entry(&target->free_tx, struct srp_iu, list); + iu = list_first_entry(&ch->free_tx, struct srp_iu, list); list_del(&iu->list); return iu; } -static int srp_post_send(struct srp_target_port *target, - struct srp_iu *iu, int len) +static int srp_post_send(struct srp_rdma_ch *ch, struct srp_iu *iu, int len) { + struct srp_target_port *target = ch->target; struct ib_sge list; struct ib_send_wr wr, *bad_wr; @@ -1586,11 +1702,12 @@ static int srp_post_send(struct srp_target_port *target, wr.opcode = IB_WR_SEND; wr.send_flags = IB_SEND_SIGNALED; - return ib_post_send(target->qp, &wr, &bad_wr); + return ib_post_send(ch->qp, &wr, &bad_wr); } -static int srp_post_recv(struct srp_target_port *target, struct srp_iu *iu) +static int srp_post_recv(struct srp_rdma_ch *ch, struct srp_iu *iu) { + struct srp_target_port *target = ch->target; struct ib_recv_wr wr, *bad_wr; struct ib_sge list; @@ -1603,35 +1720,39 @@ static int srp_post_recv(struct srp_target_port *target, struct srp_iu *iu) wr.sg_list = &list; wr.num_sge = 1; - return ib_post_recv(target->qp, &wr, &bad_wr); + return ib_post_recv(ch->qp, &wr, &bad_wr); } -static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) +static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) { + struct srp_target_port *target = ch->target; struct srp_request *req; struct scsi_cmnd *scmnd; unsigned long flags; if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) { - spin_lock_irqsave(&target->lock, flags); - target->req_lim += be32_to_cpu(rsp->req_lim_delta); - spin_unlock_irqrestore(&target->lock, flags); + spin_lock_irqsave(&ch->lock, flags); + ch->req_lim += be32_to_cpu(rsp->req_lim_delta); + spin_unlock_irqrestore(&ch->lock, flags); - target->tsk_mgmt_status = -1; + ch->tsk_mgmt_status = -1; if (be32_to_cpu(rsp->resp_data_len) >= 4) - target->tsk_mgmt_status = rsp->data[3]; - complete(&target->tsk_mgmt_done); + ch->tsk_mgmt_status = rsp->data[3]; + complete(&ch->tsk_mgmt_done); } else { - req = &target->req_ring[rsp->tag]; - scmnd = srp_claim_req(target, req, NULL, NULL); + scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag); + if (scmnd) { + req = (void *)scmnd->host_scribble; + scmnd = srp_claim_req(ch, req, NULL, scmnd); + } if (!scmnd) { shost_printk(KERN_ERR, target->scsi_host, - "Null scmnd for RSP w/tag %016llx\n", - (unsigned long long) rsp->tag); + "Null scmnd for RSP w/tag %#016llx received on ch %td / QP %#x\n", + rsp->tag, ch - target->ch, ch->qp->qp_num); - spin_lock_irqsave(&target->lock, flags); - target->req_lim += be32_to_cpu(rsp->req_lim_delta); - spin_unlock_irqrestore(&target->lock, flags); + spin_lock_irqsave(&ch->lock, flags); + ch->req_lim += be32_to_cpu(rsp->req_lim_delta); + spin_unlock_irqrestore(&ch->lock, flags); return; } @@ -1653,7 +1774,7 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) else if (unlikely(rsp->flags & SRP_RSP_FLAG_DOOVER)) scsi_set_resid(scmnd, -be32_to_cpu(rsp->data_out_res_cnt)); - srp_free_req(target, req, scmnd, + srp_free_req(ch, req, scmnd, be32_to_cpu(rsp->req_lim_delta)); scmnd->host_scribble = NULL; @@ -1661,18 +1782,19 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) } } -static int srp_response_common(struct srp_target_port *target, s32 req_delta, +static int srp_response_common(struct srp_rdma_ch *ch, s32 req_delta, void *rsp, int len) { + struct srp_target_port *target = ch->target; struct ib_device *dev = target->srp_host->srp_dev->dev; unsigned long flags; struct srp_iu *iu; int err; - spin_lock_irqsave(&target->lock, flags); - target->req_lim += req_delta; - iu = __srp_get_tx_iu(target, SRP_IU_RSP); - spin_unlock_irqrestore(&target->lock, flags); + spin_lock_irqsave(&ch->lock, flags); + ch->req_lim += req_delta; + iu = __srp_get_tx_iu(ch, SRP_IU_RSP); + spin_unlock_irqrestore(&ch->lock, flags); if (!iu) { shost_printk(KERN_ERR, target->scsi_host, PFX @@ -1684,17 +1806,17 @@ static int srp_response_common(struct srp_target_port *target, s32 req_delta, memcpy(iu->buf, rsp, len); ib_dma_sync_single_for_device(dev, iu->dma, len, DMA_TO_DEVICE); - err = srp_post_send(target, iu, len); + err = srp_post_send(ch, iu, len); if (err) { shost_printk(KERN_ERR, target->scsi_host, PFX "unable to post response: %d\n", err); - srp_put_tx_iu(target, iu, SRP_IU_RSP); + srp_put_tx_iu(ch, iu, SRP_IU_RSP); } return err; } -static void srp_process_cred_req(struct srp_target_port *target, +static void srp_process_cred_req(struct srp_rdma_ch *ch, struct srp_cred_req *req) { struct srp_cred_rsp rsp = { @@ -1703,14 +1825,15 @@ static void srp_process_cred_req(struct srp_target_port *target, }; s32 delta = be32_to_cpu(req->req_lim_delta); - if (srp_response_common(target, delta, &rsp, sizeof rsp)) - shost_printk(KERN_ERR, target->scsi_host, PFX + if (srp_response_common(ch, delta, &rsp, sizeof(rsp))) + shost_printk(KERN_ERR, ch->target->scsi_host, PFX "problems processing SRP_CRED_REQ\n"); } -static void srp_process_aer_req(struct srp_target_port *target, +static void srp_process_aer_req(struct srp_rdma_ch *ch, struct srp_aer_req *req) { + struct srp_target_port *target = ch->target; struct srp_aer_rsp rsp = { .opcode = SRP_AER_RSP, .tag = req->tag, @@ -1720,19 +1843,20 @@ static void srp_process_aer_req(struct srp_target_port *target, shost_printk(KERN_ERR, target->scsi_host, PFX "ignoring AER for LUN %llu\n", be64_to_cpu(req->lun)); - if (srp_response_common(target, delta, &rsp, sizeof rsp)) + if (srp_response_common(ch, delta, &rsp, sizeof(rsp))) shost_printk(KERN_ERR, target->scsi_host, PFX "problems processing SRP_AER_REQ\n"); } -static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc) +static void srp_handle_recv(struct srp_rdma_ch *ch, struct ib_wc *wc) { + struct srp_target_port *target = ch->target; struct ib_device *dev = target->srp_host->srp_dev->dev; struct srp_iu *iu = (struct srp_iu *) (uintptr_t) wc->wr_id; int res; u8 opcode; - ib_dma_sync_single_for_cpu(dev, iu->dma, target->max_ti_iu_len, + ib_dma_sync_single_for_cpu(dev, iu->dma, ch->max_ti_iu_len, DMA_FROM_DEVICE); opcode = *(u8 *) iu->buf; @@ -1746,15 +1870,15 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc) switch (opcode) { case SRP_RSP: - srp_process_rsp(target, iu->buf); + srp_process_rsp(ch, iu->buf); break; case SRP_CRED_REQ: - srp_process_cred_req(target, iu->buf); + srp_process_cred_req(ch, iu->buf); break; case SRP_AER_REQ: - srp_process_aer_req(target, iu->buf); + srp_process_aer_req(ch, iu->buf); break; case SRP_T_LOGOUT: @@ -1769,10 +1893,10 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc) break; } - ib_dma_sync_single_for_device(dev, iu->dma, target->max_ti_iu_len, + ib_dma_sync_single_for_device(dev, iu->dma, ch->max_ti_iu_len, DMA_FROM_DEVICE); - res = srp_post_recv(target, iu); + res = srp_post_recv(ch, iu); if (res != 0) shost_printk(KERN_ERR, target->scsi_host, PFX "Recv failed with error code %d\n", res); @@ -1795,8 +1919,15 @@ static void srp_tl_err_work(struct work_struct *work) } static void srp_handle_qp_err(u64 wr_id, enum ib_wc_status wc_status, - bool send_err, struct srp_target_port *target) + bool send_err, struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; + + if (wr_id == SRP_LAST_WR_ID) { + complete(&ch->done); + return; + } + if (target->connected && !target->qp_in_error) { if (wr_id & LOCAL_INV_WR_ID_MASK) { shost_printk(KERN_ERR, target->scsi_host, PFX @@ -1817,33 +1948,33 @@ static void srp_handle_qp_err(u64 wr_id, enum ib_wc_status wc_status, target->qp_in_error = true; } -static void srp_recv_completion(struct ib_cq *cq, void *target_ptr) +static void srp_recv_completion(struct ib_cq *cq, void *ch_ptr) { - struct srp_target_port *target = target_ptr; + struct srp_rdma_ch *ch = ch_ptr; struct ib_wc wc; ib_req_notify_cq(cq, IB_CQ_NEXT_COMP); while (ib_poll_cq(cq, 1, &wc) > 0) { if (likely(wc.status == IB_WC_SUCCESS)) { - srp_handle_recv(target, &wc); + srp_handle_recv(ch, &wc); } else { - srp_handle_qp_err(wc.wr_id, wc.status, false, target); + srp_handle_qp_err(wc.wr_id, wc.status, false, ch); } } } -static void srp_send_completion(struct ib_cq *cq, void *target_ptr) +static void srp_send_completion(struct ib_cq *cq, void *ch_ptr) { - struct srp_target_port *target = target_ptr; + struct srp_rdma_ch *ch = ch_ptr; struct ib_wc wc; struct srp_iu *iu; while (ib_poll_cq(cq, 1, &wc) > 0) { if (likely(wc.status == IB_WC_SUCCESS)) { iu = (struct srp_iu *) (uintptr_t) wc.wr_id; - list_add(&iu->list, &target->free_tx); + list_add(&iu->list, &ch->free_tx); } else { - srp_handle_qp_err(wc.wr_id, wc.status, true, target); + srp_handle_qp_err(wc.wr_id, wc.status, true, ch); } } } @@ -1852,11 +1983,14 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd) { struct srp_target_port *target = host_to_target(shost); struct srp_rport *rport = target->rport; + struct srp_rdma_ch *ch; struct srp_request *req; struct srp_iu *iu; struct srp_cmd *cmd; struct ib_device *dev; unsigned long flags; + u32 tag; + u16 idx; int len, ret; const bool in_scsi_eh = !in_interrupt() && current == shost->ehandler; @@ -1873,15 +2007,22 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd) if (unlikely(scmnd->result)) goto err; - spin_lock_irqsave(&target->lock, flags); - iu = __srp_get_tx_iu(target, SRP_IU_CMD); - if (!iu) - goto err_unlock; + WARN_ON_ONCE(scmnd->request->tag < 0); + tag = blk_mq_unique_tag(scmnd->request); + ch = &target->ch[blk_mq_unique_tag_to_hwq(tag)]; + idx = blk_mq_unique_tag_to_tag(tag); + WARN_ONCE(idx >= target->req_ring_size, "%s: tag %#x: idx %d >= %d\n", + dev_name(&shost->shost_gendev), tag, idx, + target->req_ring_size); - req = list_first_entry(&target->free_reqs, struct srp_request, list); - list_del(&req->list); - spin_unlock_irqrestore(&target->lock, flags); + spin_lock_irqsave(&ch->lock, flags); + iu = __srp_get_tx_iu(ch, SRP_IU_CMD); + spin_unlock_irqrestore(&ch->lock, flags); + if (!iu) + goto err; + + req = &ch->req_ring[idx]; dev = target->srp_host->srp_dev->dev; ib_dma_sync_single_for_cpu(dev, iu->dma, target->max_iu_len, DMA_TO_DEVICE); @@ -1893,13 +2034,13 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd) cmd->opcode = SRP_CMD; cmd->lun = cpu_to_be64((u64) scmnd->device->lun << 48); - cmd->tag = req->index; + cmd->tag = tag; memcpy(cmd->cdb, scmnd->cmnd, scmnd->cmd_len); req->scmnd = scmnd; req->cmd = iu; - len = srp_map_data(scmnd, target, req); + len = srp_map_data(scmnd, ch, req); if (len < 0) { shost_printk(KERN_ERR, target->scsi_host, PFX "Failed to map data (%d)\n", len); @@ -1917,7 +2058,7 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd) ib_dma_sync_single_for_device(dev, iu->dma, target->max_iu_len, DMA_TO_DEVICE); - if (srp_post_send(target, iu, len)) { + if (srp_post_send(ch, iu, len)) { shost_printk(KERN_ERR, target->scsi_host, PFX "Send failed\n"); goto err_unmap; } @@ -1931,10 +2072,10 @@ unlock_rport: return ret; err_unmap: - srp_unmap_data(scmnd, target, req); + srp_unmap_data(scmnd, ch, req); err_iu: - srp_put_tx_iu(target, iu, SRP_IU_CMD); + srp_put_tx_iu(ch, iu, SRP_IU_CMD); /* * Avoid that the loops that iterate over the request ring can @@ -1942,12 +2083,6 @@ err_iu: */ req->scmnd = NULL; - spin_lock_irqsave(&target->lock, flags); - list_add(&req->list, &target->free_reqs); - -err_unlock: - spin_unlock_irqrestore(&target->lock, flags); - err: if (scmnd->result) { scmnd->scsi_done(scmnd); @@ -1961,53 +2096,54 @@ err: /* * Note: the resources allocated in this function are freed in - * srp_free_target_ib(). + * srp_free_ch_ib(). */ -static int srp_alloc_iu_bufs(struct srp_target_port *target) +static int srp_alloc_iu_bufs(struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; int i; - target->rx_ring = kzalloc(target->queue_size * sizeof(*target->rx_ring), - GFP_KERNEL); - if (!target->rx_ring) + ch->rx_ring = kcalloc(target->queue_size, sizeof(*ch->rx_ring), + GFP_KERNEL); + if (!ch->rx_ring) goto err_no_ring; - target->tx_ring = kzalloc(target->queue_size * sizeof(*target->tx_ring), - GFP_KERNEL); - if (!target->tx_ring) + ch->tx_ring = kcalloc(target->queue_size, sizeof(*ch->tx_ring), + GFP_KERNEL); + if (!ch->tx_ring) goto err_no_ring; for (i = 0; i < target->queue_size; ++i) { - target->rx_ring[i] = srp_alloc_iu(target->srp_host, - target->max_ti_iu_len, - GFP_KERNEL, DMA_FROM_DEVICE); - if (!target->rx_ring[i]) + ch->rx_ring[i] = srp_alloc_iu(target->srp_host, + ch->max_ti_iu_len, + GFP_KERNEL, DMA_FROM_DEVICE); + if (!ch->rx_ring[i]) goto err; } for (i = 0; i < target->queue_size; ++i) { - target->tx_ring[i] = srp_alloc_iu(target->srp_host, - target->max_iu_len, - GFP_KERNEL, DMA_TO_DEVICE); - if (!target->tx_ring[i]) + ch->tx_ring[i] = srp_alloc_iu(target->srp_host, + target->max_iu_len, + GFP_KERNEL, DMA_TO_DEVICE); + if (!ch->tx_ring[i]) goto err; - list_add(&target->tx_ring[i]->list, &target->free_tx); + list_add(&ch->tx_ring[i]->list, &ch->free_tx); } return 0; err: for (i = 0; i < target->queue_size; ++i) { - srp_free_iu(target->srp_host, target->rx_ring[i]); - srp_free_iu(target->srp_host, target->tx_ring[i]); + srp_free_iu(target->srp_host, ch->rx_ring[i]); + srp_free_iu(target->srp_host, ch->tx_ring[i]); } err_no_ring: - kfree(target->tx_ring); - target->tx_ring = NULL; - kfree(target->rx_ring); - target->rx_ring = NULL; + kfree(ch->tx_ring); + ch->tx_ring = NULL; + kfree(ch->rx_ring); + ch->rx_ring = NULL; return -ENOMEM; } @@ -2041,23 +2177,24 @@ static uint32_t srp_compute_rq_tmo(struct ib_qp_attr *qp_attr, int attr_mask) static void srp_cm_rep_handler(struct ib_cm_id *cm_id, struct srp_login_rsp *lrsp, - struct srp_target_port *target) + struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; struct ib_qp_attr *qp_attr = NULL; int attr_mask = 0; int ret; int i; if (lrsp->opcode == SRP_LOGIN_RSP) { - target->max_ti_iu_len = be32_to_cpu(lrsp->max_ti_iu_len); - target->req_lim = be32_to_cpu(lrsp->req_lim_delta); + ch->max_ti_iu_len = be32_to_cpu(lrsp->max_ti_iu_len); + ch->req_lim = be32_to_cpu(lrsp->req_lim_delta); /* * Reserve credits for task management so we don't * bounce requests back to the SCSI mid-layer. */ target->scsi_host->can_queue - = min(target->req_lim - SRP_TSK_MGMT_SQ_SIZE, + = min(ch->req_lim - SRP_TSK_MGMT_SQ_SIZE, target->scsi_host->can_queue); target->scsi_host->cmd_per_lun = min_t(int, target->scsi_host->can_queue, @@ -2069,8 +2206,8 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id, goto error; } - if (!target->rx_ring) { - ret = srp_alloc_iu_bufs(target); + if (!ch->rx_ring) { + ret = srp_alloc_iu_bufs(ch); if (ret) goto error; } @@ -2085,13 +2222,14 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id, if (ret) goto error_free; - ret = ib_modify_qp(target->qp, qp_attr, attr_mask); + ret = ib_modify_qp(ch->qp, qp_attr, attr_mask); if (ret) goto error_free; for (i = 0; i < target->queue_size; i++) { - struct srp_iu *iu = target->rx_ring[i]; - ret = srp_post_recv(target, iu); + struct srp_iu *iu = ch->rx_ring[i]; + + ret = srp_post_recv(ch, iu); if (ret) goto error_free; } @@ -2103,7 +2241,7 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id, target->rq_tmo_jiffies = srp_compute_rq_tmo(qp_attr, attr_mask); - ret = ib_modify_qp(target->qp, qp_attr, attr_mask); + ret = ib_modify_qp(ch->qp, qp_attr, attr_mask); if (ret) goto error_free; @@ -2113,13 +2251,14 @@ error_free: kfree(qp_attr); error: - target->status = ret; + ch->status = ret; } static void srp_cm_rej_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event, - struct srp_target_port *target) + struct srp_rdma_ch *ch) { + struct srp_target_port *target = ch->target; struct Scsi_Host *shost = target->scsi_host; struct ib_class_port_info *cpi; int opcode; @@ -2127,12 +2266,12 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id, switch (event->param.rej_rcvd.reason) { case IB_CM_REJ_PORT_CM_REDIRECT: cpi = event->param.rej_rcvd.ari; - target->path.dlid = cpi->redirect_lid; - target->path.pkey = cpi->redirect_pkey; + ch->path.dlid = cpi->redirect_lid; + ch->path.pkey = cpi->redirect_pkey; cm_id->remote_cm_qpn = be32_to_cpu(cpi->redirect_qp) & 0x00ffffff; - memcpy(target->path.dgid.raw, cpi->redirect_gid, 16); + memcpy(ch->path.dgid.raw, cpi->redirect_gid, 16); - target->status = target->path.dlid ? + ch->status = ch->path.dlid ? SRP_DLID_REDIRECT : SRP_PORT_REDIRECT; break; @@ -2143,26 +2282,26 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id, * reject reason code 25 when they mean 24 * (port redirect). */ - memcpy(target->path.dgid.raw, + memcpy(ch->path.dgid.raw, event->param.rej_rcvd.ari, 16); shost_printk(KERN_DEBUG, shost, PFX "Topspin/Cisco redirect to target port GID %016llx%016llx\n", - (unsigned long long) be64_to_cpu(target->path.dgid.global.subnet_prefix), - (unsigned long long) be64_to_cpu(target->path.dgid.global.interface_id)); + be64_to_cpu(ch->path.dgid.global.subnet_prefix), + be64_to_cpu(ch->path.dgid.global.interface_id)); - target->status = SRP_PORT_REDIRECT; + ch->status = SRP_PORT_REDIRECT; } else { shost_printk(KERN_WARNING, shost, " REJ reason: IB_CM_REJ_PORT_REDIRECT\n"); - target->status = -ECONNRESET; + ch->status = -ECONNRESET; } break; case IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID: shost_printk(KERN_WARNING, shost, " REJ reason: IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID\n"); - target->status = -ECONNRESET; + ch->status = -ECONNRESET; break; case IB_CM_REJ_CONSUMER_DEFINED: @@ -2177,30 +2316,31 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id, else shost_printk(KERN_WARNING, shost, PFX "SRP LOGIN from %pI6 to %pI6 REJECTED, reason 0x%08x\n", - target->path.sgid.raw, - target->orig_dgid, reason); + target->sgid.raw, + target->orig_dgid.raw, reason); } else shost_printk(KERN_WARNING, shost, " REJ reason: IB_CM_REJ_CONSUMER_DEFINED," " opcode 0x%02x\n", opcode); - target->status = -ECONNRESET; + ch->status = -ECONNRESET; break; case IB_CM_REJ_STALE_CONN: shost_printk(KERN_WARNING, shost, " REJ reason: stale connection\n"); - target->status = SRP_STALE_CONN; + ch->status = SRP_STALE_CONN; break; default: shost_printk(KERN_WARNING, shost, " REJ reason 0x%x\n", event->param.rej_rcvd.reason); - target->status = -ECONNRESET; + ch->status = -ECONNRESET; } } static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) { - struct srp_target_port *target = cm_id->context; + struct srp_rdma_ch *ch = cm_id->context; + struct srp_target_port *target = ch->target; int comp = 0; switch (event->event) { @@ -2208,19 +2348,19 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) shost_printk(KERN_DEBUG, target->scsi_host, PFX "Sending CM REQ failed\n"); comp = 1; - target->status = -ECONNRESET; + ch->status = -ECONNRESET; break; case IB_CM_REP_RECEIVED: comp = 1; - srp_cm_rep_handler(cm_id, event->private_data, target); + srp_cm_rep_handler(cm_id, event->private_data, ch); break; case IB_CM_REJ_RECEIVED: shost_printk(KERN_DEBUG, target->scsi_host, PFX "REJ received\n"); comp = 1; - srp_cm_rej_handler(cm_id, event, target); + srp_cm_rej_handler(cm_id, event, ch); break; case IB_CM_DREQ_RECEIVED: @@ -2238,7 +2378,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) PFX "connection closed\n"); comp = 1; - target->status = 0; + ch->status = 0; break; case IB_CM_MRA_RECEIVED: @@ -2253,65 +2393,30 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) } if (comp) - complete(&target->done); + complete(&ch->done); return 0; } /** - * srp_change_queue_type - changing device queue tag type - * @sdev: scsi device struct - * @tag_type: requested tag type - * - * Returns queue tag type. - */ -static int -srp_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; -} - -/** * srp_change_queue_depth - setting device queue depth * @sdev: scsi device struct * @qdepth: requested queue depth - * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP - * (see include/scsi/scsi_host.h for definition) * * Returns queue depth. */ static int -srp_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) +srp_change_queue_depth(struct scsi_device *sdev, int qdepth) { - struct Scsi_Host *shost = sdev->host; - int max_depth; - if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) { - max_depth = shost->can_queue; - if (!sdev->tagged_supported) - max_depth = 1; - if (qdepth > max_depth) - qdepth = max_depth; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - } else if (reason == SCSI_QDEPTH_QFULL) - scsi_track_queue_full(sdev, qdepth); - else - return -EOPNOTSUPP; - - return sdev->queue_depth; + if (!sdev->tagged_supported) + qdepth = 1; + return scsi_change_queue_depth(sdev, qdepth); } -static int srp_send_tsk_mgmt(struct srp_target_port *target, - u64 req_tag, unsigned int lun, u8 func) +static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, + unsigned int lun, u8 func) { + struct srp_target_port *target = ch->target; struct srp_rport *rport = target->rport; struct ib_device *dev = target->srp_host->srp_dev->dev; struct srp_iu *iu; @@ -2320,16 +2425,16 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target, if (!target->connected || target->qp_in_error) return -1; - init_completion(&target->tsk_mgmt_done); + init_completion(&ch->tsk_mgmt_done); /* - * Lock the rport mutex to avoid that srp_create_target_ib() is + * Lock the rport mutex to avoid that srp_create_ch_ib() is * invoked while a task management function is being sent. */ mutex_lock(&rport->mutex); - spin_lock_irq(&target->lock); - iu = __srp_get_tx_iu(target, SRP_IU_TSK_MGMT); - spin_unlock_irq(&target->lock); + spin_lock_irq(&ch->lock); + iu = __srp_get_tx_iu(ch, SRP_IU_TSK_MGMT); + spin_unlock_irq(&ch->lock); if (!iu) { mutex_unlock(&rport->mutex); @@ -2350,15 +2455,15 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target, ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt, DMA_TO_DEVICE); - if (srp_post_send(target, iu, sizeof *tsk_mgmt)) { - srp_put_tx_iu(target, iu, SRP_IU_TSK_MGMT); + if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) { + srp_put_tx_iu(ch, iu, SRP_IU_TSK_MGMT); mutex_unlock(&rport->mutex); return -1; } mutex_unlock(&rport->mutex); - if (!wait_for_completion_timeout(&target->tsk_mgmt_done, + if (!wait_for_completion_timeout(&ch->tsk_mgmt_done, msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS))) return -1; @@ -2369,20 +2474,32 @@ static int srp_abort(struct scsi_cmnd *scmnd) { struct srp_target_port *target = host_to_target(scmnd->device->host); struct srp_request *req = (struct srp_request *) scmnd->host_scribble; + u32 tag; + u16 ch_idx; + struct srp_rdma_ch *ch; int ret; shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n"); - if (!req || !srp_claim_req(target, req, NULL, scmnd)) + if (!req) + return SUCCESS; + tag = blk_mq_unique_tag(scmnd->request); + ch_idx = blk_mq_unique_tag_to_hwq(tag); + if (WARN_ON_ONCE(ch_idx >= target->ch_count)) return SUCCESS; - if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun, + ch = &target->ch[ch_idx]; + if (!srp_claim_req(ch, req, NULL, scmnd)) + return SUCCESS; + shost_printk(KERN_ERR, target->scsi_host, + "Sending SRP abort for tag %#x\n", tag); + if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun, SRP_TSK_ABORT_TASK) == 0) ret = SUCCESS; else if (target->rport->state == SRP_RPORT_LOST) ret = FAST_IO_FAIL; else ret = FAILED; - srp_free_req(target, req, scmnd, 0); + srp_free_req(ch, req, scmnd, 0); scmnd->result = DID_ABORT << 16; scmnd->scsi_done(scmnd); @@ -2392,19 +2509,25 @@ static int srp_abort(struct scsi_cmnd *scmnd) static int srp_reset_device(struct scsi_cmnd *scmnd) { struct srp_target_port *target = host_to_target(scmnd->device->host); + struct srp_rdma_ch *ch; int i; shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n"); - if (srp_send_tsk_mgmt(target, SRP_TAG_NO_REQ, scmnd->device->lun, + ch = &target->ch[0]; + if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun, SRP_TSK_LUN_RESET)) return FAILED; - if (target->tsk_mgmt_status) + if (ch->tsk_mgmt_status) return FAILED; - for (i = 0; i < target->req_ring_size; ++i) { - struct srp_request *req = &target->req_ring[i]; - srp_finish_req(target, req, scmnd->device, DID_RESET << 16); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + for (i = 0; i < target->req_ring_size; ++i) { + struct srp_request *req = &ch->req_ring[i]; + + srp_finish_req(ch, req, scmnd->device, DID_RESET << 16); + } } return SUCCESS; @@ -2466,7 +2589,7 @@ static ssize_t show_pkey(struct device *dev, struct device_attribute *attr, { struct srp_target_port *target = host_to_target(class_to_shost(dev)); - return sprintf(buf, "0x%04x\n", be16_to_cpu(target->path.pkey)); + return sprintf(buf, "0x%04x\n", be16_to_cpu(target->pkey)); } static ssize_t show_sgid(struct device *dev, struct device_attribute *attr, @@ -2474,15 +2597,16 @@ static ssize_t show_sgid(struct device *dev, struct device_attribute *attr, { struct srp_target_port *target = host_to_target(class_to_shost(dev)); - return sprintf(buf, "%pI6\n", target->path.sgid.raw); + return sprintf(buf, "%pI6\n", target->sgid.raw); } static ssize_t show_dgid(struct device *dev, struct device_attribute *attr, char *buf) { struct srp_target_port *target = host_to_target(class_to_shost(dev)); + struct srp_rdma_ch *ch = &target->ch[0]; - return sprintf(buf, "%pI6\n", target->path.dgid.raw); + return sprintf(buf, "%pI6\n", ch->path.dgid.raw); } static ssize_t show_orig_dgid(struct device *dev, @@ -2490,15 +2614,21 @@ static ssize_t show_orig_dgid(struct device *dev, { struct srp_target_port *target = host_to_target(class_to_shost(dev)); - return sprintf(buf, "%pI6\n", target->orig_dgid); + return sprintf(buf, "%pI6\n", target->orig_dgid.raw); } static ssize_t show_req_lim(struct device *dev, struct device_attribute *attr, char *buf) { struct srp_target_port *target = host_to_target(class_to_shost(dev)); + struct srp_rdma_ch *ch; + int i, req_lim = INT_MAX; - return sprintf(buf, "%d\n", target->req_lim); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + req_lim = min(req_lim, ch->req_lim); + } + return sprintf(buf, "%d\n", req_lim); } static ssize_t show_zero_req_lim(struct device *dev, @@ -2525,6 +2655,14 @@ static ssize_t show_local_ib_device(struct device *dev, return sprintf(buf, "%s\n", target->srp_host->srp_dev->dev->name); } +static ssize_t show_ch_count(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct srp_target_port *target = host_to_target(class_to_shost(dev)); + + return sprintf(buf, "%d\n", target->ch_count); +} + static ssize_t show_comp_vector(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2568,6 +2706,7 @@ static DEVICE_ATTR(req_lim, S_IRUGO, show_req_lim, NULL); static DEVICE_ATTR(zero_req_lim, S_IRUGO, show_zero_req_lim, NULL); static DEVICE_ATTR(local_ib_port, S_IRUGO, show_local_ib_port, NULL); static DEVICE_ATTR(local_ib_device, S_IRUGO, show_local_ib_device, NULL); +static DEVICE_ATTR(ch_count, S_IRUGO, show_ch_count, NULL); static DEVICE_ATTR(comp_vector, S_IRUGO, show_comp_vector, NULL); static DEVICE_ATTR(tl_retry_count, S_IRUGO, show_tl_retry_count, NULL); static DEVICE_ATTR(cmd_sg_entries, S_IRUGO, show_cmd_sg_entries, NULL); @@ -2585,6 +2724,7 @@ static struct device_attribute *srp_host_attrs[] = { &dev_attr_zero_req_lim, &dev_attr_local_ib_port, &dev_attr_local_ib_device, + &dev_attr_ch_count, &dev_attr_comp_vector, &dev_attr_tl_retry_count, &dev_attr_cmd_sg_entries, @@ -2600,7 +2740,7 @@ static struct scsi_host_template srp_template = { .info = srp_target_info, .queuecommand = srp_queuecommand, .change_queue_depth = srp_change_queue_depth, - .change_queue_type = srp_change_queue_type, + .change_queue_type = scsi_change_queue_type, .eh_abort_handler = srp_abort, .eh_device_reset_handler = srp_reset_device, .eh_host_reset_handler = srp_reset_host, @@ -2610,14 +2750,28 @@ static struct scsi_host_template srp_template = { .this_id = -1, .cmd_per_lun = SRP_DEFAULT_CMD_SQ_SIZE, .use_clustering = ENABLE_CLUSTERING, - .shost_attrs = srp_host_attrs + .shost_attrs = srp_host_attrs, + .use_blk_tags = 1, + .track_queue_depth = 1, }; +static int srp_sdev_count(struct Scsi_Host *host) +{ + struct scsi_device *sdev; + int c = 0; + + shost_for_each_device(sdev, host) + c++; + + return c; +} + static int srp_add_target(struct srp_host *host, struct srp_target_port *target) { struct srp_rport_identifiers ids; struct srp_rport *rport; + target->state = SRP_TARGET_SCANNING; sprintf(target->target_name, "SRP.T10:%016llX", (unsigned long long) be64_to_cpu(target->id_ext)); @@ -2640,11 +2794,26 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target) list_add_tail(&target->list, &host->target_list); spin_unlock(&host->target_lock); - target->state = SRP_TARGET_LIVE; - scsi_scan_target(&target->scsi_host->shost_gendev, 0, target->scsi_id, SCAN_WILD_CARD, 0); + if (!target->connected || target->qp_in_error) { + shost_printk(KERN_INFO, target->scsi_host, + PFX "SCSI scan failed - removing SCSI host\n"); + srp_queue_remove_work(target); + goto out; + } + + pr_debug(PFX "%s: SCSI scan succeeded - detected %d LUNs\n", + dev_name(&target->scsi_host->shost_gendev), + srp_sdev_count(target->scsi_host)); + + spin_lock_irq(&target->lock); + if (target->state == SRP_TARGET_SCANNING) + target->state = SRP_TARGET_LIVE; + spin_unlock_irq(&target->lock); + +out: return 0; } @@ -2801,11 +2970,15 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target) } for (i = 0; i < 16; ++i) { - strlcpy(dgid, p + i * 2, 3); - target->path.dgid.raw[i] = simple_strtoul(dgid, NULL, 16); + strlcpy(dgid, p + i * 2, sizeof(dgid)); + if (sscanf(dgid, "%hhx", + &target->orig_dgid.raw[i]) < 1) { + ret = -EINVAL; + kfree(p); + goto out; + } } kfree(p); - memcpy(target->orig_dgid, target->path.dgid.raw, 16); break; case SRP_OPT_PKEY: @@ -2813,7 +2986,7 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target) pr_warn("bad P_Key parameter '%s'\n", p); goto out; } - target->path.pkey = cpu_to_be16(token); + target->pkey = cpu_to_be16(token); break; case SRP_OPT_SERVICE_ID: @@ -2823,7 +2996,6 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target) goto out; } target->service_id = cpu_to_be64(simple_strtoull(p, NULL, 16)); - target->path.service_id = target->service_id; kfree(p); break; @@ -2960,9 +3132,11 @@ static ssize_t srp_create_target(struct device *dev, container_of(dev, struct srp_host, dev); struct Scsi_Host *target_host; struct srp_target_port *target; + struct srp_rdma_ch *ch; struct srp_device *srp_dev = host->srp_dev; struct ib_device *ibdev = srp_dev->dev; - int ret; + int ret, node_idx, node, cpu, i; + bool multich = false; target_host = scsi_host_alloc(&srp_template, sizeof (struct srp_target_port)); @@ -2988,12 +3162,22 @@ static ssize_t srp_create_target(struct device *dev, target->tl_retry_count = 7; target->queue_size = SRP_DEFAULT_QUEUE_SIZE; + /* + * Avoid that the SCSI host can be removed by srp_remove_target() + * before this function returns. + */ + scsi_host_get(target->scsi_host); + mutex_lock(&host->add_target_mutex); ret = srp_parse_options(buf, target); if (ret) goto err; + ret = scsi_init_shared_tag_map(target_host, target_host->can_queue); + if (ret) + goto err; + target->req_ring_size = target->queue_size - SRP_TSK_MGMT_SQ_SIZE; if (!srp_conn_unique(target->srp_host, target)) { @@ -3022,59 +3206,115 @@ static ssize_t srp_create_target(struct device *dev, INIT_WORK(&target->tl_err_work, srp_tl_err_work); INIT_WORK(&target->remove_work, srp_remove_work); spin_lock_init(&target->lock); - INIT_LIST_HEAD(&target->free_tx); - ret = srp_alloc_req_data(target); + ret = ib_query_gid(ibdev, host->port, 0, &target->sgid); if (ret) - goto err_free_mem; + goto err; - ret = ib_query_gid(ibdev, host->port, 0, &target->path.sgid); - if (ret) - goto err_free_mem; + ret = -ENOMEM; + target->ch_count = max_t(unsigned, num_online_nodes(), + min(ch_count ? : + min(4 * num_online_nodes(), + ibdev->num_comp_vectors), + num_online_cpus())); + target->ch = kcalloc(target->ch_count, sizeof(*target->ch), + GFP_KERNEL); + if (!target->ch) + goto err; - ret = srp_create_target_ib(target); - if (ret) - goto err_free_mem; + node_idx = 0; + for_each_online_node(node) { + const int ch_start = (node_idx * target->ch_count / + num_online_nodes()); + const int ch_end = ((node_idx + 1) * target->ch_count / + num_online_nodes()); + const int cv_start = (node_idx * ibdev->num_comp_vectors / + num_online_nodes() + target->comp_vector) + % ibdev->num_comp_vectors; + const int cv_end = ((node_idx + 1) * ibdev->num_comp_vectors / + num_online_nodes() + target->comp_vector) + % ibdev->num_comp_vectors; + int cpu_idx = 0; + + for_each_online_cpu(cpu) { + if (cpu_to_node(cpu) != node) + continue; + if (ch_start + cpu_idx >= ch_end) + continue; + ch = &target->ch[ch_start + cpu_idx]; + ch->target = target; + ch->comp_vector = cv_start == cv_end ? cv_start : + cv_start + cpu_idx % (cv_end - cv_start); + spin_lock_init(&ch->lock); + INIT_LIST_HEAD(&ch->free_tx); + ret = srp_new_cm_id(ch); + if (ret) + goto err_disconnect; - ret = srp_new_cm_id(target); - if (ret) - goto err_free_ib; + ret = srp_create_ch_ib(ch); + if (ret) + goto err_disconnect; - ret = srp_connect_target(target); - if (ret) { - shost_printk(KERN_ERR, target->scsi_host, - PFX "Connection failed\n"); - goto err_cm_id; + ret = srp_alloc_req_data(ch); + if (ret) + goto err_disconnect; + + ret = srp_connect_ch(ch, multich); + if (ret) { + shost_printk(KERN_ERR, target->scsi_host, + PFX "Connection %d/%d failed\n", + ch_start + cpu_idx, + target->ch_count); + if (node_idx == 0 && cpu_idx == 0) { + goto err_disconnect; + } else { + srp_free_ch_ib(target, ch); + srp_free_req_data(target, ch); + target->ch_count = ch - target->ch; + break; + } + } + + multich = true; + cpu_idx++; + } + node_idx++; } + target->scsi_host->nr_hw_queues = target->ch_count; + ret = srp_add_target(host, target); if (ret) goto err_disconnect; - shost_printk(KERN_DEBUG, target->scsi_host, PFX - "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n", - be64_to_cpu(target->id_ext), - be64_to_cpu(target->ioc_guid), - be16_to_cpu(target->path.pkey), - be64_to_cpu(target->service_id), - target->path.sgid.raw, target->path.dgid.raw); + if (target->state != SRP_TARGET_REMOVED) { + shost_printk(KERN_DEBUG, target->scsi_host, PFX + "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n", + be64_to_cpu(target->id_ext), + be64_to_cpu(target->ioc_guid), + be16_to_cpu(target->pkey), + be64_to_cpu(target->service_id), + target->sgid.raw, target->orig_dgid.raw); + } ret = count; out: mutex_unlock(&host->add_target_mutex); + + scsi_host_put(target->scsi_host); + return ret; err_disconnect: srp_disconnect_target(target); -err_cm_id: - ib_destroy_cm_id(target->cm_id); - -err_free_ib: - srp_free_target_ib(target); + for (i = 0; i < target->ch_count; i++) { + ch = &target->ch[i]; + srp_free_ch_ib(target, ch); + srp_free_req_data(target, ch); + } -err_free_mem: - srp_free_req_data(target); + kfree(target->ch); err: scsi_host_put(target_host); diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index e46ecb15aa0d..a611556406ac 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -70,9 +70,12 @@ enum { LOCAL_INV_WR_ID_MASK = 1, FAST_REG_WR_ID_MASK = 2, + + SRP_LAST_WR_ID = 0xfffffffcU, }; enum srp_target_state { + SRP_TARGET_SCANNING, SRP_TARGET_LIVE, SRP_TARGET_REMOVED, }; @@ -115,7 +118,6 @@ struct srp_host { }; struct srp_request { - struct list_head list; struct scsi_cmnd *scmnd; struct srp_iu *cmd; union { @@ -126,24 +128,62 @@ struct srp_request { struct srp_direct_buf *indirect_desc; dma_addr_t indirect_dma_addr; short nmdesc; - short index; }; -struct srp_target_port { +/** + * struct srp_rdma_ch + * @comp_vector: Completion vector used by this RDMA channel. + */ +struct srp_rdma_ch { /* These are RW in the hot path, and commonly used together */ struct list_head free_tx; - struct list_head free_reqs; spinlock_t lock; s32 req_lim; /* These are read-only in the hot path */ - struct ib_cq *send_cq ____cacheline_aligned_in_smp; + struct srp_target_port *target ____cacheline_aligned_in_smp; + struct ib_cq *send_cq; struct ib_cq *recv_cq; struct ib_qp *qp; union { struct ib_fmr_pool *fmr_pool; struct srp_fr_pool *fr_pool; }; + + /* Everything above this point is used in the hot path of + * command processing. Try to keep them packed into cachelines. + */ + + struct completion done; + int status; + + struct ib_sa_path_rec path; + struct ib_sa_query *path_query; + int path_query_id; + + struct ib_cm_id *cm_id; + struct srp_iu **tx_ring; + struct srp_iu **rx_ring; + struct srp_request *req_ring; + int max_ti_iu_len; + int comp_vector; + + struct completion tsk_mgmt_done; + u8 tsk_mgmt_status; +}; + +/** + * struct srp_target_port + * @comp_vector: Completion vector used by the first RDMA channel created for + * this target port. + */ +struct srp_target_port { + /* read and written in the hot path */ + spinlock_t lock; + + /* read only in the hot path */ + struct srp_rdma_ch *ch; + u32 ch_count; u32 lkey; u32 rkey; enum srp_target_state state; @@ -152,10 +192,8 @@ struct srp_target_port { unsigned int indirect_size; bool allow_ext_sg; - /* Everything above this point is used in the hot path of - * command processing. Try to keep them packed into cachelines. - */ - + /* other member variables */ + union ib_gid sgid; __be64 id_ext; __be64 ioc_guid; __be64 service_id; @@ -172,34 +210,19 @@ struct srp_target_port { int comp_vector; int tl_retry_count; - struct ib_sa_path_rec path; - __be16 orig_dgid[8]; - struct ib_sa_query *path_query; - int path_query_id; + union ib_gid orig_dgid; + __be16 pkey; u32 rq_tmo_jiffies; bool connected; - struct ib_cm_id *cm_id; - - int max_ti_iu_len; - int zero_req_lim; - struct srp_iu **tx_ring; - struct srp_iu **rx_ring; - struct srp_request *req_ring; - struct work_struct tl_err_work; struct work_struct remove_work; struct list_head list; - struct completion done; - int status; bool qp_in_error; - - struct completion tsk_mgmt_done; - u8 tsk_mgmt_status; }; struct srp_iu { diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index bc203485716d..8afa28e4570e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -421,7 +421,7 @@ static int evdev_open(struct inode *inode, struct file *file) err_free_client: evdev_detach_client(evdev, client); - kfree(client); + kvfree(client); return error; } diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 2ed7905a068f..fc55f0d15b70 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1179,9 +1179,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id } ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; - usb_fill_bulk_urb(xpad->bulk_out, udev, - usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), - xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); + if (usb_endpoint_is_bulk_out(ep_irq_in)) { + usb_fill_bulk_urb(xpad->bulk_out, udev, + usb_sndbulkpipe(udev, + ep_irq_in->bEndpointAddress), + xpad->bdata, XPAD_PKT_LEN, + xpad_bulk_out, xpad); + } else { + usb_fill_int_urb(xpad->bulk_out, udev, + usb_sndintpipe(udev, + ep_irq_in->bEndpointAddress), + xpad->bdata, XPAD_PKT_LEN, + xpad_bulk_out, xpad, 0); + } /* * Submit the int URB immediately rather than waiting for open diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 3fcb6b3cb0bd..f2b978026407 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -428,14 +428,6 @@ static void elantech_report_trackpoint(struct psmouse *psmouse, int x, y; u32 t; - if (dev_WARN_ONCE(&psmouse->ps2dev.serio->dev, - !tp_dev, - psmouse_fmt("Unexpected trackpoint message\n"))) { - if (etd->debug == 1) - elantech_packet_dump(psmouse); - return; - } - t = get_unaligned_le32(&packet[0]); switch (t & ~7U) { @@ -793,7 +785,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) unsigned char packet_type = packet[3] & 0x03; bool sanity_check; - if ((packet[3] & 0x0f) == 0x06) + if (etd->tp_dev && (packet[3] & 0x0f) == 0x06) return PACKET_TRACKPOINT; /* diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2a7a9174c702..f9472920d986 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -143,6 +143,10 @@ static const struct min_max_quirk min_max_pnpid_table[] = { (const char * const []){"LEN2001", NULL}, 1024, 5022, 2508, 4832 }, + { + (const char * const []){"LEN2006", NULL}, + 1264, 5675, 1171, 4688 + }, { } }; diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index dd5112265cc9..6dbfbc209491 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -163,14 +163,14 @@ config TEGRA_IOMMU_GART hardware included on Tegra SoCs. config TEGRA_IOMMU_SMMU - bool "Tegra SMMU IOMMU Support" - depends on ARCH_TEGRA && TEGRA_AHB + bool "NVIDIA Tegra SMMU Support" + depends on ARCH_TEGRA + depends on TEGRA_AHB + depends on TEGRA_MC select IOMMU_API help - Enables support for remapping discontiguous physical memory - shared with the operating system into contiguous I/O virtual - space through the SMMU (System Memory Management Unit) - hardware included on Tegra SoCs. + This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra + SoCs (Tegra30 up to Tegra124). config EXYNOS_IOMMU bool "Exynos IOMMU Support" diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 505a9adac2d5..2d84c9edf3b8 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3424,6 +3424,7 @@ static const struct iommu_ops amd_iommu_ops = { .detach_dev = amd_iommu_detach_device, .map = amd_iommu_map, .unmap = amd_iommu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = amd_iommu_iova_to_phys, .pgsize_bitmap = AMD_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 60558f794922..e393ae01b5d2 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1652,6 +1652,7 @@ static const struct iommu_ops arm_smmu_ops = { .detach_dev = arm_smmu_detach_dev, .map = arm_smmu_map, .unmap = arm_smmu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = arm_smmu_iova_to_phys, .add_device = arm_smmu_add_device, .remove_device = arm_smmu_remove_device, diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 74233186f6f7..28372b85d8da 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1178,6 +1178,7 @@ static const struct iommu_ops exynos_iommu_ops = { .detach_dev = exynos_iommu_detach_device, .map = exynos_iommu_map, .unmap = exynos_iommu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = exynos_iommu_iova_to_phys, .add_device = exynos_iommu_add_device, .remove_device = exynos_iommu_remove_device, diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index a27d6cb1a793..02cd26a17fe0 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4467,6 +4467,7 @@ static const struct iommu_ops intel_iommu_ops = { .detach_dev = intel_iommu_detach_device, .map = intel_iommu_map, .unmap = intel_iommu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = intel_iommu_iova_to_phys, .add_device = intel_iommu_add_device, .remove_device = intel_iommu_remove_device, diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index ed8b04867b1f..02e4313e937c 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) kfree(nb); return err; } - return bus_for_each_dev(bus, NULL, &cb, add_iommu_group); + + err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group); + if (err) { + bus_unregister_notifier(bus, nb); + kfree(nb); + return err; + } + + return 0; } /** @@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) */ int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) { + int err; + if (bus->iommu_ops != NULL) return -EBUSY; bus->iommu_ops = ops; /* Do IOMMU specific setup for this bus-type */ - return iommu_bus_init(bus, ops); + err = iommu_bus_init(bus, ops); + if (err) + bus->iommu_ops = NULL; + + return err; } EXPORT_SYMBOL_GPL(bus_set_iommu); @@ -1124,6 +1138,38 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) } EXPORT_SYMBOL_GPL(iommu_unmap); +size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, + struct scatterlist *sg, unsigned int nents, int prot) +{ + struct scatterlist *s; + size_t mapped = 0; + unsigned int i; + int ret; + + for_each_sg(sg, s, nents, i) { + phys_addr_t phys = page_to_phys(sg_page(s)); + + /* We are mapping on page boundarys, so offset must be 0 */ + if (s->offset) + goto out_err; + + ret = iommu_map(domain, iova + mapped, phys, s->length, prot); + if (ret) + goto out_err; + + mapped += s->length; + } + + return mapped; + +out_err: + /* undo mappings already done */ + iommu_unmap(domain, iova, mapped); + + return 0; + +} +EXPORT_SYMBOL_GPL(default_iommu_map_sg); int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t paddr, u64 size, int prot) diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 7dab5cbcc775..e509c58eee92 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = { .detach_dev = ipmmu_detach_device, .map = ipmmu_map, .unmap = ipmmu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = ipmmu_iova_to_phys, .add_device = ipmmu_add_device, .remove_device = ipmmu_remove_device, diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 6e3dcc289d59..1c7b78ecf3e3 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = { .detach_dev = msm_iommu_detach_dev, .map = msm_iommu_map, .unmap = msm_iommu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = msm_iommu_iova_to_phys, .pgsize_bitmap = MSM_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 36278870e84a..18003c044454 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = { .detach_dev = omap_iommu_detach_dev, .map = omap_iommu_map, .unmap = omap_iommu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = omap_iommu_iova_to_phys, .add_device = omap_iommu_add_device, .remove_device = omap_iommu_remove_device, diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c index 1333e6fb3405..f1b00774e4de 100644 --- a/drivers/iommu/shmobile-iommu.c +++ b/drivers/iommu/shmobile-iommu.c @@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = { .detach_dev = shmobile_iommu_detach_device, .map = shmobile_iommu_map, .unmap = shmobile_iommu_unmap, + .map_sg = default_iommu_map_sg, .iova_to_phys = shmobile_iommu_iova_to_phys, .add_device = shmobile_iommu_add_device, .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 3afdf43f732a..6e134c7c227f 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1,1295 +1,732 @@ /* - * IOMMU API for SMMU in Tegra30 + * Copyright (C) 2011-2014 NVIDIA CORPORATION. All rights reserved. * - * Copyright (c) 2011-2013, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ -#define pr_fmt(fmt) "%s(): " fmt, __func__ - #include <linux/err.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/device.h> -#include <linux/sched.h> #include <linux/iommu.h> -#include <linux/io.h> +#include <linux/kernel.h> #include <linux/of.h> -#include <linux/of_iommu.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> #include <soc/tegra/ahb.h> +#include <soc/tegra/mc.h> -#include <asm/page.h> -#include <asm/cacheflush.h> - -enum smmu_hwgrp { - HWGRP_AFI, - HWGRP_AVPC, - HWGRP_DC, - HWGRP_DCB, - HWGRP_EPP, - HWGRP_G2, - HWGRP_HC, - HWGRP_HDA, - HWGRP_ISP, - HWGRP_MPE, - HWGRP_NV, - HWGRP_NV2, - HWGRP_PPCS, - HWGRP_SATA, - HWGRP_VDE, - HWGRP_VI, - - HWGRP_COUNT, - - HWGRP_END = ~0, -}; +struct tegra_smmu { + void __iomem *regs; + struct device *dev; -#define HWG_AFI (1 << HWGRP_AFI) -#define HWG_AVPC (1 << HWGRP_AVPC) -#define HWG_DC (1 << HWGRP_DC) -#define HWG_DCB (1 << HWGRP_DCB) -#define HWG_EPP (1 << HWGRP_EPP) -#define HWG_G2 (1 << HWGRP_G2) -#define HWG_HC (1 << HWGRP_HC) -#define HWG_HDA (1 << HWGRP_HDA) -#define HWG_ISP (1 << HWGRP_ISP) -#define HWG_MPE (1 << HWGRP_MPE) -#define HWG_NV (1 << HWGRP_NV) -#define HWG_NV2 (1 << HWGRP_NV2) -#define HWG_PPCS (1 << HWGRP_PPCS) -#define HWG_SATA (1 << HWGRP_SATA) -#define HWG_VDE (1 << HWGRP_VDE) -#define HWG_VI (1 << HWGRP_VI) - -/* bitmap of the page sizes currently supported */ -#define SMMU_IOMMU_PGSIZES (SZ_4K) - -#define SMMU_CONFIG 0x10 -#define SMMU_CONFIG_DISABLE 0 -#define SMMU_CONFIG_ENABLE 1 - -/* REVISIT: To support multiple MCs */ -enum { - _MC = 0, -}; + struct tegra_mc *mc; + const struct tegra_smmu_soc *soc; -enum { - _TLB = 0, - _PTC, -}; + unsigned long *asids; + struct mutex lock; -#define SMMU_CACHE_CONFIG_BASE 0x14 -#define __SMMU_CACHE_CONFIG(mc, cache) (SMMU_CACHE_CONFIG_BASE + 4 * cache) -#define SMMU_CACHE_CONFIG(cache) __SMMU_CACHE_CONFIG(_MC, cache) - -#define SMMU_CACHE_CONFIG_STATS_SHIFT 31 -#define SMMU_CACHE_CONFIG_STATS_ENABLE (1 << SMMU_CACHE_CONFIG_STATS_SHIFT) -#define SMMU_CACHE_CONFIG_STATS_TEST_SHIFT 30 -#define SMMU_CACHE_CONFIG_STATS_TEST (1 << SMMU_CACHE_CONFIG_STATS_TEST_SHIFT) - -#define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) -#define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 -#define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 - -#define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) -#define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f -#define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f - -#define SMMU_PTB_ASID 0x1c -#define SMMU_PTB_ASID_CURRENT_SHIFT 0 - -#define SMMU_PTB_DATA 0x20 -#define SMMU_PTB_DATA_RESET_VAL 0 -#define SMMU_PTB_DATA_ASID_NONSECURE_SHIFT 29 -#define SMMU_PTB_DATA_ASID_WRITABLE_SHIFT 30 -#define SMMU_PTB_DATA_ASID_READABLE_SHIFT 31 - -#define SMMU_TLB_FLUSH 0x30 -#define SMMU_TLB_FLUSH_VA_MATCH_ALL 0 -#define SMMU_TLB_FLUSH_VA_MATCH_SECTION 2 -#define SMMU_TLB_FLUSH_VA_MATCH_GROUP 3 -#define SMMU_TLB_FLUSH_ASID_SHIFT 29 -#define SMMU_TLB_FLUSH_ASID_MATCH_DISABLE 0 -#define SMMU_TLB_FLUSH_ASID_MATCH_ENABLE 1 -#define SMMU_TLB_FLUSH_ASID_MATCH_SHIFT 31 - -#define SMMU_PTC_FLUSH 0x34 -#define SMMU_PTC_FLUSH_TYPE_ALL 0 -#define SMMU_PTC_FLUSH_TYPE_ADR 1 -#define SMMU_PTC_FLUSH_ADR_SHIFT 4 - -#define SMMU_ASID_SECURITY 0x38 - -#define SMMU_STATS_CACHE_COUNT_BASE 0x1f0 - -#define SMMU_STATS_CACHE_COUNT(mc, cache, hitmiss) \ - (SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss) - -#define SMMU_TRANSLATION_ENABLE_0 0x228 -#define SMMU_TRANSLATION_ENABLE_1 0x22c -#define SMMU_TRANSLATION_ENABLE_2 0x230 - -#define SMMU_AFI_ASID 0x238 /* PCIE */ -#define SMMU_AVPC_ASID 0x23c /* AVP */ -#define SMMU_DC_ASID 0x240 /* Display controller */ -#define SMMU_DCB_ASID 0x244 /* Display controller B */ -#define SMMU_EPP_ASID 0x248 /* Encoder pre-processor */ -#define SMMU_G2_ASID 0x24c /* 2D engine */ -#define SMMU_HC_ASID 0x250 /* Host1x */ -#define SMMU_HDA_ASID 0x254 /* High-def audio */ -#define SMMU_ISP_ASID 0x258 /* Image signal processor */ -#define SMMU_MPE_ASID 0x264 /* MPEG encoder */ -#define SMMU_NV_ASID 0x268 /* (3D) */ -#define SMMU_NV2_ASID 0x26c /* (3D) */ -#define SMMU_PPCS_ASID 0x270 /* AHB */ -#define SMMU_SATA_ASID 0x278 /* SATA */ -#define SMMU_VDE_ASID 0x27c /* Video decoder */ -#define SMMU_VI_ASID 0x280 /* Video input */ - -#define SMMU_PDE_NEXT_SHIFT 28 - -#define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 -#define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ -#define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 -#define SMMU_TLB_FLUSH_VA_GROUP__SHIFT 12 /* right shift */ -#define SMMU_TLB_FLUSH_VA(iova, which) \ - ((((iova) & SMMU_TLB_FLUSH_VA_##which##__MASK) >> \ - SMMU_TLB_FLUSH_VA_##which##__SHIFT) | \ - SMMU_TLB_FLUSH_VA_MATCH_##which) -#define SMMU_PTB_ASID_CUR(n) \ - ((n) << SMMU_PTB_ASID_CURRENT_SHIFT) -#define SMMU_TLB_FLUSH_ASID_MATCH_disable \ - (SMMU_TLB_FLUSH_ASID_MATCH_DISABLE << \ - SMMU_TLB_FLUSH_ASID_MATCH_SHIFT) -#define SMMU_TLB_FLUSH_ASID_MATCH__ENABLE \ - (SMMU_TLB_FLUSH_ASID_MATCH_ENABLE << \ - SMMU_TLB_FLUSH_ASID_MATCH_SHIFT) - -#define SMMU_PAGE_SHIFT 12 -#define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) -#define SMMU_PAGE_MASK ((1 << SMMU_PAGE_SHIFT) - 1) - -#define SMMU_PDIR_COUNT 1024 -#define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) -#define SMMU_PTBL_COUNT 1024 -#define SMMU_PTBL_SIZE (sizeof(unsigned long) * SMMU_PTBL_COUNT) -#define SMMU_PDIR_SHIFT 12 -#define SMMU_PDE_SHIFT 12 -#define SMMU_PTE_SHIFT 12 -#define SMMU_PFN_MASK 0x000fffff - -#define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12) -#define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22) -#define SMMU_PDN_TO_ADDR(pdn) ((pdn) << 22) - -#define _READABLE (1 << SMMU_PTB_DATA_ASID_READABLE_SHIFT) -#define _WRITABLE (1 << SMMU_PTB_DATA_ASID_WRITABLE_SHIFT) -#define _NONSECURE (1 << SMMU_PTB_DATA_ASID_NONSECURE_SHIFT) -#define _PDE_NEXT (1 << SMMU_PDE_NEXT_SHIFT) -#define _MASK_ATTR (_READABLE | _WRITABLE | _NONSECURE) - -#define _PDIR_ATTR (_READABLE | _WRITABLE | _NONSECURE) - -#define _PDE_ATTR (_READABLE | _WRITABLE | _NONSECURE) -#define _PDE_ATTR_N (_PDE_ATTR | _PDE_NEXT) -#define _PDE_VACANT(pdn) (((pdn) << 10) | _PDE_ATTR) - -#define _PTE_ATTR (_READABLE | _WRITABLE | _NONSECURE) -#define _PTE_VACANT(addr) (((addr) >> SMMU_PAGE_SHIFT) | _PTE_ATTR) - -#define SMMU_MK_PDIR(page, attr) \ - ((page_to_phys(page) >> SMMU_PDIR_SHIFT) | (attr)) -#define SMMU_MK_PDE(page, attr) \ - (unsigned long)((page_to_phys(page) >> SMMU_PDE_SHIFT) | (attr)) -#define SMMU_EX_PTBL_PAGE(pde) \ - pfn_to_page((unsigned long)(pde) & SMMU_PFN_MASK) -#define SMMU_PFN_TO_PTE(pfn, attr) (unsigned long)((pfn) | (attr)) - -#define SMMU_ASID_ENABLE(asid) ((asid) | (1 << 31)) -#define SMMU_ASID_DISABLE 0 -#define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) - -#define NUM_SMMU_REG_BANKS 3 - -#define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) -#define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) -#define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) -#define __smmu_client_disable_hwgrp(c) __smmu_client_set_hwgrp(c, 0, 0) - -#define HWGRP_INIT(client) [HWGRP_##client] = SMMU_##client##_ASID - -static const u32 smmu_hwgrp_asid_reg[] = { - HWGRP_INIT(AFI), - HWGRP_INIT(AVPC), - HWGRP_INIT(DC), - HWGRP_INIT(DCB), - HWGRP_INIT(EPP), - HWGRP_INIT(G2), - HWGRP_INIT(HC), - HWGRP_INIT(HDA), - HWGRP_INIT(ISP), - HWGRP_INIT(MPE), - HWGRP_INIT(NV), - HWGRP_INIT(NV2), - HWGRP_INIT(PPCS), - HWGRP_INIT(SATA), - HWGRP_INIT(VDE), - HWGRP_INIT(VI), + struct list_head list; }; -#define HWGRP_ASID_REG(x) (smmu_hwgrp_asid_reg[x]) -/* - * Per client for address space - */ -struct smmu_client { - struct device *dev; - struct list_head list; - struct smmu_as *as; - u32 hwgrp; +struct tegra_smmu_as { + struct iommu_domain *domain; + struct tegra_smmu *smmu; + unsigned int use_count; + struct page *count; + struct page *pd; + unsigned id; + u32 attr; }; -/* - * Per address space - */ -struct smmu_as { - struct smmu_device *smmu; /* back pointer to container */ - unsigned int asid; - spinlock_t lock; /* for pagetable */ - struct page *pdir_page; - unsigned long pdir_attr; - unsigned long pde_attr; - unsigned long pte_attr; - unsigned int *pte_count; - - struct list_head client; - spinlock_t client_lock; /* for client list */ -}; +static inline void smmu_writel(struct tegra_smmu *smmu, u32 value, + unsigned long offset) +{ + writel(value, smmu->regs + offset); +} -struct smmu_debugfs_info { - struct smmu_device *smmu; - int mc; - int cache; -}; +static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) +{ + return readl(smmu->regs + offset); +} -/* - * Per SMMU device - IOMMU device - */ -struct smmu_device { - void __iomem *regbase; /* register offset base */ - void __iomem **regs; /* register block start address array */ - void __iomem **rege; /* register block end address array */ - int nregs; /* number of register blocks */ - - unsigned long iovmm_base; /* remappable base address */ - unsigned long page_count; /* total remappable size */ - spinlock_t lock; - char *name; - struct device *dev; - struct page *avp_vector_page; /* dummy page shared by all AS's */ +#define SMMU_CONFIG 0x010 +#define SMMU_CONFIG_ENABLE (1 << 0) - /* - * Register image savers for suspend/resume - */ - unsigned long translation_enable_0; - unsigned long translation_enable_1; - unsigned long translation_enable_2; - unsigned long asid_security; +#define SMMU_TLB_CONFIG 0x14 +#define SMMU_TLB_CONFIG_HIT_UNDER_MISS (1 << 29) +#define SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION (1 << 28) +#define SMMU_TLB_CONFIG_ACTIVE_LINES(x) ((x) & 0x3f) - struct dentry *debugfs_root; - struct smmu_debugfs_info *debugfs_info; +#define SMMU_PTC_CONFIG 0x18 +#define SMMU_PTC_CONFIG_ENABLE (1 << 29) +#define SMMU_PTC_CONFIG_REQ_LIMIT(x) (((x) & 0x0f) << 24) +#define SMMU_PTC_CONFIG_INDEX_MAP(x) ((x) & 0x3f) - struct device_node *ahb; +#define SMMU_PTB_ASID 0x01c +#define SMMU_PTB_ASID_VALUE(x) ((x) & 0x7f) - int num_as; - struct smmu_as as[0]; /* Run-time allocated array */ -}; +#define SMMU_PTB_DATA 0x020 +#define SMMU_PTB_DATA_VALUE(page, attr) (page_to_phys(page) >> 12 | (attr)) -static struct smmu_device *smmu_handle; /* unique for a system */ +#define SMMU_MK_PDE(page, attr) (page_to_phys(page) >> SMMU_PTE_SHIFT | (attr)) -/* - * SMMU register accessors - */ -static bool inline smmu_valid_reg(struct smmu_device *smmu, - void __iomem *addr) -{ - int i; +#define SMMU_TLB_FLUSH 0x030 +#define SMMU_TLB_FLUSH_VA_MATCH_ALL (0 << 0) +#define SMMU_TLB_FLUSH_VA_MATCH_SECTION (2 << 0) +#define SMMU_TLB_FLUSH_VA_MATCH_GROUP (3 << 0) +#define SMMU_TLB_FLUSH_ASID(x) (((x) & 0x7f) << 24) +#define SMMU_TLB_FLUSH_VA_SECTION(addr) ((((addr) & 0xffc00000) >> 12) | \ + SMMU_TLB_FLUSH_VA_MATCH_SECTION) +#define SMMU_TLB_FLUSH_VA_GROUP(addr) ((((addr) & 0xffffc000) >> 12) | \ + SMMU_TLB_FLUSH_VA_MATCH_GROUP) +#define SMMU_TLB_FLUSH_ASID_MATCH (1 << 31) - for (i = 0; i < smmu->nregs; i++) { - if (addr < smmu->regs[i]) - break; - if (addr <= smmu->rege[i]) - return true; - } +#define SMMU_PTC_FLUSH 0x034 +#define SMMU_PTC_FLUSH_TYPE_ALL (0 << 0) +#define SMMU_PTC_FLUSH_TYPE_ADR (1 << 0) - return false; -} +#define SMMU_PTC_FLUSH_HI 0x9b8 +#define SMMU_PTC_FLUSH_HI_MASK 0x3 -static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) -{ - void __iomem *addr = smmu->regbase + offs; +/* per-SWGROUP SMMU_*_ASID register */ +#define SMMU_ASID_ENABLE (1 << 31) +#define SMMU_ASID_MASK 0x7f +#define SMMU_ASID_VALUE(x) ((x) & SMMU_ASID_MASK) - BUG_ON(!smmu_valid_reg(smmu, addr)); +/* page table definitions */ +#define SMMU_NUM_PDE 1024 +#define SMMU_NUM_PTE 1024 - return readl(addr); -} +#define SMMU_SIZE_PD (SMMU_NUM_PDE * 4) +#define SMMU_SIZE_PT (SMMU_NUM_PTE * 4) -static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) -{ - void __iomem *addr = smmu->regbase + offs; +#define SMMU_PDE_SHIFT 22 +#define SMMU_PTE_SHIFT 12 - BUG_ON(!smmu_valid_reg(smmu, addr)); +#define SMMU_PFN_MASK 0x000fffff - writel(val, addr); -} +#define SMMU_PD_READABLE (1 << 31) +#define SMMU_PD_WRITABLE (1 << 30) +#define SMMU_PD_NONSECURE (1 << 29) -#define VA_PAGE_TO_PA(va, page) \ - (page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK)) +#define SMMU_PDE_READABLE (1 << 31) +#define SMMU_PDE_WRITABLE (1 << 30) +#define SMMU_PDE_NONSECURE (1 << 29) +#define SMMU_PDE_NEXT (1 << 28) -#define FLUSH_CPU_DCACHE(va, page, size) \ - do { \ - unsigned long _pa_ = VA_PAGE_TO_PA(va, page); \ - __cpuc_flush_dcache_area((void *)(va), (size_t)(size)); \ - outer_flush_range(_pa_, _pa_+(size_t)(size)); \ - } while (0) +#define SMMU_PTE_READABLE (1 << 31) +#define SMMU_PTE_WRITABLE (1 << 30) +#define SMMU_PTE_NONSECURE (1 << 29) -/* - * Any interaction between any block on PPSB and a block on APB or AHB - * must have these read-back barriers to ensure the APB/AHB bus - * transaction is complete before initiating activity on the PPSB - * block. - */ -#define FLUSH_SMMU_REGS(smmu) smmu_read(smmu, SMMU_CONFIG) +#define SMMU_PDE_ATTR (SMMU_PDE_READABLE | SMMU_PDE_WRITABLE | \ + SMMU_PDE_NONSECURE) +#define SMMU_PTE_ATTR (SMMU_PTE_READABLE | SMMU_PTE_WRITABLE | \ + SMMU_PTE_NONSECURE) -#define smmu_client_hwgrp(c) (u32)((c)->dev->platform_data) - -static int __smmu_client_set_hwgrp(struct smmu_client *c, - unsigned long map, int on) +static inline void smmu_flush_ptc(struct tegra_smmu *smmu, struct page *page, + unsigned long offset) { - int i; - struct smmu_as *as = c->as; - u32 val, offs, mask = SMMU_ASID_ENABLE(as->asid); - struct smmu_device *smmu = as->smmu; - - WARN_ON(!on && map); - if (on && !map) - return -EINVAL; - if (!on) - map = smmu_client_hwgrp(c); - - for_each_set_bit(i, &map, HWGRP_COUNT) { - offs = HWGRP_ASID_REG(i); - val = smmu_read(smmu, offs); - if (on) { - if (WARN_ON(val & mask)) - goto err_hw_busy; - val |= mask; - } else { - WARN_ON((val & mask) == mask); - val &= ~mask; + phys_addr_t phys = page ? page_to_phys(page) : 0; + u32 value; + + if (page) { + offset &= ~(smmu->mc->soc->atom_size - 1); + + if (smmu->mc->soc->num_address_bits > 32) { +#ifdef CONFIG_PHYS_ADDR_T_64BIT + value = (phys >> 32) & SMMU_PTC_FLUSH_HI_MASK; +#else + value = 0; +#endif + smmu_writel(smmu, value, SMMU_PTC_FLUSH_HI); } - smmu_write(smmu, val, offs); - } - FLUSH_SMMU_REGS(smmu); - c->hwgrp = map; - return 0; -err_hw_busy: - for_each_set_bit(i, &map, HWGRP_COUNT) { - offs = HWGRP_ASID_REG(i); - val = smmu_read(smmu, offs); - val &= ~mask; - smmu_write(smmu, val, offs); + value = (phys + offset) | SMMU_PTC_FLUSH_TYPE_ADR; + } else { + value = SMMU_PTC_FLUSH_TYPE_ALL; } - return -EBUSY; + + smmu_writel(smmu, value, SMMU_PTC_FLUSH); } -static int smmu_client_set_hwgrp(struct smmu_client *c, u32 map, int on) +static inline void smmu_flush_tlb(struct tegra_smmu *smmu) { - u32 val; - unsigned long flags; - struct smmu_as *as = c->as; - struct smmu_device *smmu = as->smmu; - - spin_lock_irqsave(&smmu->lock, flags); - val = __smmu_client_set_hwgrp(c, map, on); - spin_unlock_irqrestore(&smmu->lock, flags); - return val; + smmu_writel(smmu, SMMU_TLB_FLUSH_VA_MATCH_ALL, SMMU_TLB_FLUSH); } -/* - * Flush all TLB entries and all PTC entries - * Caller must lock smmu - */ -static void smmu_flush_regs(struct smmu_device *smmu, int enable) +static inline void smmu_flush_tlb_asid(struct tegra_smmu *smmu, + unsigned long asid) { - u32 val; - - smmu_write(smmu, SMMU_PTC_FLUSH_TYPE_ALL, SMMU_PTC_FLUSH); - FLUSH_SMMU_REGS(smmu); - val = SMMU_TLB_FLUSH_VA_MATCH_ALL | - SMMU_TLB_FLUSH_ASID_MATCH_disable; - smmu_write(smmu, val, SMMU_TLB_FLUSH); + u32 value; - if (enable) - smmu_write(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); - FLUSH_SMMU_REGS(smmu); + value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | + SMMU_TLB_FLUSH_VA_MATCH_ALL; + smmu_writel(smmu, value, SMMU_TLB_FLUSH); } -static int smmu_setup_regs(struct smmu_device *smmu) +static inline void smmu_flush_tlb_section(struct tegra_smmu *smmu, + unsigned long asid, + unsigned long iova) { - int i; - u32 val; + u32 value; - for (i = 0; i < smmu->num_as; i++) { - struct smmu_as *as = &smmu->as[i]; - struct smmu_client *c; - - smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); - val = as->pdir_page ? - SMMU_MK_PDIR(as->pdir_page, as->pdir_attr) : - SMMU_PTB_DATA_RESET_VAL; - smmu_write(smmu, val, SMMU_PTB_DATA); - - list_for_each_entry(c, &as->client, list) - __smmu_client_set_hwgrp(c, c->hwgrp, 1); - } - - smmu_write(smmu, smmu->translation_enable_0, SMMU_TRANSLATION_ENABLE_0); - smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); - smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); - smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); - smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_TLB)); - smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC)); - - smmu_flush_regs(smmu, 1); - - return tegra_ahb_enable_smmu(smmu->ahb); + value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | + SMMU_TLB_FLUSH_VA_SECTION(iova); + smmu_writel(smmu, value, SMMU_TLB_FLUSH); } -static void flush_ptc_and_tlb(struct smmu_device *smmu, - struct smmu_as *as, dma_addr_t iova, - unsigned long *pte, struct page *page, int is_pde) +static inline void smmu_flush_tlb_group(struct tegra_smmu *smmu, + unsigned long asid, + unsigned long iova) { - u32 val; - unsigned long tlb_flush_va = is_pde - ? SMMU_TLB_FLUSH_VA(iova, SECTION) - : SMMU_TLB_FLUSH_VA(iova, GROUP); - - val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pte, page); - smmu_write(smmu, val, SMMU_PTC_FLUSH); - FLUSH_SMMU_REGS(smmu); - val = tlb_flush_va | - SMMU_TLB_FLUSH_ASID_MATCH__ENABLE | - (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT); - smmu_write(smmu, val, SMMU_TLB_FLUSH); - FLUSH_SMMU_REGS(smmu); -} + u32 value; -static void free_ptbl(struct smmu_as *as, dma_addr_t iova) -{ - unsigned long pdn = SMMU_ADDR_TO_PDN(iova); - unsigned long *pdir = (unsigned long *)page_address(as->pdir_page); - - if (pdir[pdn] != _PDE_VACANT(pdn)) { - dev_dbg(as->smmu->dev, "pdn: %lx\n", pdn); - - ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn])); - __free_page(SMMU_EX_PTBL_PAGE(pdir[pdn])); - pdir[pdn] = _PDE_VACANT(pdn); - FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]); - flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn], - as->pdir_page, 1); - } + value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | + SMMU_TLB_FLUSH_VA_GROUP(iova); + smmu_writel(smmu, value, SMMU_TLB_FLUSH); } -static void free_pdir(struct smmu_as *as) +static inline void smmu_flush(struct tegra_smmu *smmu) { - unsigned addr; - int count; - struct device *dev = as->smmu->dev; - - if (!as->pdir_page) - return; - - addr = as->smmu->iovmm_base; - count = as->smmu->page_count; - while (count-- > 0) { - free_ptbl(as, addr); - addr += SMMU_PAGE_SIZE * SMMU_PTBL_COUNT; - } - ClearPageReserved(as->pdir_page); - __free_page(as->pdir_page); - as->pdir_page = NULL; - devm_kfree(dev, as->pte_count); - as->pte_count = NULL; + smmu_readl(smmu, SMMU_CONFIG); } -/* - * Maps PTBL for given iova and returns the PTE address - * Caller must unmap the mapped PTBL returned in *ptbl_page_p - */ -static unsigned long *locate_pte(struct smmu_as *as, - dma_addr_t iova, bool allocate, - struct page **ptbl_page_p, - unsigned int **count) +static int tegra_smmu_alloc_asid(struct tegra_smmu *smmu, unsigned int *idp) { - unsigned long ptn = SMMU_ADDR_TO_PFN(iova); - unsigned long pdn = SMMU_ADDR_TO_PDN(iova); - unsigned long *pdir = page_address(as->pdir_page); - unsigned long *ptbl; - - if (pdir[pdn] != _PDE_VACANT(pdn)) { - /* Mapped entry table already exists */ - *ptbl_page_p = SMMU_EX_PTBL_PAGE(pdir[pdn]); - ptbl = page_address(*ptbl_page_p); - } else if (!allocate) { - return NULL; - } else { - int pn; - unsigned long addr = SMMU_PDN_TO_ADDR(pdn); + unsigned long id; - /* Vacant - allocate a new page table */ - dev_dbg(as->smmu->dev, "New PTBL pdn: %lx\n", pdn); + mutex_lock(&smmu->lock); - *ptbl_page_p = alloc_page(GFP_ATOMIC); - if (!*ptbl_page_p) { - dev_err(as->smmu->dev, - "failed to allocate smmu_device page table\n"); - return NULL; - } - SetPageReserved(*ptbl_page_p); - ptbl = (unsigned long *)page_address(*ptbl_page_p); - for (pn = 0; pn < SMMU_PTBL_COUNT; - pn++, addr += SMMU_PAGE_SIZE) { - ptbl[pn] = _PTE_VACANT(addr); - } - FLUSH_CPU_DCACHE(ptbl, *ptbl_page_p, SMMU_PTBL_SIZE); - pdir[pdn] = SMMU_MK_PDE(*ptbl_page_p, - as->pde_attr | _PDE_NEXT); - FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]); - flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn], - as->pdir_page, 1); + id = find_first_zero_bit(smmu->asids, smmu->soc->num_asids); + if (id >= smmu->soc->num_asids) { + mutex_unlock(&smmu->lock); + return -ENOSPC; } - *count = &as->pte_count[pdn]; - return &ptbl[ptn % SMMU_PTBL_COUNT]; + set_bit(id, smmu->asids); + *idp = id; + + mutex_unlock(&smmu->lock); + return 0; } -#ifdef CONFIG_SMMU_SIG_DEBUG -static void put_signature(struct smmu_as *as, - dma_addr_t iova, unsigned long pfn) +static void tegra_smmu_free_asid(struct tegra_smmu *smmu, unsigned int id) { - struct page *page; - unsigned long *vaddr; - - page = pfn_to_page(pfn); - vaddr = page_address(page); - if (!vaddr) - return; - - vaddr[0] = iova; - vaddr[1] = pfn << PAGE_SHIFT; - FLUSH_CPU_DCACHE(vaddr, page, sizeof(vaddr[0]) * 2); + mutex_lock(&smmu->lock); + clear_bit(id, smmu->asids); + mutex_unlock(&smmu->lock); } -#else -static inline void put_signature(struct smmu_as *as, - unsigned long addr, unsigned long pfn) + +static bool tegra_smmu_capable(enum iommu_cap cap) { + return false; } -#endif -/* - * Caller must not hold as->lock - */ -static int alloc_pdir(struct smmu_as *as) +static int tegra_smmu_domain_init(struct iommu_domain *domain) { - unsigned long *pdir, flags; - int pdn, err = 0; - u32 val; - struct smmu_device *smmu = as->smmu; - struct page *page; - unsigned int *cnt; + struct tegra_smmu_as *as; + unsigned int i; + uint32_t *pd; - /* - * do the allocation, then grab as->lock - */ - cnt = devm_kzalloc(smmu->dev, - sizeof(cnt[0]) * SMMU_PDIR_COUNT, - GFP_KERNEL); - page = alloc_page(GFP_KERNEL | __GFP_DMA); + as = kzalloc(sizeof(*as), GFP_KERNEL); + if (!as) + return -ENOMEM; - spin_lock_irqsave(&as->lock, flags); + as->attr = SMMU_PD_READABLE | SMMU_PD_WRITABLE | SMMU_PD_NONSECURE; + as->domain = domain; - if (as->pdir_page) { - /* We raced, free the redundant */ - err = -EAGAIN; - goto err_out; + as->pd = alloc_page(GFP_KERNEL | __GFP_DMA); + if (!as->pd) { + kfree(as); + return -ENOMEM; } - if (!page || !cnt) { - dev_err(smmu->dev, "failed to allocate at %s\n", __func__); - err = -ENOMEM; - goto err_out; + as->count = alloc_page(GFP_KERNEL); + if (!as->count) { + __free_page(as->pd); + kfree(as); + return -ENOMEM; } - as->pdir_page = page; - as->pte_count = cnt; + /* clear PDEs */ + pd = page_address(as->pd); + SetPageReserved(as->pd); - SetPageReserved(as->pdir_page); - pdir = page_address(as->pdir_page); + for (i = 0; i < SMMU_NUM_PDE; i++) + pd[i] = 0; - for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++) - pdir[pdn] = _PDE_VACANT(pdn); - FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE); - val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pdir, as->pdir_page); - smmu_write(smmu, val, SMMU_PTC_FLUSH); - FLUSH_SMMU_REGS(as->smmu); - val = SMMU_TLB_FLUSH_VA_MATCH_ALL | - SMMU_TLB_FLUSH_ASID_MATCH__ENABLE | - (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT); - smmu_write(smmu, val, SMMU_TLB_FLUSH); - FLUSH_SMMU_REGS(as->smmu); + /* clear PDE usage counters */ + pd = page_address(as->count); + SetPageReserved(as->count); - spin_unlock_irqrestore(&as->lock, flags); - - return 0; + for (i = 0; i < SMMU_NUM_PDE; i++) + pd[i] = 0; -err_out: - spin_unlock_irqrestore(&as->lock, flags); + domain->priv = as; - devm_kfree(smmu->dev, cnt); - if (page) - __free_page(page); - return err; + return 0; } -static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) +static void tegra_smmu_domain_destroy(struct iommu_domain *domain) { - unsigned long *pte; - struct page *page; - unsigned int *count; + struct tegra_smmu_as *as = domain->priv; - pte = locate_pte(as, iova, false, &page, &count); - if (WARN_ON(!pte)) - return; + /* TODO: free page directory and page tables */ + ClearPageReserved(as->pd); - if (WARN_ON(*pte == _PTE_VACANT(iova))) - return; - - *pte = _PTE_VACANT(iova); - FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); - flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); - if (!--(*count)) - free_ptbl(as, iova); + kfree(as); } -static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, - unsigned long pfn) +static const struct tegra_smmu_swgroup * +tegra_smmu_find_swgroup(struct tegra_smmu *smmu, unsigned int swgroup) { - struct smmu_device *smmu = as->smmu; - unsigned long *pte; - unsigned int *count; - struct page *page; + const struct tegra_smmu_swgroup *group = NULL; + unsigned int i; - pte = locate_pte(as, iova, true, &page, &count); - if (WARN_ON(!pte)) - return; + for (i = 0; i < smmu->soc->num_swgroups; i++) { + if (smmu->soc->swgroups[i].swgroup == swgroup) { + group = &smmu->soc->swgroups[i]; + break; + } + } - if (*pte == _PTE_VACANT(iova)) - (*count)++; - *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr); - if (unlikely((*pte == _PTE_VACANT(iova)))) - (*count)--; - FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); - flush_ptc_and_tlb(smmu, as, iova, pte, page, 0); - put_signature(as, iova, pfn); + return group; } -static int smmu_iommu_map(struct iommu_domain *domain, unsigned long iova, - phys_addr_t pa, size_t bytes, int prot) +static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup, + unsigned int asid) { - struct smmu_as *as = domain->priv; - unsigned long pfn = __phys_to_pfn(pa); - unsigned long flags; + const struct tegra_smmu_swgroup *group; + unsigned int i; + u32 value; - dev_dbg(as->smmu->dev, "[%d] %08lx:%pa\n", as->asid, iova, &pa); + for (i = 0; i < smmu->soc->num_clients; i++) { + const struct tegra_mc_client *client = &smmu->soc->clients[i]; - if (!pfn_valid(pfn)) - return -ENOMEM; - - spin_lock_irqsave(&as->lock, flags); - __smmu_iommu_map_pfn(as, iova, pfn); - spin_unlock_irqrestore(&as->lock, flags); - return 0; -} - -static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t bytes) -{ - struct smmu_as *as = domain->priv; - unsigned long flags; + if (client->swgroup != swgroup) + continue; - dev_dbg(as->smmu->dev, "[%d] %08lx\n", as->asid, iova); + value = smmu_readl(smmu, client->smmu.reg); + value |= BIT(client->smmu.bit); + smmu_writel(smmu, value, client->smmu.reg); + } - spin_lock_irqsave(&as->lock, flags); - __smmu_iommu_unmap(as, iova); - spin_unlock_irqrestore(&as->lock, flags); - return SMMU_PAGE_SIZE; + group = tegra_smmu_find_swgroup(smmu, swgroup); + if (group) { + value = smmu_readl(smmu, group->reg); + value &= ~SMMU_ASID_MASK; + value |= SMMU_ASID_VALUE(asid); + value |= SMMU_ASID_ENABLE; + smmu_writel(smmu, value, group->reg); + } } -static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, - dma_addr_t iova) +static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup, + unsigned int asid) { - struct smmu_as *as = domain->priv; - unsigned long *pte; - unsigned int *count; - struct page *page; - unsigned long pfn; - unsigned long flags; + const struct tegra_smmu_swgroup *group; + unsigned int i; + u32 value; - spin_lock_irqsave(&as->lock, flags); + group = tegra_smmu_find_swgroup(smmu, swgroup); + if (group) { + value = smmu_readl(smmu, group->reg); + value &= ~SMMU_ASID_MASK; + value |= SMMU_ASID_VALUE(asid); + value &= ~SMMU_ASID_ENABLE; + smmu_writel(smmu, value, group->reg); + } - pte = locate_pte(as, iova, true, &page, &count); - pfn = *pte & SMMU_PFN_MASK; - WARN_ON(!pfn_valid(pfn)); - dev_dbg(as->smmu->dev, - "iova:%08llx pfn:%08lx asid:%d\n", (unsigned long long)iova, - pfn, as->asid); + for (i = 0; i < smmu->soc->num_clients; i++) { + const struct tegra_mc_client *client = &smmu->soc->clients[i]; - spin_unlock_irqrestore(&as->lock, flags); - return PFN_PHYS(pfn); -} + if (client->swgroup != swgroup) + continue; -static bool smmu_iommu_capable(enum iommu_cap cap) -{ - return false; + value = smmu_readl(smmu, client->smmu.reg); + value &= ~BIT(client->smmu.bit); + smmu_writel(smmu, value, client->smmu.reg); + } } -static int smmu_iommu_attach_dev(struct iommu_domain *domain, - struct device *dev) +static int tegra_smmu_as_prepare(struct tegra_smmu *smmu, + struct tegra_smmu_as *as) { - struct smmu_as *as = domain->priv; - struct smmu_device *smmu = as->smmu; - struct smmu_client *client, *c; - u32 map; + u32 value; int err; - client = devm_kzalloc(smmu->dev, sizeof(*c), GFP_KERNEL); - if (!client) - return -ENOMEM; - client->dev = dev; - client->as = as; - map = (unsigned long)dev->platform_data; - if (!map) - return -EINVAL; - - err = smmu_client_enable_hwgrp(client, map); - if (err) - goto err_hwgrp; - - spin_lock(&as->client_lock); - list_for_each_entry(c, &as->client, list) { - if (c->dev == dev) { - dev_err(smmu->dev, - "%s is already attached\n", dev_name(c->dev)); - err = -EINVAL; - goto err_client; - } + if (as->use_count > 0) { + as->use_count++; + return 0; } - list_add(&client->list, &as->client); - spin_unlock(&as->client_lock); - /* - * Reserve "page zero" for AVP vectors using a common dummy - * page. - */ - if (map & HWG_AVPC) { - struct page *page; + err = tegra_smmu_alloc_asid(smmu, &as->id); + if (err < 0) + return err; - page = as->smmu->avp_vector_page; - __smmu_iommu_map_pfn(as, 0, page_to_pfn(page)); + smmu->soc->ops->flush_dcache(as->pd, 0, SMMU_SIZE_PD); + smmu_flush_ptc(smmu, as->pd, 0); + smmu_flush_tlb_asid(smmu, as->id); - pr_info("Reserve \"page zero\" for AVP vectors using a common dummy\n"); - } + smmu_writel(smmu, as->id & 0x7f, SMMU_PTB_ASID); + value = SMMU_PTB_DATA_VALUE(as->pd, as->attr); + smmu_writel(smmu, value, SMMU_PTB_DATA); + smmu_flush(smmu); - dev_dbg(smmu->dev, "%s is attached\n", dev_name(dev)); - return 0; + as->smmu = smmu; + as->use_count++; -err_client: - smmu_client_disable_hwgrp(client); - spin_unlock(&as->client_lock); -err_hwgrp: - devm_kfree(smmu->dev, client); - return err; + return 0; } -static void smmu_iommu_detach_dev(struct iommu_domain *domain, - struct device *dev) +static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu, + struct tegra_smmu_as *as) { - struct smmu_as *as = domain->priv; - struct smmu_device *smmu = as->smmu; - struct smmu_client *c; - - spin_lock(&as->client_lock); - - list_for_each_entry(c, &as->client, list) { - if (c->dev == dev) { - smmu_client_disable_hwgrp(c); - list_del(&c->list); - devm_kfree(smmu->dev, c); - c->as = NULL; - dev_dbg(smmu->dev, - "%s is detached\n", dev_name(c->dev)); - goto out; - } - } - dev_err(smmu->dev, "Couldn't find %s\n", dev_name(dev)); -out: - spin_unlock(&as->client_lock); + if (--as->use_count > 0) + return; + + tegra_smmu_free_asid(smmu, as->id); + as->smmu = NULL; } -static int smmu_iommu_domain_init(struct iommu_domain *domain) +static int tegra_smmu_attach_dev(struct iommu_domain *domain, + struct device *dev) { - int i, err = -EAGAIN; - unsigned long flags; - struct smmu_as *as; - struct smmu_device *smmu = smmu_handle; + struct tegra_smmu *smmu = dev->archdata.iommu; + struct tegra_smmu_as *as = domain->priv; + struct device_node *np = dev->of_node; + struct of_phandle_args args; + unsigned int index = 0; + int err = 0; - /* Look for a free AS with lock held */ - for (i = 0; i < smmu->num_as; i++) { - as = &smmu->as[i]; + while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, + &args)) { + unsigned int swgroup = args.args[0]; - if (as->pdir_page) + if (args.np != smmu->dev->of_node) { + of_node_put(args.np); continue; + } - err = alloc_pdir(as); - if (!err) - goto found; + of_node_put(args.np); - if (err != -EAGAIN) - break; + err = tegra_smmu_as_prepare(smmu, as); + if (err < 0) + return err; + + tegra_smmu_enable(smmu, swgroup, as->id); + index++; } - if (i == smmu->num_as) - dev_err(smmu->dev, "no free AS\n"); - return err; -found: - spin_lock_irqsave(&smmu->lock, flags); + if (index == 0) + return -ENODEV; - /* Update PDIR register */ - smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); - smmu_write(smmu, - SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA); - FLUSH_SMMU_REGS(smmu); + return 0; +} - spin_unlock_irqrestore(&smmu->lock, flags); +static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct tegra_smmu_as *as = domain->priv; + struct device_node *np = dev->of_node; + struct tegra_smmu *smmu = as->smmu; + struct of_phandle_args args; + unsigned int index = 0; - domain->priv = as; + while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, + &args)) { + unsigned int swgroup = args.args[0]; - domain->geometry.aperture_start = smmu->iovmm_base; - domain->geometry.aperture_end = smmu->iovmm_base + - smmu->page_count * SMMU_PAGE_SIZE - 1; - domain->geometry.force_aperture = true; + if (args.np != smmu->dev->of_node) { + of_node_put(args.np); + continue; + } - dev_dbg(smmu->dev, "smmu_as@%p\n", as); + of_node_put(args.np); - return 0; + tegra_smmu_disable(smmu, swgroup, as->id); + tegra_smmu_as_unprepare(smmu, as); + index++; + } } -static void smmu_iommu_domain_destroy(struct iommu_domain *domain) +static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, + struct page **pagep) { - struct smmu_as *as = domain->priv; - struct smmu_device *smmu = as->smmu; - unsigned long flags; + u32 *pd = page_address(as->pd), *pt, *count; + u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff; + u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff; + struct tegra_smmu *smmu = as->smmu; + struct page *page; + unsigned int i; + + if (pd[pde] == 0) { + page = alloc_page(GFP_KERNEL | __GFP_DMA); + if (!page) + return NULL; - spin_lock_irqsave(&as->lock, flags); + pt = page_address(page); + SetPageReserved(page); - if (as->pdir_page) { - spin_lock(&smmu->lock); - smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); - smmu_write(smmu, SMMU_PTB_DATA_RESET_VAL, SMMU_PTB_DATA); - FLUSH_SMMU_REGS(smmu); - spin_unlock(&smmu->lock); + for (i = 0; i < SMMU_NUM_PTE; i++) + pt[i] = 0; - free_pdir(as); - } + smmu->soc->ops->flush_dcache(page, 0, SMMU_SIZE_PT); - if (!list_empty(&as->client)) { - struct smmu_client *c; + pd[pde] = SMMU_MK_PDE(page, SMMU_PDE_ATTR | SMMU_PDE_NEXT); - list_for_each_entry(c, &as->client, list) - smmu_iommu_detach_dev(domain, c->dev); + smmu->soc->ops->flush_dcache(as->pd, pde << 2, 4); + smmu_flush_ptc(smmu, as->pd, pde << 2); + smmu_flush_tlb_section(smmu, as->id, iova); + smmu_flush(smmu); + } else { + page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + pt = page_address(page); } - spin_unlock_irqrestore(&as->lock, flags); + *pagep = page; - domain->priv = NULL; - dev_dbg(smmu->dev, "smmu_as@%p\n", as); -} + /* Keep track of entries in this page table. */ + count = page_address(as->count); + if (pt[pte] == 0) + count[pde]++; -static const struct iommu_ops smmu_iommu_ops = { - .capable = smmu_iommu_capable, - .domain_init = smmu_iommu_domain_init, - .domain_destroy = smmu_iommu_domain_destroy, - .attach_dev = smmu_iommu_attach_dev, - .detach_dev = smmu_iommu_detach_dev, - .map = smmu_iommu_map, - .unmap = smmu_iommu_unmap, - .iova_to_phys = smmu_iommu_iova_to_phys, - .pgsize_bitmap = SMMU_IOMMU_PGSIZES, -}; - -/* Should be in the order of enum */ -static const char * const smmu_debugfs_mc[] = { "mc", }; -static const char * const smmu_debugfs_cache[] = { "tlb", "ptc", }; + return &pt[pte]; +} -static ssize_t smmu_debugfs_stats_write(struct file *file, - const char __user *buffer, - size_t count, loff_t *pos) +static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova) { - struct smmu_debugfs_info *info; - struct smmu_device *smmu; - int i; - enum { - _OFF = 0, - _ON, - _RESET, - }; - const char * const command[] = { - [_OFF] = "off", - [_ON] = "on", - [_RESET] = "reset", - }; - char str[] = "reset"; - u32 val; - size_t offs; + u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff; + u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff; + u32 *count = page_address(as->count); + u32 *pd = page_address(as->pd), *pt; + struct page *page; - count = min_t(size_t, count, sizeof(str)); - if (copy_from_user(str, buffer, count)) - return -EINVAL; + page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + pt = page_address(page); - for (i = 0; i < ARRAY_SIZE(command); i++) - if (strncmp(str, command[i], - strlen(command[i])) == 0) - break; + /* + * When no entries in this page table are used anymore, return the + * memory page to the system. + */ + if (pt[pte] != 0) { + if (--count[pde] == 0) { + ClearPageReserved(page); + __free_page(page); + pd[pde] = 0; + } - if (i == ARRAY_SIZE(command)) - return -EINVAL; - - info = file_inode(file)->i_private; - smmu = info->smmu; - - offs = SMMU_CACHE_CONFIG(info->cache); - val = smmu_read(smmu, offs); - switch (i) { - case _OFF: - val &= ~SMMU_CACHE_CONFIG_STATS_ENABLE; - val &= ~SMMU_CACHE_CONFIG_STATS_TEST; - smmu_write(smmu, val, offs); - break; - case _ON: - val |= SMMU_CACHE_CONFIG_STATS_ENABLE; - val &= ~SMMU_CACHE_CONFIG_STATS_TEST; - smmu_write(smmu, val, offs); - break; - case _RESET: - val |= SMMU_CACHE_CONFIG_STATS_TEST; - smmu_write(smmu, val, offs); - val &= ~SMMU_CACHE_CONFIG_STATS_TEST; - smmu_write(smmu, val, offs); - break; - default: - BUG(); - break; + pt[pte] = 0; } - - dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__, - val, smmu_read(smmu, offs), offs); - - return count; } -static int smmu_debugfs_stats_show(struct seq_file *s, void *v) +static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) { - struct smmu_debugfs_info *info = s->private; - struct smmu_device *smmu = info->smmu; - int i; - const char * const stats[] = { "hit", "miss", }; + struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu *smmu = as->smmu; + unsigned long offset; + struct page *page; + u32 *pte; + pte = as_get_pte(as, iova, &page); + if (!pte) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(stats); i++) { - u32 val; - size_t offs; + *pte = __phys_to_pfn(paddr) | SMMU_PTE_ATTR; + offset = offset_in_page(pte); - offs = SMMU_STATS_CACHE_COUNT(info->mc, info->cache, i); - val = smmu_read(smmu, offs); - seq_printf(s, "%s:%08x ", stats[i], val); + smmu->soc->ops->flush_dcache(page, offset, 4); + smmu_flush_ptc(smmu, page, offset); + smmu_flush_tlb_group(smmu, as->id, iova); + smmu_flush(smmu); - dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__, - stats[i], val, offs); - } - seq_printf(s, "\n"); return 0; } -static int smmu_debugfs_stats_open(struct inode *inode, struct file *file) +static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t size) { - return single_open(file, smmu_debugfs_stats_show, inode->i_private); -} + struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu *smmu = as->smmu; + unsigned long offset; + struct page *page; + u32 *pte; -static const struct file_operations smmu_debugfs_stats_fops = { - .open = smmu_debugfs_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = smmu_debugfs_stats_write, -}; + pte = as_get_pte(as, iova, &page); + if (!pte) + return 0; -static void smmu_debugfs_delete(struct smmu_device *smmu) -{ - debugfs_remove_recursive(smmu->debugfs_root); - kfree(smmu->debugfs_info); + offset = offset_in_page(pte); + as_put_pte(as, iova); + + smmu->soc->ops->flush_dcache(page, offset, 4); + smmu_flush_ptc(smmu, page, offset); + smmu_flush_tlb_group(smmu, as->id, iova); + smmu_flush(smmu); + + return size; } -static void smmu_debugfs_create(struct smmu_device *smmu) +static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) { - int i; - size_t bytes; - struct dentry *root; - - bytes = ARRAY_SIZE(smmu_debugfs_mc) * ARRAY_SIZE(smmu_debugfs_cache) * - sizeof(*smmu->debugfs_info); - smmu->debugfs_info = kmalloc(bytes, GFP_KERNEL); - if (!smmu->debugfs_info) - return; - - root = debugfs_create_dir(dev_name(smmu->dev), NULL); - if (!root) - goto err_out; - smmu->debugfs_root = root; - - for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { - int j; - struct dentry *mc; - - mc = debugfs_create_dir(smmu_debugfs_mc[i], root); - if (!mc) - goto err_out; - - for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { - struct dentry *cache; - struct smmu_debugfs_info *info; - - info = smmu->debugfs_info; - info += i * ARRAY_SIZE(smmu_debugfs_mc) + j; - info->smmu = smmu; - info->mc = i; - info->cache = j; - - cache = debugfs_create_file(smmu_debugfs_cache[j], - S_IWUGO | S_IRUGO, mc, - (void *)info, - &smmu_debugfs_stats_fops); - if (!cache) - goto err_out; - } - } + struct tegra_smmu_as *as = domain->priv; + struct page *page; + unsigned long pfn; + u32 *pte; - return; + pte = as_get_pte(as, iova, &page); + pfn = *pte & SMMU_PFN_MASK; -err_out: - smmu_debugfs_delete(smmu); + return PFN_PHYS(pfn); } -static int tegra_smmu_suspend(struct device *dev) +static struct tegra_smmu *tegra_smmu_find(struct device_node *np) { - struct smmu_device *smmu = dev_get_drvdata(dev); + struct platform_device *pdev; + struct tegra_mc *mc; - smmu->translation_enable_0 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_0); - smmu->translation_enable_1 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_1); - smmu->translation_enable_2 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_2); - smmu->asid_security = smmu_read(smmu, SMMU_ASID_SECURITY); - return 0; + pdev = of_find_device_by_node(np); + if (!pdev) + return NULL; + + mc = platform_get_drvdata(pdev); + if (!mc) + return NULL; + + return mc->smmu; } -static int tegra_smmu_resume(struct device *dev) +static int tegra_smmu_add_device(struct device *dev) { - struct smmu_device *smmu = dev_get_drvdata(dev); - unsigned long flags; - int err; + struct device_node *np = dev->of_node; + struct of_phandle_args args; + unsigned int index = 0; - spin_lock_irqsave(&smmu->lock, flags); - err = smmu_setup_regs(smmu); - spin_unlock_irqrestore(&smmu->lock, flags); - return err; + while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, + &args) == 0) { + struct tegra_smmu *smmu; + + smmu = tegra_smmu_find(args.np); + if (smmu) { + /* + * Only a single IOMMU master interface is currently + * supported by the Linux kernel, so abort after the + * first match. + */ + dev->archdata.iommu = smmu; + break; + } + + index++; + } + + return 0; } -static int tegra_smmu_probe(struct platform_device *pdev) +static void tegra_smmu_remove_device(struct device *dev) { - struct smmu_device *smmu; - struct device *dev = &pdev->dev; - int i, asids, err = 0; - dma_addr_t uninitialized_var(base); - size_t bytes, uninitialized_var(size); + dev->archdata.iommu = NULL; +} - if (smmu_handle) - return -EIO; +static const struct iommu_ops tegra_smmu_ops = { + .capable = tegra_smmu_capable, + .domain_init = tegra_smmu_domain_init, + .domain_destroy = tegra_smmu_domain_destroy, + .attach_dev = tegra_smmu_attach_dev, + .detach_dev = tegra_smmu_detach_dev, + .add_device = tegra_smmu_add_device, + .remove_device = tegra_smmu_remove_device, + .map = tegra_smmu_map, + .unmap = tegra_smmu_unmap, + .map_sg = default_iommu_map_sg, + .iova_to_phys = tegra_smmu_iova_to_phys, - BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); + .pgsize_bitmap = SZ_4K, +}; - if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids)) - return -ENODEV; +static void tegra_smmu_ahb_enable(void) +{ + static const struct of_device_id ahb_match[] = { + { .compatible = "nvidia,tegra30-ahb", }, + { } + }; + struct device_node *ahb; - bytes = sizeof(*smmu) + asids * sizeof(*smmu->as); - smmu = devm_kzalloc(dev, bytes, GFP_KERNEL); - if (!smmu) { - dev_err(dev, "failed to allocate smmu_device\n"); - return -ENOMEM; + ahb = of_find_matching_node(NULL, ahb_match); + if (ahb) { + tegra_ahb_enable_smmu(ahb); + of_node_put(ahb); } +} - smmu->nregs = pdev->num_resources; - smmu->regs = devm_kzalloc(dev, 2 * smmu->nregs * sizeof(*smmu->regs), - GFP_KERNEL); - smmu->rege = smmu->regs + smmu->nregs; - if (!smmu->regs) - return -ENOMEM; - for (i = 0; i < smmu->nregs; i++) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - smmu->regs[i] = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(smmu->regs[i])) - return PTR_ERR(smmu->regs[i]); - smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; - } - /* Same as "mc" 1st regiter block start address */ - smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & PAGE_MASK); +struct tegra_smmu *tegra_smmu_probe(struct device *dev, + const struct tegra_smmu_soc *soc, + struct tegra_mc *mc) +{ + struct tegra_smmu *smmu; + size_t size; + u32 value; + int err; - err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); - if (err) - return -ENODEV; + /* This can happen on Tegra20 which doesn't have an SMMU */ + if (!soc) + return NULL; - if (size & SMMU_PAGE_MASK) - return -EINVAL; + smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + if (!smmu) + return ERR_PTR(-ENOMEM); - size >>= SMMU_PAGE_SHIFT; - if (!size) - return -EINVAL; + /* + * This is a bit of a hack. Ideally we'd want to simply return this + * value. However the IOMMU registration process will attempt to add + * all devices to the IOMMU when bus_set_iommu() is called. In order + * not to rely on global variables to track the IOMMU instance, we + * set it here so that it can be looked up from the .add_device() + * callback via the IOMMU device's .drvdata field. + */ + mc->smmu = smmu; - smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); - if (!smmu->ahb) - return -ENODEV; + size = BITS_TO_LONGS(soc->num_asids) * sizeof(long); - smmu->dev = dev; - smmu->num_as = asids; - smmu->iovmm_base = base; - smmu->page_count = size; - - smmu->translation_enable_0 = ~0; - smmu->translation_enable_1 = ~0; - smmu->translation_enable_2 = ~0; - smmu->asid_security = 0; - - for (i = 0; i < smmu->num_as; i++) { - struct smmu_as *as = &smmu->as[i]; - - as->smmu = smmu; - as->asid = i; - as->pdir_attr = _PDIR_ATTR; - as->pde_attr = _PDE_ATTR; - as->pte_attr = _PTE_ATTR; - - spin_lock_init(&as->lock); - spin_lock_init(&as->client_lock); - INIT_LIST_HEAD(&as->client); - } - spin_lock_init(&smmu->lock); - err = smmu_setup_regs(smmu); - if (err) - return err; - platform_set_drvdata(pdev, smmu); + smmu->asids = devm_kzalloc(dev, size, GFP_KERNEL); + if (!smmu->asids) + return ERR_PTR(-ENOMEM); - smmu->avp_vector_page = alloc_page(GFP_KERNEL); - if (!smmu->avp_vector_page) - return -ENOMEM; + mutex_init(&smmu->lock); - smmu_debugfs_create(smmu); - smmu_handle = smmu; - bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); - return 0; -} + smmu->regs = mc->regs; + smmu->soc = soc; + smmu->dev = dev; + smmu->mc = mc; -static int tegra_smmu_remove(struct platform_device *pdev) -{ - struct smmu_device *smmu = platform_get_drvdata(pdev); - int i; + value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); - smmu_debugfs_delete(smmu); + if (soc->supports_request_limit) + value |= SMMU_PTC_CONFIG_REQ_LIMIT(8); - smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); - for (i = 0; i < smmu->num_as; i++) - free_pdir(&smmu->as[i]); - __free_page(smmu->avp_vector_page); - smmu_handle = NULL; - return 0; -} + smmu_writel(smmu, value, SMMU_PTC_CONFIG); -static const struct dev_pm_ops tegra_smmu_pm_ops = { - .suspend = tegra_smmu_suspend, - .resume = tegra_smmu_resume, -}; + value = SMMU_TLB_CONFIG_HIT_UNDER_MISS | + SMMU_TLB_CONFIG_ACTIVE_LINES(0x20); -static const struct of_device_id tegra_smmu_of_match[] = { - { .compatible = "nvidia,tegra30-smmu", }, - { }, -}; -MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); - -static struct platform_driver tegra_smmu_driver = { - .probe = tegra_smmu_probe, - .remove = tegra_smmu_remove, - .driver = { - .owner = THIS_MODULE, - .name = "tegra-smmu", - .pm = &tegra_smmu_pm_ops, - .of_match_table = tegra_smmu_of_match, - }, -}; + if (soc->supports_round_robin_arbitration) + value |= SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION; -static int tegra_smmu_init(void) -{ - return platform_driver_register(&tegra_smmu_driver); -} + smmu_writel(smmu, value, SMMU_TLB_CONFIG); -static void __exit tegra_smmu_exit(void) -{ - platform_driver_unregister(&tegra_smmu_driver); -} + smmu_flush_ptc(smmu, NULL, 0); + smmu_flush_tlb(smmu); + smmu_writel(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); + smmu_flush(smmu); + + tegra_smmu_ahb_enable(); -subsys_initcall(tegra_smmu_init); -module_exit(tegra_smmu_exit); + err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops); + if (err < 0) + return ERR_PTR(err); -MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); -MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); -MODULE_ALIAS("platform:tegra-smmu"); -MODULE_LICENSE("GPL v2"); + return smmu; +} diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 6a2e168c3ab0..b907550ae9a5 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -26,6 +26,7 @@ #include <linux/of_pci.h> #include <linux/irqdomain.h> #include <linux/slab.h> +#include <linux/syscore_ops.h> #include <linux/msi.h> #include <asm/mach/arch.h> #include <asm/exception.h> @@ -67,6 +68,7 @@ static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; +static u32 doorbell_mask_reg; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); @@ -485,6 +487,54 @@ armada_370_xp_handle_irq(struct pt_regs *regs) } while (1); } +static int armada_370_xp_mpic_suspend(void) +{ + doorbell_mask_reg = readl(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + return 0; +} + +static void armada_370_xp_mpic_resume(void) +{ + int nirqs; + irq_hw_number_t irq; + + /* Re-enable interrupts */ + nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; + for (irq = 0; irq < nirqs; irq++) { + struct irq_data *data; + int virq; + + virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); + if (virq == 0) + continue; + + if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + writel(irq, per_cpu_int_base + + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + else + writel(irq, main_int_base + + ARMADA_370_XP_INT_SET_ENABLE_OFFS); + + data = irq_get_irq_data(virq); + if (!irqd_irq_disabled(data)) + armada_370_xp_irq_unmask(data); + } + + /* Reconfigure doorbells for IPIs and MSIs */ + writel(doorbell_mask_reg, + per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + if (doorbell_mask_reg & IPI_DOORBELL_MASK) + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +} + +struct syscore_ops armada_370_xp_mpic_syscore_ops = { + .suspend = armada_370_xp_mpic_suspend, + .resume = armada_370_xp_mpic_resume, +}; + static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { @@ -541,6 +591,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, armada_370_xp_mpic_handle_cascade_irq); } + register_syscore_ops(&armada_370_xp_mpic_syscore_ops); + return 0; } diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index 6ae3cdee0681..cc4f9d80122e 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -217,8 +217,9 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, } ret = irq_alloc_domain_generic_chips(domain, 32, 1, name, - handle_level_irq, 0, 0, - IRQCHIP_SKIP_SET_WAKE); + handle_fasteoi_irq, + IRQ_NOREQUEST | IRQ_NOPROBE | + IRQ_NOAUTOEN, 0, 0); if (ret) goto err_domain_remove; @@ -230,7 +231,6 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, gc->unused = 0; gc->wake_enabled = ~0; gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK; - gc->chip_types[0].handler = handle_fasteoi_irq; gc->chip_types[0].chip.irq_eoi = irq_gc_eoi; gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown; diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index b9f4fb808e49..5fb38a2ac226 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -101,9 +101,9 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, int parent_irq; parent_irq = irq_of_parse_and_map(dn, irq); - if (parent_irq < 0) { + if (!parent_irq) { pr_err("failed to map interrupt %d\n", irq); - return parent_irq; + return -EINVAL; } data->irq_map_mask |= be32_to_cpup(map_mask + irq); diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index c15c840987d2..14691a4cb84c 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -135,9 +135,9 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, __raw_writel(0xffffffff, data->base + CPU_CLEAR); data->parent_irq = irq_of_parse_and_map(np, 0); - if (data->parent_irq < 0) { + if (!data->parent_irq) { pr_err("failed to find parent interrupt\n"); - ret = data->parent_irq; + ret = -EINVAL; goto out_unmap; } diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 38493ff28fa5..7f9be0785c6a 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1041,6 +1041,8 @@ gic_of_init(struct device_node *node, struct device_node *parent) return 0; } IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init); +IRQCHIP_DECLARE(arm11mp_gic, "arm,arm11mp-gic", gic_of_init); +IRQCHIP_DECLARE(arm1176jzf_dc_gic, "arm,arm1176jzf-devchip-gic", gic_of_init); IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init); IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init); IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init); diff --git a/drivers/md/dm-bio-prison.c b/drivers/md/dm-bio-prison.c index f752d12081ff..be065300e93c 100644 --- a/drivers/md/dm-bio-prison.c +++ b/drivers/md/dm-bio-prison.c @@ -14,68 +14,38 @@ /*----------------------------------------------------------------*/ -struct bucket { - spinlock_t lock; - struct hlist_head cells; -}; +#define MIN_CELLS 1024 struct dm_bio_prison { + spinlock_t lock; mempool_t *cell_pool; - - unsigned nr_buckets; - unsigned hash_mask; - struct bucket *buckets; + struct rb_root cells; }; -/*----------------------------------------------------------------*/ - -static uint32_t calc_nr_buckets(unsigned nr_cells) -{ - uint32_t n = 128; - - nr_cells /= 4; - nr_cells = min(nr_cells, 8192u); - - while (n < nr_cells) - n <<= 1; - - return n; -} - static struct kmem_cache *_cell_cache; -static void init_bucket(struct bucket *b) -{ - spin_lock_init(&b->lock); - INIT_HLIST_HEAD(&b->cells); -} +/*----------------------------------------------------------------*/ /* * @nr_cells should be the number of cells you want in use _concurrently_. * Don't confuse it with the number of distinct keys. */ -struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells) +struct dm_bio_prison *dm_bio_prison_create(void) { - unsigned i; - uint32_t nr_buckets = calc_nr_buckets(nr_cells); - size_t len = sizeof(struct dm_bio_prison) + - (sizeof(struct bucket) * nr_buckets); - struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL); + struct dm_bio_prison *prison = kmalloc(sizeof(*prison), GFP_KERNEL); if (!prison) return NULL; - prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache); + spin_lock_init(&prison->lock); + + prison->cell_pool = mempool_create_slab_pool(MIN_CELLS, _cell_cache); if (!prison->cell_pool) { kfree(prison); return NULL; } - prison->nr_buckets = nr_buckets; - prison->hash_mask = nr_buckets - 1; - prison->buckets = (struct bucket *) (prison + 1); - for (i = 0; i < nr_buckets; i++) - init_bucket(prison->buckets + i); + prison->cells = RB_ROOT; return prison; } @@ -101,68 +71,73 @@ void dm_bio_prison_free_cell(struct dm_bio_prison *prison, } EXPORT_SYMBOL_GPL(dm_bio_prison_free_cell); -static uint32_t hash_key(struct dm_bio_prison *prison, struct dm_cell_key *key) +static void __setup_new_cell(struct dm_cell_key *key, + struct bio *holder, + struct dm_bio_prison_cell *cell) { - const unsigned long BIG_PRIME = 4294967291UL; - uint64_t hash = key->block * BIG_PRIME; - - return (uint32_t) (hash & prison->hash_mask); + memcpy(&cell->key, key, sizeof(cell->key)); + cell->holder = holder; + bio_list_init(&cell->bios); } -static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs) +static int cmp_keys(struct dm_cell_key *lhs, + struct dm_cell_key *rhs) { - return (lhs->virtual == rhs->virtual) && - (lhs->dev == rhs->dev) && - (lhs->block == rhs->block); -} + if (lhs->virtual < rhs->virtual) + return -1; -static struct bucket *get_bucket(struct dm_bio_prison *prison, - struct dm_cell_key *key) -{ - return prison->buckets + hash_key(prison, key); -} + if (lhs->virtual > rhs->virtual) + return 1; -static struct dm_bio_prison_cell *__search_bucket(struct bucket *b, - struct dm_cell_key *key) -{ - struct dm_bio_prison_cell *cell; + if (lhs->dev < rhs->dev) + return -1; - hlist_for_each_entry(cell, &b->cells, list) - if (keys_equal(&cell->key, key)) - return cell; + if (lhs->dev > rhs->dev) + return 1; - return NULL; -} + if (lhs->block_end <= rhs->block_begin) + return -1; -static void __setup_new_cell(struct bucket *b, - struct dm_cell_key *key, - struct bio *holder, - struct dm_bio_prison_cell *cell) -{ - memcpy(&cell->key, key, sizeof(cell->key)); - cell->holder = holder; - bio_list_init(&cell->bios); - hlist_add_head(&cell->list, &b->cells); + if (lhs->block_begin >= rhs->block_end) + return 1; + + return 0; } -static int __bio_detain(struct bucket *b, +static int __bio_detain(struct dm_bio_prison *prison, struct dm_cell_key *key, struct bio *inmate, struct dm_bio_prison_cell *cell_prealloc, struct dm_bio_prison_cell **cell_result) { - struct dm_bio_prison_cell *cell; - - cell = __search_bucket(b, key); - if (cell) { - if (inmate) - bio_list_add(&cell->bios, inmate); - *cell_result = cell; - return 1; + int r; + struct rb_node **new = &prison->cells.rb_node, *parent = NULL; + + while (*new) { + struct dm_bio_prison_cell *cell = + container_of(*new, struct dm_bio_prison_cell, node); + + r = cmp_keys(key, &cell->key); + + parent = *new; + if (r < 0) + new = &((*new)->rb_left); + else if (r > 0) + new = &((*new)->rb_right); + else { + if (inmate) + bio_list_add(&cell->bios, inmate); + *cell_result = cell; + return 1; + } } - __setup_new_cell(b, key, inmate, cell_prealloc); + __setup_new_cell(key, inmate, cell_prealloc); *cell_result = cell_prealloc; + + rb_link_node(&cell_prealloc->node, parent, new); + rb_insert_color(&cell_prealloc->node, &prison->cells); + return 0; } @@ -174,11 +149,10 @@ static int bio_detain(struct dm_bio_prison *prison, { int r; unsigned long flags; - struct bucket *b = get_bucket(prison, key); - spin_lock_irqsave(&b->lock, flags); - r = __bio_detain(b, key, inmate, cell_prealloc, cell_result); - spin_unlock_irqrestore(&b->lock, flags); + spin_lock_irqsave(&prison->lock, flags); + r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result); + spin_unlock_irqrestore(&prison->lock, flags); return r; } @@ -205,10 +179,11 @@ EXPORT_SYMBOL_GPL(dm_get_cell); /* * @inmates must have been initialised prior to this call */ -static void __cell_release(struct dm_bio_prison_cell *cell, +static void __cell_release(struct dm_bio_prison *prison, + struct dm_bio_prison_cell *cell, struct bio_list *inmates) { - hlist_del(&cell->list); + rb_erase(&cell->node, &prison->cells); if (inmates) { if (cell->holder) @@ -222,21 +197,21 @@ void dm_cell_release(struct dm_bio_prison *prison, struct bio_list *bios) { unsigned long flags; - struct bucket *b = get_bucket(prison, &cell->key); - spin_lock_irqsave(&b->lock, flags); - __cell_release(cell, bios); - spin_unlock_irqrestore(&b->lock, flags); + spin_lock_irqsave(&prison->lock, flags); + __cell_release(prison, cell, bios); + spin_unlock_irqrestore(&prison->lock, flags); } EXPORT_SYMBOL_GPL(dm_cell_release); /* * Sometimes we don't want the holder, just the additional bios. */ -static void __cell_release_no_holder(struct dm_bio_prison_cell *cell, +static void __cell_release_no_holder(struct dm_bio_prison *prison, + struct dm_bio_prison_cell *cell, struct bio_list *inmates) { - hlist_del(&cell->list); + rb_erase(&cell->node, &prison->cells); bio_list_merge(inmates, &cell->bios); } @@ -245,11 +220,10 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison, struct bio_list *inmates) { unsigned long flags; - struct bucket *b = get_bucket(prison, &cell->key); - spin_lock_irqsave(&b->lock, flags); - __cell_release_no_holder(cell, inmates); - spin_unlock_irqrestore(&b->lock, flags); + spin_lock_irqsave(&prison->lock, flags); + __cell_release_no_holder(prison, cell, inmates); + spin_unlock_irqrestore(&prison->lock, flags); } EXPORT_SYMBOL_GPL(dm_cell_release_no_holder); @@ -267,6 +241,20 @@ void dm_cell_error(struct dm_bio_prison *prison, } EXPORT_SYMBOL_GPL(dm_cell_error); +void dm_cell_visit_release(struct dm_bio_prison *prison, + void (*visit_fn)(void *, struct dm_bio_prison_cell *), + void *context, + struct dm_bio_prison_cell *cell) +{ + unsigned long flags; + + spin_lock_irqsave(&prison->lock, flags); + visit_fn(context, cell); + rb_erase(&cell->node, &prison->cells); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_visit_release); + /*----------------------------------------------------------------*/ #define DEFERRED_SET_SIZE 64 diff --git a/drivers/md/dm-bio-prison.h b/drivers/md/dm-bio-prison.h index 6805a142b750..74cf01144b1f 100644 --- a/drivers/md/dm-bio-prison.h +++ b/drivers/md/dm-bio-prison.h @@ -10,8 +10,8 @@ #include "persistent-data/dm-block-manager.h" /* FIXME: for dm_block_t */ #include "dm-thin-metadata.h" /* FIXME: for dm_thin_id */ -#include <linux/list.h> #include <linux/bio.h> +#include <linux/rbtree.h> /*----------------------------------------------------------------*/ @@ -23,11 +23,14 @@ */ struct dm_bio_prison; -/* FIXME: this needs to be more abstract */ +/* + * Keys define a range of blocks within either a virtual or physical + * device. + */ struct dm_cell_key { int virtual; dm_thin_id dev; - dm_block_t block; + dm_block_t block_begin, block_end; }; /* @@ -35,13 +38,15 @@ struct dm_cell_key { * themselves. */ struct dm_bio_prison_cell { - struct hlist_node list; + struct list_head user_list; /* for client use */ + struct rb_node node; + struct dm_cell_key key; struct bio *holder; struct bio_list bios; }; -struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells); +struct dm_bio_prison *dm_bio_prison_create(void); void dm_bio_prison_destroy(struct dm_bio_prison *prison); /* @@ -57,7 +62,7 @@ void dm_bio_prison_free_cell(struct dm_bio_prison *prison, struct dm_bio_prison_cell *cell); /* - * Creates, or retrieves a cell for the given key. + * Creates, or retrieves a cell that overlaps the given key. * * Returns 1 if pre-existing cell returned, zero if new cell created using * @cell_prealloc. @@ -68,7 +73,8 @@ int dm_get_cell(struct dm_bio_prison *prison, struct dm_bio_prison_cell **cell_result); /* - * An atomic op that combines retrieving a cell, and adding a bio to it. + * An atomic op that combines retrieving or creating a cell, and adding a + * bio to it. * * Returns 1 if the cell was already held, 0 if @inmate is the new holder. */ @@ -87,6 +93,14 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison, void dm_cell_error(struct dm_bio_prison *prison, struct dm_bio_prison_cell *cell, int error); +/* + * Visits the cell and then releases. Guarantees no new inmates are + * inserted between the visit and release. + */ +void dm_cell_visit_release(struct dm_bio_prison *prison, + void (*visit_fn)(void *, struct dm_bio_prison_cell *), + void *context, struct dm_bio_prison_cell *cell); + /*----------------------------------------------------------------*/ /* diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index afe79719ea32..c33b49792b87 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -14,6 +14,7 @@ #include <linux/vmalloc.h> #include <linux/shrinker.h> #include <linux/module.h> +#include <linux/rbtree.h> #define DM_MSG_PREFIX "bufio" @@ -34,26 +35,23 @@ /* * Check buffer ages in this interval (seconds) */ -#define DM_BUFIO_WORK_TIMER_SECS 10 +#define DM_BUFIO_WORK_TIMER_SECS 30 /* * Free buffers when they are older than this (seconds) */ -#define DM_BUFIO_DEFAULT_AGE_SECS 60 +#define DM_BUFIO_DEFAULT_AGE_SECS 300 /* - * The number of bvec entries that are embedded directly in the buffer. - * If the chunk size is larger, dm-io is used to do the io. + * The nr of bytes of cached data to keep around. */ -#define DM_BUFIO_INLINE_VECS 16 +#define DM_BUFIO_DEFAULT_RETAIN_BYTES (256 * 1024) /* - * Buffer hash + * The number of bvec entries that are embedded directly in the buffer. + * If the chunk size is larger, dm-io is used to do the io. */ -#define DM_BUFIO_HASH_BITS 20 -#define DM_BUFIO_HASH(block) \ - ((((block) >> DM_BUFIO_HASH_BITS) ^ (block)) & \ - ((1 << DM_BUFIO_HASH_BITS) - 1)) +#define DM_BUFIO_INLINE_VECS 16 /* * Don't try to use kmem_cache_alloc for blocks larger than this. @@ -106,7 +104,7 @@ struct dm_bufio_client { unsigned minimum_buffers; - struct hlist_head *cache_hash; + struct rb_root buffer_tree; wait_queue_head_t free_buffer_wait; int async_write_error; @@ -135,7 +133,7 @@ enum data_mode { }; struct dm_buffer { - struct hlist_node hash_list; + struct rb_node node; struct list_head lru_list; sector_t block; void *data; @@ -223,6 +221,7 @@ static DEFINE_SPINLOCK(param_spinlock); * Buffers are freed after this timeout */ static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS; +static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES; static unsigned long dm_bufio_peak_allocated; static unsigned long dm_bufio_allocated_kmem_cache; @@ -253,6 +252,53 @@ static LIST_HEAD(dm_bufio_all_clients); */ static DEFINE_MUTEX(dm_bufio_clients_lock); +/*---------------------------------------------------------------- + * A red/black tree acts as an index for all the buffers. + *--------------------------------------------------------------*/ +static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block) +{ + struct rb_node *n = c->buffer_tree.rb_node; + struct dm_buffer *b; + + while (n) { + b = container_of(n, struct dm_buffer, node); + + if (b->block == block) + return b; + + n = (b->block < block) ? n->rb_left : n->rb_right; + } + + return NULL; +} + +static void __insert(struct dm_bufio_client *c, struct dm_buffer *b) +{ + struct rb_node **new = &c->buffer_tree.rb_node, *parent = NULL; + struct dm_buffer *found; + + while (*new) { + found = container_of(*new, struct dm_buffer, node); + + if (found->block == b->block) { + BUG_ON(found != b); + return; + } + + parent = *new; + new = (found->block < b->block) ? + &((*new)->rb_left) : &((*new)->rb_right); + } + + rb_link_node(&b->node, parent, new); + rb_insert_color(&b->node, &c->buffer_tree); +} + +static void __remove(struct dm_bufio_client *c, struct dm_buffer *b) +{ + rb_erase(&b->node, &c->buffer_tree); +} + /*----------------------------------------------------------------*/ static void adjust_total_allocated(enum data_mode data_mode, long diff) @@ -434,7 +480,7 @@ static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty) b->block = block; b->list_mode = dirty; list_add(&b->lru_list, &c->lru[dirty]); - hlist_add_head(&b->hash_list, &c->cache_hash[DM_BUFIO_HASH(block)]); + __insert(b->c, b); b->last_accessed = jiffies; } @@ -448,7 +494,7 @@ static void __unlink_buffer(struct dm_buffer *b) BUG_ON(!c->n_buffers[b->list_mode]); c->n_buffers[b->list_mode]--; - hlist_del(&b->hash_list); + __remove(b->c, b); list_del(&b->lru_list); } @@ -532,6 +578,19 @@ static void use_dmio(struct dm_buffer *b, int rw, sector_t block, end_io(&b->bio, r); } +static void inline_endio(struct bio *bio, int error) +{ + bio_end_io_t *end_fn = bio->bi_private; + + /* + * Reset the bio to free any attached resources + * (e.g. bio integrity profiles). + */ + bio_reset(bio); + + end_fn(bio, error); +} + static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block, bio_end_io_t *end_io) { @@ -543,7 +602,12 @@ static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block, b->bio.bi_max_vecs = DM_BUFIO_INLINE_VECS; b->bio.bi_iter.bi_sector = block << b->c->sectors_per_block_bits; b->bio.bi_bdev = b->c->bdev; - b->bio.bi_end_io = end_io; + b->bio.bi_end_io = inline_endio; + /* + * Use of .bi_private isn't a problem here because + * the dm_buffer's inline bio is local to bufio. + */ + b->bio.bi_private = end_io; /* * We assume that if len >= PAGE_SIZE ptr is page-aligned. @@ -887,23 +951,6 @@ static void __check_watermark(struct dm_bufio_client *c, __write_dirty_buffers_async(c, 1, write_list); } -/* - * Find a buffer in the hash. - */ -static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block) -{ - struct dm_buffer *b; - - hlist_for_each_entry(b, &c->cache_hash[DM_BUFIO_HASH(block)], - hash_list) { - dm_bufio_cond_resched(); - if (b->block == block) - return b; - } - - return NULL; -} - /*---------------------------------------------------------------- * Getting a buffer *--------------------------------------------------------------*/ @@ -1433,45 +1480,52 @@ static void drop_buffers(struct dm_bufio_client *c) } /* - * Test if the buffer is unused and too old, and commit it. + * We may not be able to evict this buffer if IO pending or the client + * is still using it. Caller is expected to know buffer is too old. + * * And if GFP_NOFS is used, we must not do any I/O because we hold * dm_bufio_clients_lock and we would risk deadlock if the I/O gets * rerouted to different bufio client. */ -static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp, - unsigned long max_jiffies) +static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp) { - if (jiffies - b->last_accessed < max_jiffies) - return 0; - if (!(gfp & __GFP_FS)) { if (test_bit(B_READING, &b->state) || test_bit(B_WRITING, &b->state) || test_bit(B_DIRTY, &b->state)) - return 0; + return false; } if (b->hold_count) - return 0; + return false; __make_buffer_clean(b); __unlink_buffer(b); __free_buffer_wake(b); - return 1; + return true; } -static long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, - gfp_t gfp_mask) +static unsigned get_retain_buffers(struct dm_bufio_client *c) +{ + unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes); + return retain_bytes / c->block_size; +} + +static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, + gfp_t gfp_mask) { int l; struct dm_buffer *b, *tmp; - long freed = 0; + unsigned long freed = 0; + unsigned long count = nr_to_scan; + unsigned retain_target = get_retain_buffers(c); for (l = 0; l < LIST_SIZE; l++) { list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) { - freed += __cleanup_old_buffer(b, gfp_mask, 0); - if (!--nr_to_scan) + if (__try_evict_buffer(b, gfp_mask)) + freed++; + if (!--nr_to_scan || ((count - freed) <= retain_target)) return freed; dm_bufio_cond_resched(); } @@ -1533,11 +1587,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign r = -ENOMEM; goto bad_client; } - c->cache_hash = vmalloc(sizeof(struct hlist_head) << DM_BUFIO_HASH_BITS); - if (!c->cache_hash) { - r = -ENOMEM; - goto bad_hash; - } + c->buffer_tree = RB_ROOT; c->bdev = bdev; c->block_size = block_size; @@ -1556,9 +1606,6 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign c->n_buffers[i] = 0; } - for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++) - INIT_HLIST_HEAD(&c->cache_hash[i]); - mutex_init(&c->lock); INIT_LIST_HEAD(&c->reserved_buffers); c->need_reserved_buffers = reserved_buffers; @@ -1632,8 +1679,6 @@ bad_cache: } dm_io_client_destroy(c->dm_io); bad_dm_io: - vfree(c->cache_hash); -bad_hash: kfree(c); bad_client: return ERR_PTR(r); @@ -1660,9 +1705,7 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c) mutex_unlock(&dm_bufio_clients_lock); - for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++) - BUG_ON(!hlist_empty(&c->cache_hash[i])); - + BUG_ON(!RB_EMPTY_ROOT(&c->buffer_tree)); BUG_ON(c->need_reserved_buffers); while (!list_empty(&c->reserved_buffers)) { @@ -1680,36 +1723,60 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c) BUG_ON(c->n_buffers[i]); dm_io_client_destroy(c->dm_io); - vfree(c->cache_hash); kfree(c); } EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); -static void cleanup_old_buffers(void) +static unsigned get_max_age_hz(void) { - unsigned long max_age = ACCESS_ONCE(dm_bufio_max_age); - struct dm_bufio_client *c; + unsigned max_age = ACCESS_ONCE(dm_bufio_max_age); - if (max_age > ULONG_MAX / HZ) - max_age = ULONG_MAX / HZ; + if (max_age > UINT_MAX / HZ) + max_age = UINT_MAX / HZ; - mutex_lock(&dm_bufio_clients_lock); - list_for_each_entry(c, &dm_bufio_all_clients, client_list) { - if (!dm_bufio_trylock(c)) - continue; + return max_age * HZ; +} - while (!list_empty(&c->lru[LIST_CLEAN])) { - struct dm_buffer *b; - b = list_entry(c->lru[LIST_CLEAN].prev, - struct dm_buffer, lru_list); - if (!__cleanup_old_buffer(b, 0, max_age * HZ)) - break; - dm_bufio_cond_resched(); - } +static bool older_than(struct dm_buffer *b, unsigned long age_hz) +{ + return (jiffies - b->last_accessed) >= age_hz; +} + +static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz) +{ + struct dm_buffer *b, *tmp; + unsigned retain_target = get_retain_buffers(c); + unsigned count; + + dm_bufio_lock(c); + + count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY]; + list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) { + if (count <= retain_target) + break; + + if (!older_than(b, age_hz)) + break; + + if (__try_evict_buffer(b, 0)) + count--; - dm_bufio_unlock(c); dm_bufio_cond_resched(); } + + dm_bufio_unlock(c); +} + +static void cleanup_old_buffers(void) +{ + unsigned long max_age_hz = get_max_age_hz(); + struct dm_bufio_client *c; + + mutex_lock(&dm_bufio_clients_lock); + + list_for_each_entry(c, &dm_bufio_all_clients, client_list) + __evict_old_buffers(c, max_age_hz); + mutex_unlock(&dm_bufio_clients_lock); } @@ -1834,6 +1901,9 @@ MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache"); module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds"); +module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory"); + module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(peak_allocated_bytes, "Tracks the maximum allocated memory"); diff --git a/drivers/md/dm-cache-block-types.h b/drivers/md/dm-cache-block-types.h index aac0e2df06be..bed4ad4e1b7c 100644 --- a/drivers/md/dm-cache-block-types.h +++ b/drivers/md/dm-cache-block-types.h @@ -19,6 +19,7 @@ typedef dm_block_t __bitwise__ dm_oblock_t; typedef uint32_t __bitwise__ dm_cblock_t; +typedef dm_block_t __bitwise__ dm_dblock_t; static inline dm_oblock_t to_oblock(dm_block_t b) { @@ -40,4 +41,14 @@ static inline uint32_t from_cblock(dm_cblock_t b) return (__force uint32_t) b; } +static inline dm_dblock_t to_dblock(dm_block_t b) +{ + return (__force dm_dblock_t) b; +} + +static inline dm_block_t from_dblock(dm_dblock_t b) +{ + return (__force dm_block_t) b; +} + #endif /* DM_CACHE_BLOCK_TYPES_H */ diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 06709257adde..9fc616c2755e 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -109,7 +109,7 @@ struct dm_cache_metadata { dm_block_t discard_root; sector_t discard_block_size; - dm_oblock_t discard_nr_blocks; + dm_dblock_t discard_nr_blocks; sector_t data_block_size; dm_cblock_t cache_blocks; @@ -329,7 +329,7 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd) disk_super->hint_root = cpu_to_le64(cmd->hint_root); disk_super->discard_root = cpu_to_le64(cmd->discard_root); disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size); - disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks)); + disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks)); disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE); disk_super->data_block_size = cpu_to_le32(cmd->data_block_size); disk_super->cache_blocks = cpu_to_le32(0); @@ -528,7 +528,7 @@ static void read_superblock_fields(struct dm_cache_metadata *cmd, cmd->hint_root = le64_to_cpu(disk_super->hint_root); cmd->discard_root = le64_to_cpu(disk_super->discard_root); cmd->discard_block_size = le64_to_cpu(disk_super->discard_block_size); - cmd->discard_nr_blocks = to_oblock(le64_to_cpu(disk_super->discard_nr_blocks)); + cmd->discard_nr_blocks = to_dblock(le64_to_cpu(disk_super->discard_nr_blocks)); cmd->data_block_size = le32_to_cpu(disk_super->data_block_size); cmd->cache_blocks = to_cblock(le32_to_cpu(disk_super->cache_blocks)); strncpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name)); @@ -626,7 +626,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd, disk_super->hint_root = cpu_to_le64(cmd->hint_root); disk_super->discard_root = cpu_to_le64(cmd->discard_root); disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size); - disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks)); + disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks)); disk_super->cache_blocks = cpu_to_le32(from_cblock(cmd->cache_blocks)); strncpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name)); disk_super->policy_version[0] = cpu_to_le32(cmd->policy_version[0]); @@ -797,15 +797,15 @@ out: int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd, sector_t discard_block_size, - dm_oblock_t new_nr_entries) + dm_dblock_t new_nr_entries) { int r; down_write(&cmd->root_lock); r = dm_bitset_resize(&cmd->discard_info, cmd->discard_root, - from_oblock(cmd->discard_nr_blocks), - from_oblock(new_nr_entries), + from_dblock(cmd->discard_nr_blocks), + from_dblock(new_nr_entries), false, &cmd->discard_root); if (!r) { cmd->discard_block_size = discard_block_size; @@ -818,28 +818,28 @@ int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd, return r; } -static int __set_discard(struct dm_cache_metadata *cmd, dm_oblock_t b) +static int __set_discard(struct dm_cache_metadata *cmd, dm_dblock_t b) { return dm_bitset_set_bit(&cmd->discard_info, cmd->discard_root, - from_oblock(b), &cmd->discard_root); + from_dblock(b), &cmd->discard_root); } -static int __clear_discard(struct dm_cache_metadata *cmd, dm_oblock_t b) +static int __clear_discard(struct dm_cache_metadata *cmd, dm_dblock_t b) { return dm_bitset_clear_bit(&cmd->discard_info, cmd->discard_root, - from_oblock(b), &cmd->discard_root); + from_dblock(b), &cmd->discard_root); } -static int __is_discarded(struct dm_cache_metadata *cmd, dm_oblock_t b, +static int __is_discarded(struct dm_cache_metadata *cmd, dm_dblock_t b, bool *is_discarded) { return dm_bitset_test_bit(&cmd->discard_info, cmd->discard_root, - from_oblock(b), &cmd->discard_root, + from_dblock(b), &cmd->discard_root, is_discarded); } static int __discard(struct dm_cache_metadata *cmd, - dm_oblock_t dblock, bool discard) + dm_dblock_t dblock, bool discard) { int r; @@ -852,7 +852,7 @@ static int __discard(struct dm_cache_metadata *cmd, } int dm_cache_set_discard(struct dm_cache_metadata *cmd, - dm_oblock_t dblock, bool discard) + dm_dblock_t dblock, bool discard) { int r; @@ -870,8 +870,8 @@ static int __load_discards(struct dm_cache_metadata *cmd, dm_block_t b; bool discard; - for (b = 0; b < from_oblock(cmd->discard_nr_blocks); b++) { - dm_oblock_t dblock = to_oblock(b); + for (b = 0; b < from_dblock(cmd->discard_nr_blocks); b++) { + dm_dblock_t dblock = to_dblock(b); if (cmd->clean_when_opened) { r = __is_discarded(cmd, dblock, &discard); diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h index 7383c90ccdb8..4ecc403be283 100644 --- a/drivers/md/dm-cache-metadata.h +++ b/drivers/md/dm-cache-metadata.h @@ -70,14 +70,14 @@ dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd); int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd, sector_t discard_block_size, - dm_oblock_t new_nr_entries); + dm_dblock_t new_nr_entries); typedef int (*load_discard_fn)(void *context, sector_t discard_block_size, - dm_oblock_t dblock, bool discarded); + dm_dblock_t dblock, bool discarded); int dm_cache_load_discards(struct dm_cache_metadata *cmd, load_discard_fn fn, void *context); -int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_oblock_t dblock, bool discard); +int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_dblock_t dblock, bool discard); int dm_cache_remove_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock); int dm_cache_insert_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock, dm_oblock_t oblock); diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c index 0e385e40909e..13f547a4eeb6 100644 --- a/drivers/md/dm-cache-policy-mq.c +++ b/drivers/md/dm-cache-policy-mq.c @@ -181,24 +181,30 @@ static void queue_shift_down(struct queue *q) * Gives us the oldest entry of the lowest popoulated level. If the first * level is emptied then we shift down one level. */ -static struct list_head *queue_pop(struct queue *q) +static struct list_head *queue_peek(struct queue *q) { unsigned level; - struct list_head *r; for (level = 0; level < NR_QUEUE_LEVELS; level++) - if (!list_empty(q->qs + level)) { - r = q->qs[level].next; - list_del(r); + if (!list_empty(q->qs + level)) + return q->qs[level].next; - /* have we just emptied the bottom level? */ - if (level == 0 && list_empty(q->qs)) - queue_shift_down(q); + return NULL; +} - return r; - } +static struct list_head *queue_pop(struct queue *q) +{ + struct list_head *r = queue_peek(q); - return NULL; + if (r) { + list_del(r); + + /* have we just emptied the bottom level? */ + if (list_empty(q->qs)) + queue_shift_down(q); + } + + return r; } static struct list_head *list_pop(struct list_head *lh) @@ -383,13 +389,6 @@ struct mq_policy { unsigned generation; unsigned generation_period; /* in lookups (will probably change) */ - /* - * Entries in the pre_cache whose hit count passes the promotion - * threshold move to the cache proper. Working out the correct - * value for the promotion_threshold is crucial to this policy. - */ - unsigned promote_threshold; - unsigned discard_promote_adjustment; unsigned read_promote_adjustment; unsigned write_promote_adjustment; @@ -406,6 +405,7 @@ struct mq_policy { #define DEFAULT_DISCARD_PROMOTE_ADJUSTMENT 1 #define DEFAULT_READ_PROMOTE_ADJUSTMENT 4 #define DEFAULT_WRITE_PROMOTE_ADJUSTMENT 8 +#define DISCOURAGE_DEMOTING_DIRTY_THRESHOLD 128 /*----------------------------------------------------------------*/ @@ -518,6 +518,12 @@ static struct entry *pop(struct mq_policy *mq, struct queue *q) return e; } +static struct entry *peek(struct queue *q) +{ + struct list_head *h = queue_peek(q); + return h ? container_of(h, struct entry, list) : NULL; +} + /* * Has this entry already been updated? */ @@ -570,10 +576,6 @@ static void check_generation(struct mq_policy *mq) break; } } - - mq->promote_threshold = nr ? total / nr : 1; - if (mq->promote_threshold * nr < total) - mq->promote_threshold++; } } @@ -641,6 +643,30 @@ static int demote_cblock(struct mq_policy *mq, dm_oblock_t *oblock) } /* + * Entries in the pre_cache whose hit count passes the promotion + * threshold move to the cache proper. Working out the correct + * value for the promotion_threshold is crucial to this policy. + */ +static unsigned promote_threshold(struct mq_policy *mq) +{ + struct entry *e; + + if (any_free_cblocks(mq)) + return 0; + + e = peek(&mq->cache_clean); + if (e) + return e->hit_count; + + e = peek(&mq->cache_dirty); + if (e) + return e->hit_count + DISCOURAGE_DEMOTING_DIRTY_THRESHOLD; + + /* This should never happen */ + return 0; +} + +/* * We modify the basic promotion_threshold depending on the specific io. * * If the origin block has been discarded then there's no cost to copy it @@ -653,7 +679,7 @@ static unsigned adjusted_promote_threshold(struct mq_policy *mq, bool discarded_oblock, int data_dir) { if (data_dir == READ) - return mq->promote_threshold + mq->read_promote_adjustment; + return promote_threshold(mq) + mq->read_promote_adjustment; if (discarded_oblock && (any_free_cblocks(mq) || any_clean_cblocks(mq))) { /* @@ -663,7 +689,7 @@ static unsigned adjusted_promote_threshold(struct mq_policy *mq, return mq->discard_promote_adjustment; } - return mq->promote_threshold + mq->write_promote_adjustment; + return promote_threshold(mq) + mq->write_promote_adjustment; } static bool should_promote(struct mq_policy *mq, struct entry *e, @@ -839,7 +865,8 @@ static int map(struct mq_policy *mq, dm_oblock_t oblock, if (e && in_cache(mq, e)) r = cache_entry_found(mq, e, result); - else if (iot_pattern(&mq->tracker) == PATTERN_SEQUENTIAL) + else if (mq->tracker.thresholds[PATTERN_SEQUENTIAL] && + iot_pattern(&mq->tracker) == PATTERN_SEQUENTIAL) result->op = POLICY_MISS; else if (e) @@ -1230,7 +1257,6 @@ static struct dm_cache_policy *mq_create(dm_cblock_t cache_size, mq->tick = 0; mq->hit_count = 0; mq->generation = 0; - mq->promote_threshold = 0; mq->discard_promote_adjustment = DEFAULT_DISCARD_PROMOTE_ADJUSTMENT; mq->read_promote_adjustment = DEFAULT_READ_PROMOTE_ADJUSTMENT; mq->write_promote_adjustment = DEFAULT_WRITE_PROMOTE_ADJUSTMENT; @@ -1265,7 +1291,7 @@ bad_pre_cache_init: static struct dm_cache_policy_type mq_policy_type = { .name = "mq", - .version = {1, 2, 0}, + .version = {1, 3, 0}, .hint_size = 4, .owner = THIS_MODULE, .create = mq_create @@ -1273,7 +1299,7 @@ static struct dm_cache_policy_type mq_policy_type = { static struct dm_cache_policy_type default_policy_type = { .name = "default", - .version = {1, 2, 0}, + .version = {1, 3, 0}, .hint_size = 4, .owner = THIS_MODULE, .create = mq_create, diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 7130505c2425..1e96d7889f51 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -95,7 +95,6 @@ static void dm_unhook_bio(struct dm_hook_info *h, struct bio *bio) /*----------------------------------------------------------------*/ -#define PRISON_CELLS 1024 #define MIGRATION_POOL_SIZE 128 #define COMMIT_PERIOD HZ #define MIGRATION_COUNT_WINDOW 10 @@ -237,8 +236,9 @@ struct cache { /* * origin_blocks entries, discarded if set. */ - dm_oblock_t discard_nr_blocks; + dm_dblock_t discard_nr_blocks; unsigned long *discard_bitset; + uint32_t discard_block_size; /* a power of 2 times sectors per block */ /* * Rather than reconstructing the table line for the status we just @@ -310,6 +310,7 @@ struct dm_cache_migration { dm_cblock_t cblock; bool err:1; + bool discard:1; bool writeback:1; bool demote:1; bool promote:1; @@ -433,11 +434,12 @@ static void prealloc_put_cell(struct prealloc *p, struct dm_bio_prison_cell *cel /*----------------------------------------------------------------*/ -static void build_key(dm_oblock_t oblock, struct dm_cell_key *key) +static void build_key(dm_oblock_t begin, dm_oblock_t end, struct dm_cell_key *key) { key->virtual = 0; key->dev = 0; - key->block = from_oblock(oblock); + key->block_begin = from_oblock(begin); + key->block_end = from_oblock(end); } /* @@ -447,15 +449,15 @@ static void build_key(dm_oblock_t oblock, struct dm_cell_key *key) */ typedef void (*cell_free_fn)(void *context, struct dm_bio_prison_cell *cell); -static int bio_detain(struct cache *cache, dm_oblock_t oblock, - struct bio *bio, struct dm_bio_prison_cell *cell_prealloc, - cell_free_fn free_fn, void *free_context, - struct dm_bio_prison_cell **cell_result) +static int bio_detain_range(struct cache *cache, dm_oblock_t oblock_begin, dm_oblock_t oblock_end, + struct bio *bio, struct dm_bio_prison_cell *cell_prealloc, + cell_free_fn free_fn, void *free_context, + struct dm_bio_prison_cell **cell_result) { int r; struct dm_cell_key key; - build_key(oblock, &key); + build_key(oblock_begin, oblock_end, &key); r = dm_bio_detain(cache->prison, &key, bio, cell_prealloc, cell_result); if (r) free_fn(free_context, cell_prealloc); @@ -463,6 +465,16 @@ static int bio_detain(struct cache *cache, dm_oblock_t oblock, return r; } +static int bio_detain(struct cache *cache, dm_oblock_t oblock, + struct bio *bio, struct dm_bio_prison_cell *cell_prealloc, + cell_free_fn free_fn, void *free_context, + struct dm_bio_prison_cell **cell_result) +{ + dm_oblock_t end = to_oblock(from_oblock(oblock) + 1ULL); + return bio_detain_range(cache, oblock, end, bio, + cell_prealloc, free_fn, free_context, cell_result); +} + static int get_cell(struct cache *cache, dm_oblock_t oblock, struct prealloc *structs, @@ -474,7 +486,7 @@ static int get_cell(struct cache *cache, cell_prealloc = prealloc_get_cell(structs); - build_key(oblock, &key); + build_key(oblock, to_oblock(from_oblock(oblock) + 1ULL), &key); r = dm_get_cell(cache->prison, &key, cell_prealloc, cell_result); if (r) prealloc_put_cell(structs, cell_prealloc); @@ -524,33 +536,57 @@ static dm_block_t block_div(dm_block_t b, uint32_t n) return b; } -static void set_discard(struct cache *cache, dm_oblock_t b) +static dm_block_t oblocks_per_dblock(struct cache *cache) +{ + dm_block_t oblocks = cache->discard_block_size; + + if (block_size_is_power_of_two(cache)) + oblocks >>= cache->sectors_per_block_shift; + else + oblocks = block_div(oblocks, cache->sectors_per_block); + + return oblocks; +} + +static dm_dblock_t oblock_to_dblock(struct cache *cache, dm_oblock_t oblock) +{ + return to_dblock(block_div(from_oblock(oblock), + oblocks_per_dblock(cache))); +} + +static dm_oblock_t dblock_to_oblock(struct cache *cache, dm_dblock_t dblock) +{ + return to_oblock(from_dblock(dblock) * oblocks_per_dblock(cache)); +} + +static void set_discard(struct cache *cache, dm_dblock_t b) { unsigned long flags; + BUG_ON(from_dblock(b) >= from_dblock(cache->discard_nr_blocks)); atomic_inc(&cache->stats.discard_count); spin_lock_irqsave(&cache->lock, flags); - set_bit(from_oblock(b), cache->discard_bitset); + set_bit(from_dblock(b), cache->discard_bitset); spin_unlock_irqrestore(&cache->lock, flags); } -static void clear_discard(struct cache *cache, dm_oblock_t b) +static void clear_discard(struct cache *cache, dm_dblock_t b) { unsigned long flags; spin_lock_irqsave(&cache->lock, flags); - clear_bit(from_oblock(b), cache->discard_bitset); + clear_bit(from_dblock(b), cache->discard_bitset); spin_unlock_irqrestore(&cache->lock, flags); } -static bool is_discarded(struct cache *cache, dm_oblock_t b) +static bool is_discarded(struct cache *cache, dm_dblock_t b) { int r; unsigned long flags; spin_lock_irqsave(&cache->lock, flags); - r = test_bit(from_oblock(b), cache->discard_bitset); + r = test_bit(from_dblock(b), cache->discard_bitset); spin_unlock_irqrestore(&cache->lock, flags); return r; @@ -562,7 +598,8 @@ static bool is_discarded_oblock(struct cache *cache, dm_oblock_t b) unsigned long flags; spin_lock_irqsave(&cache->lock, flags); - r = test_bit(from_oblock(b), cache->discard_bitset); + r = test_bit(from_dblock(oblock_to_dblock(cache, b)), + cache->discard_bitset); spin_unlock_irqrestore(&cache->lock, flags); return r; @@ -687,7 +724,7 @@ static void remap_to_origin_clear_discard(struct cache *cache, struct bio *bio, check_if_tick_bio_needed(cache, bio); remap_to_origin(cache, bio); if (bio_data_dir(bio) == WRITE) - clear_discard(cache, oblock); + clear_discard(cache, oblock_to_dblock(cache, oblock)); } static void remap_to_cache_dirty(struct cache *cache, struct bio *bio, @@ -697,7 +734,7 @@ static void remap_to_cache_dirty(struct cache *cache, struct bio *bio, remap_to_cache(cache, bio, cblock); if (bio_data_dir(bio) == WRITE) { set_dirty(cache, oblock, cblock); - clear_discard(cache, oblock); + clear_discard(cache, oblock_to_dblock(cache, oblock)); } } @@ -951,10 +988,14 @@ static void migration_success_post_commit(struct dm_cache_migration *mg) } } else { - clear_dirty(cache, mg->new_oblock, mg->cblock); - if (mg->requeue_holder) + if (mg->requeue_holder) { + clear_dirty(cache, mg->new_oblock, mg->cblock); cell_defer(cache, mg->new_ocell, true); - else { + } else { + /* + * The block was promoted via an overwrite, so it's dirty. + */ + set_dirty(cache, mg->new_oblock, mg->cblock); bio_endio(mg->new_ocell->holder, 0); cell_defer(cache, mg->new_ocell, false); } @@ -978,7 +1019,7 @@ static void copy_complete(int read_err, unsigned long write_err, void *context) wake_worker(cache); } -static void issue_copy_real(struct dm_cache_migration *mg) +static void issue_copy(struct dm_cache_migration *mg) { int r; struct dm_io_region o_region, c_region; @@ -1057,11 +1098,46 @@ static void avoid_copy(struct dm_cache_migration *mg) migration_success_pre_commit(mg); } -static void issue_copy(struct dm_cache_migration *mg) +static void calc_discard_block_range(struct cache *cache, struct bio *bio, + dm_dblock_t *b, dm_dblock_t *e) +{ + sector_t sb = bio->bi_iter.bi_sector; + sector_t se = bio_end_sector(bio); + + *b = to_dblock(dm_sector_div_up(sb, cache->discard_block_size)); + + if (se - sb < cache->discard_block_size) + *e = *b; + else + *e = to_dblock(block_div(se, cache->discard_block_size)); +} + +static void issue_discard(struct dm_cache_migration *mg) +{ + dm_dblock_t b, e; + struct bio *bio = mg->new_ocell->holder; + + calc_discard_block_range(mg->cache, bio, &b, &e); + while (b != e) { + set_discard(mg->cache, b); + b = to_dblock(from_dblock(b) + 1); + } + + bio_endio(bio, 0); + cell_defer(mg->cache, mg->new_ocell, false); + free_migration(mg); +} + +static void issue_copy_or_discard(struct dm_cache_migration *mg) { bool avoid; struct cache *cache = mg->cache; + if (mg->discard) { + issue_discard(mg); + return; + } + if (mg->writeback || mg->demote) avoid = !is_dirty(cache, mg->cblock) || is_discarded_oblock(cache, mg->old_oblock); @@ -1070,13 +1146,14 @@ static void issue_copy(struct dm_cache_migration *mg) avoid = is_discarded_oblock(cache, mg->new_oblock); - if (!avoid && bio_writes_complete_block(cache, bio)) { + if (writeback_mode(&cache->features) && + !avoid && bio_writes_complete_block(cache, bio)) { issue_overwrite(mg, bio); return; } } - avoid ? avoid_copy(mg) : issue_copy_real(mg); + avoid ? avoid_copy(mg) : issue_copy(mg); } static void complete_migration(struct dm_cache_migration *mg) @@ -1161,6 +1238,7 @@ static void promote(struct cache *cache, struct prealloc *structs, struct dm_cache_migration *mg = prealloc_get_migration(structs); mg->err = false; + mg->discard = false; mg->writeback = false; mg->demote = false; mg->promote = true; @@ -1184,6 +1262,7 @@ static void writeback(struct cache *cache, struct prealloc *structs, struct dm_cache_migration *mg = prealloc_get_migration(structs); mg->err = false; + mg->discard = false; mg->writeback = true; mg->demote = false; mg->promote = false; @@ -1209,6 +1288,7 @@ static void demote_then_promote(struct cache *cache, struct prealloc *structs, struct dm_cache_migration *mg = prealloc_get_migration(structs); mg->err = false; + mg->discard = false; mg->writeback = false; mg->demote = true; mg->promote = true; @@ -1237,6 +1317,7 @@ static void invalidate(struct cache *cache, struct prealloc *structs, struct dm_cache_migration *mg = prealloc_get_migration(structs); mg->err = false; + mg->discard = false; mg->writeback = false; mg->demote = true; mg->promote = false; @@ -1253,6 +1334,26 @@ static void invalidate(struct cache *cache, struct prealloc *structs, quiesce_migration(mg); } +static void discard(struct cache *cache, struct prealloc *structs, + struct dm_bio_prison_cell *cell) +{ + struct dm_cache_migration *mg = prealloc_get_migration(structs); + + mg->err = false; + mg->discard = true; + mg->writeback = false; + mg->demote = false; + mg->promote = false; + mg->requeue_holder = false; + mg->invalidate = false; + mg->cache = cache; + mg->old_ocell = NULL; + mg->new_ocell = cell; + mg->start_jiffies = jiffies; + + quiesce_migration(mg); +} + /*---------------------------------------------------------------- * bio processing *--------------------------------------------------------------*/ @@ -1286,31 +1387,27 @@ static void process_flush_bio(struct cache *cache, struct bio *bio) issue(cache, bio); } -/* - * People generally discard large parts of a device, eg, the whole device - * when formatting. Splitting these large discards up into cache block - * sized ios and then quiescing (always neccessary for discard) takes too - * long. - * - * We keep it simple, and allow any size of discard to come in, and just - * mark off blocks on the discard bitset. No passdown occurs! - * - * To implement passdown we need to change the bio_prison such that a cell - * can have a key that spans many blocks. - */ -static void process_discard_bio(struct cache *cache, struct bio *bio) +static void process_discard_bio(struct cache *cache, struct prealloc *structs, + struct bio *bio) { - dm_block_t start_block = dm_sector_div_up(bio->bi_iter.bi_sector, - cache->sectors_per_block); - dm_block_t end_block = bio_end_sector(bio); - dm_block_t b; + int r; + dm_dblock_t b, e; + struct dm_bio_prison_cell *cell_prealloc, *new_ocell; - end_block = block_div(end_block, cache->sectors_per_block); + calc_discard_block_range(cache, bio, &b, &e); + if (b == e) { + bio_endio(bio, 0); + return; + } - for (b = start_block; b < end_block; b++) - set_discard(cache, to_oblock(b)); + cell_prealloc = prealloc_get_cell(structs); + r = bio_detain_range(cache, dblock_to_oblock(cache, b), dblock_to_oblock(cache, e), bio, cell_prealloc, + (cell_free_fn) prealloc_put_cell, + structs, &new_ocell); + if (r > 0) + return; - bio_endio(bio, 0); + discard(cache, structs, new_ocell); } static bool spare_migration_bandwidth(struct cache *cache) @@ -1340,9 +1437,8 @@ static void process_bio(struct cache *cache, struct prealloc *structs, dm_oblock_t block = get_bio_block(cache, bio); struct dm_bio_prison_cell *cell_prealloc, *old_ocell, *new_ocell; struct policy_result lookup_result; - bool discarded_block = is_discarded_oblock(cache, block); bool passthrough = passthrough_mode(&cache->features); - bool can_migrate = !passthrough && (discarded_block || spare_migration_bandwidth(cache)); + bool discarded_block, can_migrate; /* * Check to see if that block is currently migrating. @@ -1354,6 +1450,9 @@ static void process_bio(struct cache *cache, struct prealloc *structs, if (r > 0) return; + discarded_block = is_discarded_oblock(cache, block); + can_migrate = !passthrough && (discarded_block || spare_migration_bandwidth(cache)); + r = policy_map(cache->policy, block, true, can_migrate, discarded_block, bio, &lookup_result); @@ -1500,7 +1599,7 @@ static void process_deferred_bios(struct cache *cache) if (bio->bi_rw & REQ_FLUSH) process_flush_bio(cache, bio); else if (bio->bi_rw & REQ_DISCARD) - process_discard_bio(cache, bio); + process_discard_bio(cache, &structs, bio); else process_bio(cache, &structs, bio); } @@ -1715,7 +1814,7 @@ static void do_worker(struct work_struct *ws) process_invalidation_requests(cache); } - process_migrations(cache, &cache->quiesced_migrations, issue_copy); + process_migrations(cache, &cache->quiesced_migrations, issue_copy_or_discard); process_migrations(cache, &cache->completed_migrations, complete_migration); if (commit_if_needed(cache)) { @@ -2180,6 +2279,45 @@ static int create_cache_policy(struct cache *cache, struct cache_args *ca, return 0; } +/* + * We want the discard block size to be at least the size of the cache + * block size and have no more than 2^14 discard blocks across the origin. + */ +#define MAX_DISCARD_BLOCKS (1 << 14) + +static bool too_many_discard_blocks(sector_t discard_block_size, + sector_t origin_size) +{ + (void) sector_div(origin_size, discard_block_size); + + return origin_size > MAX_DISCARD_BLOCKS; +} + +static sector_t calculate_discard_block_size(sector_t cache_block_size, + sector_t origin_size) +{ + sector_t discard_block_size = cache_block_size; + + if (origin_size) + while (too_many_discard_blocks(discard_block_size, origin_size)) + discard_block_size *= 2; + + return discard_block_size; +} + +static void set_cache_size(struct cache *cache, dm_cblock_t size) +{ + dm_block_t nr_blocks = from_cblock(size); + + if (nr_blocks > (1 << 20) && cache->cache_size != size) + DMWARN_LIMIT("You have created a cache device with a lot of individual cache blocks (%llu)\n" + "All these mappings can consume a lot of kernel memory, and take some time to read/write.\n" + "Please consider increasing the cache block size to reduce the overall cache block count.", + (unsigned long long) nr_blocks); + + cache->cache_size = size; +} + #define DEFAULT_MIGRATION_THRESHOLD 2048 static int cache_create(struct cache_args *ca, struct cache **result) @@ -2204,8 +2342,7 @@ static int cache_create(struct cache_args *ca, struct cache **result) ti->num_discard_bios = 1; ti->discards_supported = true; ti->discard_zeroes_data_unsupported = true; - /* Discard bios must be split on a block boundary */ - ti->split_discard_bios = true; + ti->split_discard_bios = false; cache->features = ca->features; ti->per_bio_data_size = get_per_bio_data_size(cache); @@ -2235,10 +2372,10 @@ static int cache_create(struct cache_args *ca, struct cache **result) cache->sectors_per_block_shift = -1; cache_size = block_div(cache_size, ca->block_size); - cache->cache_size = to_cblock(cache_size); + set_cache_size(cache, to_cblock(cache_size)); } else { cache->sectors_per_block_shift = __ffs(ca->block_size); - cache->cache_size = to_cblock(ca->cache_sectors >> cache->sectors_per_block_shift); + set_cache_size(cache, to_cblock(ca->cache_sectors >> cache->sectors_per_block_shift)); } r = create_cache_policy(cache, ca, error); @@ -2303,13 +2440,17 @@ static int cache_create(struct cache_args *ca, struct cache **result) } clear_bitset(cache->dirty_bitset, from_cblock(cache->cache_size)); - cache->discard_nr_blocks = cache->origin_blocks; - cache->discard_bitset = alloc_bitset(from_oblock(cache->discard_nr_blocks)); + cache->discard_block_size = + calculate_discard_block_size(cache->sectors_per_block, + cache->origin_sectors); + cache->discard_nr_blocks = to_dblock(dm_sector_div_up(cache->origin_sectors, + cache->discard_block_size)); + cache->discard_bitset = alloc_bitset(from_dblock(cache->discard_nr_blocks)); if (!cache->discard_bitset) { *error = "could not allocate discard bitset"; goto bad; } - clear_bitset(cache->discard_bitset, from_oblock(cache->discard_nr_blocks)); + clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks)); cache->copier = dm_kcopyd_client_create(&dm_kcopyd_throttle); if (IS_ERR(cache->copier)) { @@ -2327,7 +2468,7 @@ static int cache_create(struct cache_args *ca, struct cache **result) INIT_DELAYED_WORK(&cache->waker, do_waker); cache->last_commit_jiffies = jiffies; - cache->prison = dm_bio_prison_create(PRISON_CELLS); + cache->prison = dm_bio_prison_create(); if (!cache->prison) { *error = "could not create bio prison"; goto bad; @@ -2549,11 +2690,11 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso static int cache_map(struct dm_target *ti, struct bio *bio) { int r; - struct dm_bio_prison_cell *cell; + struct dm_bio_prison_cell *cell = NULL; struct cache *cache = ti->private; r = __cache_map(cache, bio, &cell); - if (r == DM_MAPIO_REMAPPED) { + if (r == DM_MAPIO_REMAPPED && cell) { inc_ds(cache, bio, cell); cell_defer(cache, cell, false); } @@ -2599,16 +2740,16 @@ static int write_discard_bitset(struct cache *cache) { unsigned i, r; - r = dm_cache_discard_bitset_resize(cache->cmd, cache->sectors_per_block, - cache->origin_blocks); + r = dm_cache_discard_bitset_resize(cache->cmd, cache->discard_block_size, + cache->discard_nr_blocks); if (r) { DMERR("could not resize on-disk discard bitset"); return r; } - for (i = 0; i < from_oblock(cache->discard_nr_blocks); i++) { - r = dm_cache_set_discard(cache->cmd, to_oblock(i), - is_discarded(cache, to_oblock(i))); + for (i = 0; i < from_dblock(cache->discard_nr_blocks); i++) { + r = dm_cache_set_discard(cache->cmd, to_dblock(i), + is_discarded(cache, to_dblock(i))); if (r) return r; } @@ -2680,15 +2821,86 @@ static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock, return 0; } +/* + * The discard block size in the on disk metadata is not + * neccessarily the same as we're currently using. So we have to + * be careful to only set the discarded attribute if we know it + * covers a complete block of the new size. + */ +struct discard_load_info { + struct cache *cache; + + /* + * These blocks are sized using the on disk dblock size, rather + * than the current one. + */ + dm_block_t block_size; + dm_block_t discard_begin, discard_end; +}; + +static void discard_load_info_init(struct cache *cache, + struct discard_load_info *li) +{ + li->cache = cache; + li->discard_begin = li->discard_end = 0; +} + +static void set_discard_range(struct discard_load_info *li) +{ + sector_t b, e; + + if (li->discard_begin == li->discard_end) + return; + + /* + * Convert to sectors. + */ + b = li->discard_begin * li->block_size; + e = li->discard_end * li->block_size; + + /* + * Then convert back to the current dblock size. + */ + b = dm_sector_div_up(b, li->cache->discard_block_size); + sector_div(e, li->cache->discard_block_size); + + /* + * The origin may have shrunk, so we need to check we're still in + * bounds. + */ + if (e > from_dblock(li->cache->discard_nr_blocks)) + e = from_dblock(li->cache->discard_nr_blocks); + + for (; b < e; b++) + set_discard(li->cache, to_dblock(b)); +} + static int load_discard(void *context, sector_t discard_block_size, - dm_oblock_t oblock, bool discard) + dm_dblock_t dblock, bool discard) { - struct cache *cache = context; + struct discard_load_info *li = context; - if (discard) - set_discard(cache, oblock); - else - clear_discard(cache, oblock); + li->block_size = discard_block_size; + + if (discard) { + if (from_dblock(dblock) == li->discard_end) + /* + * We're already in a discard range, just extend it. + */ + li->discard_end = li->discard_end + 1ULL; + + else { + /* + * Emit the old range and start a new one. + */ + set_discard_range(li); + li->discard_begin = from_dblock(dblock); + li->discard_end = li->discard_begin + 1ULL; + } + } else { + set_discard_range(li); + li->discard_begin = li->discard_end = 0; + } return 0; } @@ -2730,7 +2942,7 @@ static int resize_cache_dev(struct cache *cache, dm_cblock_t new_size) return r; } - cache->cache_size = new_size; + set_cache_size(cache, new_size); return 0; } @@ -2772,11 +2984,22 @@ static int cache_preresume(struct dm_target *ti) } if (!cache->loaded_discards) { - r = dm_cache_load_discards(cache->cmd, load_discard, cache); + struct discard_load_info li; + + /* + * The discard bitset could have been resized, or the + * discard block size changed. To be safe we start by + * setting every dblock to not discarded. + */ + clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks)); + + discard_load_info_init(cache, &li); + r = dm_cache_load_discards(cache->cmd, load_discard, &li); if (r) { DMERR("could not load origin discards"); return r; } + set_discard_range(&li); cache->loaded_discards = true; } @@ -3079,8 +3302,9 @@ static void set_discard_limits(struct cache *cache, struct queue_limits *limits) /* * FIXME: these limits may be incompatible with the cache device */ - limits->max_discard_sectors = cache->sectors_per_block; - limits->discard_granularity = cache->sectors_per_block << SECTOR_SHIFT; + limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024, + cache->origin_sectors); + limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT; } static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits) @@ -3104,7 +3328,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits) static struct target_type cache_target = { .name = "cache", - .version = {1, 5, 0}, + .version = {1, 6, 0}, .module = THIS_MODULE, .ctr = cache_ctr, .dtr = cache_dtr, diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index fc93b9330af4..08981be7baa1 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -705,7 +705,7 @@ static int crypt_iv_tcw_whitening(struct crypt_config *cc, for (i = 0; i < ((1 << SECTOR_SHIFT) / 8); i++) crypto_xor(data + i * 8, buf, 8); out: - memset(buf, 0, sizeof(buf)); + memzero_explicit(buf, sizeof(buf)); return r; } diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 0be9381365d7..73f791bb9ea4 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -684,11 +684,14 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param) int srcu_idx; param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | - DM_ACTIVE_PRESENT_FLAG); + DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG); if (dm_suspended_md(md)) param->flags |= DM_SUSPEND_FLAG; + if (dm_suspended_internally_md(md)) + param->flags |= DM_INTERNAL_SUSPEND_FLAG; + if (dm_test_deferred_remove_flag(md)) param->flags |= DM_DEFERRED_REMOVE; diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 87f86c77b094..f478a4c96d2f 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -824,7 +824,7 @@ static int message_stats_create(struct mapped_device *md, return 1; id = dm_stats_create(dm_get_stats(md), start, end, step, program_id, aux_data, - dm_internal_suspend, dm_internal_resume, md); + dm_internal_suspend_fast, dm_internal_resume_fast, md); if (id < 0) return id; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index b2bd1ebf4562..3afae9e062f8 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1521,18 +1521,32 @@ fmode_t dm_table_get_mode(struct dm_table *t) } EXPORT_SYMBOL(dm_table_get_mode); -static void suspend_targets(struct dm_table *t, unsigned postsuspend) +enum suspend_mode { + PRESUSPEND, + PRESUSPEND_UNDO, + POSTSUSPEND, +}; + +static void suspend_targets(struct dm_table *t, enum suspend_mode mode) { int i = t->num_targets; struct dm_target *ti = t->targets; while (i--) { - if (postsuspend) { + switch (mode) { + case PRESUSPEND: + if (ti->type->presuspend) + ti->type->presuspend(ti); + break; + case PRESUSPEND_UNDO: + if (ti->type->presuspend_undo) + ti->type->presuspend_undo(ti); + break; + case POSTSUSPEND: if (ti->type->postsuspend) ti->type->postsuspend(ti); - } else if (ti->type->presuspend) - ti->type->presuspend(ti); - + break; + } ti++; } } @@ -1542,7 +1556,15 @@ void dm_table_presuspend_targets(struct dm_table *t) if (!t) return; - suspend_targets(t, 0); + suspend_targets(t, PRESUSPEND); +} + +void dm_table_presuspend_undo_targets(struct dm_table *t) +{ + if (!t) + return; + + suspend_targets(t, PRESUSPEND_UNDO); } void dm_table_postsuspend_targets(struct dm_table *t) @@ -1550,7 +1572,7 @@ void dm_table_postsuspend_targets(struct dm_table *t) if (!t) return; - suspend_targets(t, 1); + suspend_targets(t, POSTSUSPEND); } int dm_table_resume_targets(struct dm_table *t) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index e9d33ad59df5..43adbb863f5a 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1384,42 +1384,38 @@ static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time) } int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block, - int can_block, struct dm_thin_lookup_result *result) + int can_issue_io, struct dm_thin_lookup_result *result) { - int r = -EINVAL; - uint64_t block_time = 0; + int r; __le64 value; struct dm_pool_metadata *pmd = td->pmd; dm_block_t keys[2] = { td->id, block }; struct dm_btree_info *info; - if (can_block) { - down_read(&pmd->root_lock); - info = &pmd->info; - } else if (down_read_trylock(&pmd->root_lock)) - info = &pmd->nb_info; - else - return -EWOULDBLOCK; - if (pmd->fail_io) - goto out; + return -EINVAL; - r = dm_btree_lookup(info, pmd->root, keys, &value); - if (!r) - block_time = le64_to_cpu(value); + down_read(&pmd->root_lock); -out: - up_read(&pmd->root_lock); + if (can_issue_io) { + info = &pmd->info; + } else + info = &pmd->nb_info; + r = dm_btree_lookup(info, pmd->root, keys, &value); if (!r) { + uint64_t block_time = 0; dm_block_t exception_block; uint32_t exception_time; + + block_time = le64_to_cpu(value); unpack_block_time(block_time, &exception_block, &exception_time); result->block = exception_block; result->shared = __snapshotted_since(td, exception_time); } + up_read(&pmd->root_lock); return r; } @@ -1813,3 +1809,8 @@ bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd) return needs_check; } + +void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd) +{ + dm_tm_issue_prefetches(pmd->tm); +} diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h index e3c857db195a..921d15ee56a0 100644 --- a/drivers/md/dm-thin-metadata.h +++ b/drivers/md/dm-thin-metadata.h @@ -139,12 +139,12 @@ struct dm_thin_lookup_result { /* * Returns: - * -EWOULDBLOCK iff @can_block is set and would block. + * -EWOULDBLOCK iff @can_issue_io is set and would issue IO * -ENODATA iff that mapping is not present. * 0 success */ int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block, - int can_block, struct dm_thin_lookup_result *result); + int can_issue_io, struct dm_thin_lookup_result *result); /* * Obtain an unused block. @@ -213,6 +213,11 @@ int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd, int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd); bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd); +/* + * Issue any prefetches that may be useful. + */ +void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd); + /*----------------------------------------------------------------*/ #endif diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 0f86d802b533..8735543eacdb 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -11,11 +11,13 @@ #include <linux/device-mapper.h> #include <linux/dm-io.h> #include <linux/dm-kcopyd.h> +#include <linux/log2.h> #include <linux/list.h> #include <linux/rculist.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sort.h> #include <linux/rbtree.h> #define DM_MSG_PREFIX "thin" @@ -25,7 +27,6 @@ */ #define ENDIO_HOOK_POOL_SIZE 1024 #define MAPPING_POOL_SIZE 1024 -#define PRISON_CELLS 1024 #define COMMIT_PERIOD HZ #define NO_SPACE_TIMEOUT_SECS 60 @@ -114,7 +115,8 @@ static void build_data_key(struct dm_thin_device *td, { key->virtual = 0; key->dev = dm_thin_dev_id(td); - key->block = b; + key->block_begin = b; + key->block_end = b + 1ULL; } static void build_virtual_key(struct dm_thin_device *td, dm_block_t b, @@ -122,7 +124,55 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b, { key->virtual = 1; key->dev = dm_thin_dev_id(td); - key->block = b; + key->block_begin = b; + key->block_end = b + 1ULL; +} + +/*----------------------------------------------------------------*/ + +#define THROTTLE_THRESHOLD (1 * HZ) + +struct throttle { + struct rw_semaphore lock; + unsigned long threshold; + bool throttle_applied; +}; + +static void throttle_init(struct throttle *t) +{ + init_rwsem(&t->lock); + t->throttle_applied = false; +} + +static void throttle_work_start(struct throttle *t) +{ + t->threshold = jiffies + THROTTLE_THRESHOLD; +} + +static void throttle_work_update(struct throttle *t) +{ + if (!t->throttle_applied && jiffies > t->threshold) { + down_write(&t->lock); + t->throttle_applied = true; + } +} + +static void throttle_work_complete(struct throttle *t) +{ + if (t->throttle_applied) { + t->throttle_applied = false; + up_write(&t->lock); + } +} + +static void throttle_lock(struct throttle *t) +{ + down_read(&t->lock); +} + +static void throttle_unlock(struct throttle *t) +{ + up_read(&t->lock); } /*----------------------------------------------------------------*/ @@ -155,8 +205,11 @@ struct pool_features { struct thin_c; typedef void (*process_bio_fn)(struct thin_c *tc, struct bio *bio); +typedef void (*process_cell_fn)(struct thin_c *tc, struct dm_bio_prison_cell *cell); typedef void (*process_mapping_fn)(struct dm_thin_new_mapping *m); +#define CELL_SORT_ARRAY_SIZE 8192 + struct pool { struct list_head list; struct dm_target *ti; /* Only set if a pool target is bound */ @@ -171,11 +224,13 @@ struct pool { struct pool_features pf; bool low_water_triggered:1; /* A dm event has been sent */ + bool suspended:1; struct dm_bio_prison *prison; struct dm_kcopyd_client *copier; struct workqueue_struct *wq; + struct throttle throttle; struct work_struct worker; struct delayed_work waker; struct delayed_work no_space_timeout; @@ -198,8 +253,13 @@ struct pool { process_bio_fn process_bio; process_bio_fn process_discard; + process_cell_fn process_cell; + process_cell_fn process_discard_cell; + process_mapping_fn process_prepared_mapping; process_mapping_fn process_prepared_discard; + + struct dm_bio_prison_cell *cell_sort_array[CELL_SORT_ARRAY_SIZE]; }; static enum pool_mode get_pool_mode(struct pool *pool); @@ -232,8 +292,11 @@ struct thin_c { struct pool *pool; struct dm_thin_device *td; + struct mapped_device *thin_md; + bool requeue_mode:1; spinlock_t lock; + struct list_head deferred_cells; struct bio_list deferred_bio_list; struct bio_list retry_on_resume_list; struct rb_root sort_bio_list; /* sorted list of deferred bios */ @@ -290,6 +353,15 @@ static void cell_release(struct pool *pool, dm_bio_prison_free_cell(pool->prison, cell); } +static void cell_visit_release(struct pool *pool, + void (*fn)(void *, struct dm_bio_prison_cell *), + void *context, + struct dm_bio_prison_cell *cell) +{ + dm_cell_visit_release(pool->prison, fn, context, cell); + dm_bio_prison_free_cell(pool->prison, cell); +} + static void cell_release_no_holder(struct pool *pool, struct dm_bio_prison_cell *cell, struct bio_list *bios) @@ -298,19 +370,6 @@ static void cell_release_no_holder(struct pool *pool, dm_bio_prison_free_cell(pool->prison, cell); } -static void cell_defer_no_holder_no_free(struct thin_c *tc, - struct dm_bio_prison_cell *cell) -{ - struct pool *pool = tc->pool; - unsigned long flags; - - spin_lock_irqsave(&tc->lock, flags); - dm_cell_release_no_holder(pool->prison, cell, &tc->deferred_bio_list); - spin_unlock_irqrestore(&tc->lock, flags); - - wake_worker(pool); -} - static void cell_error_with_code(struct pool *pool, struct dm_bio_prison_cell *cell, int error_code) { @@ -323,6 +382,16 @@ static void cell_error(struct pool *pool, struct dm_bio_prison_cell *cell) cell_error_with_code(pool, cell, -EIO); } +static void cell_success(struct pool *pool, struct dm_bio_prison_cell *cell) +{ + cell_error_with_code(pool, cell, 0); +} + +static void cell_requeue(struct pool *pool, struct dm_bio_prison_cell *cell) +{ + cell_error_with_code(pool, cell, DM_ENDIO_REQUEUE); +} + /*----------------------------------------------------------------*/ /* @@ -393,44 +462,65 @@ struct dm_thin_endio_hook { struct rb_node rb_node; }; -static void requeue_bio_list(struct thin_c *tc, struct bio_list *master) +static void __merge_bio_list(struct bio_list *bios, struct bio_list *master) +{ + bio_list_merge(bios, master); + bio_list_init(master); +} + +static void error_bio_list(struct bio_list *bios, int error) { struct bio *bio; + + while ((bio = bio_list_pop(bios))) + bio_endio(bio, error); +} + +static void error_thin_bio_list(struct thin_c *tc, struct bio_list *master, int error) +{ struct bio_list bios; unsigned long flags; bio_list_init(&bios); spin_lock_irqsave(&tc->lock, flags); - bio_list_merge(&bios, master); - bio_list_init(master); + __merge_bio_list(&bios, master); spin_unlock_irqrestore(&tc->lock, flags); - while ((bio = bio_list_pop(&bios))) - bio_endio(bio, DM_ENDIO_REQUEUE); + error_bio_list(&bios, error); } -static void requeue_io(struct thin_c *tc) +static void requeue_deferred_cells(struct thin_c *tc) { - requeue_bio_list(tc, &tc->deferred_bio_list); - requeue_bio_list(tc, &tc->retry_on_resume_list); + struct pool *pool = tc->pool; + unsigned long flags; + struct list_head cells; + struct dm_bio_prison_cell *cell, *tmp; + + INIT_LIST_HEAD(&cells); + + spin_lock_irqsave(&tc->lock, flags); + list_splice_init(&tc->deferred_cells, &cells); + spin_unlock_irqrestore(&tc->lock, flags); + + list_for_each_entry_safe(cell, tmp, &cells, user_list) + cell_requeue(pool, cell); } -static void error_thin_retry_list(struct thin_c *tc) +static void requeue_io(struct thin_c *tc) { - struct bio *bio; - unsigned long flags; struct bio_list bios; + unsigned long flags; bio_list_init(&bios); spin_lock_irqsave(&tc->lock, flags); - bio_list_merge(&bios, &tc->retry_on_resume_list); - bio_list_init(&tc->retry_on_resume_list); + __merge_bio_list(&bios, &tc->deferred_bio_list); + __merge_bio_list(&bios, &tc->retry_on_resume_list); spin_unlock_irqrestore(&tc->lock, flags); - while ((bio = bio_list_pop(&bios))) - bio_io_error(bio); + error_bio_list(&bios, DM_ENDIO_REQUEUE); + requeue_deferred_cells(tc); } static void error_retry_list(struct pool *pool) @@ -439,7 +529,7 @@ static void error_retry_list(struct pool *pool) rcu_read_lock(); list_for_each_entry_rcu(tc, &pool->active_thins, list) - error_thin_retry_list(tc); + error_thin_bio_list(tc, &tc->retry_on_resume_list, -EIO); rcu_read_unlock(); } @@ -629,33 +719,75 @@ static void overwrite_endio(struct bio *bio, int err) */ /* - * This sends the bios in the cell back to the deferred_bios list. + * This sends the bios in the cell, except the original holder, back + * to the deferred_bios list. */ -static void cell_defer(struct thin_c *tc, struct dm_bio_prison_cell *cell) +static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *cell) { struct pool *pool = tc->pool; unsigned long flags; spin_lock_irqsave(&tc->lock, flags); - cell_release(pool, cell, &tc->deferred_bio_list); + cell_release_no_holder(pool, cell, &tc->deferred_bio_list); spin_unlock_irqrestore(&tc->lock, flags); wake_worker(pool); } -/* - * Same as cell_defer above, except it omits the original holder of the cell. - */ -static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *cell) +static void thin_defer_bio(struct thin_c *tc, struct bio *bio); + +struct remap_info { + struct thin_c *tc; + struct bio_list defer_bios; + struct bio_list issue_bios; +}; + +static void __inc_remap_and_issue_cell(void *context, + struct dm_bio_prison_cell *cell) { - struct pool *pool = tc->pool; - unsigned long flags; + struct remap_info *info = context; + struct bio *bio; - spin_lock_irqsave(&tc->lock, flags); - cell_release_no_holder(pool, cell, &tc->deferred_bio_list); - spin_unlock_irqrestore(&tc->lock, flags); + while ((bio = bio_list_pop(&cell->bios))) { + if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) + bio_list_add(&info->defer_bios, bio); + else { + inc_all_io_entry(info->tc->pool, bio); - wake_worker(pool); + /* + * We can't issue the bios with the bio prison lock + * held, so we add them to a list to issue on + * return from this function. + */ + bio_list_add(&info->issue_bios, bio); + } + } +} + +static void inc_remap_and_issue_cell(struct thin_c *tc, + struct dm_bio_prison_cell *cell, + dm_block_t block) +{ + struct bio *bio; + struct remap_info info; + + info.tc = tc; + bio_list_init(&info.defer_bios); + bio_list_init(&info.issue_bios); + + /* + * We have to be careful to inc any bios we're about to issue + * before the cell is released, and avoid a race with new bios + * being added to the cell. + */ + cell_visit_release(tc->pool, __inc_remap_and_issue_cell, + &info, cell); + + while ((bio = bio_list_pop(&info.defer_bios))) + thin_defer_bio(tc, bio); + + while ((bio = bio_list_pop(&info.issue_bios))) + remap_and_issue(info.tc, bio, block); } static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m) @@ -706,10 +838,13 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) * the bios in the cell. */ if (bio) { - cell_defer_no_holder(tc, m->cell); + inc_remap_and_issue_cell(tc, m->cell, m->data_block); bio_endio(bio, 0); - } else - cell_defer(tc, m->cell); + } else { + inc_all_io_entry(tc->pool, m->cell->holder); + remap_and_issue(tc, m->cell->holder, m->data_block); + inc_remap_and_issue_cell(tc, m->cell, m->data_block); + } out: list_del(&m->list); @@ -842,6 +977,20 @@ static void ll_zero(struct thin_c *tc, struct dm_thin_new_mapping *m, } } +static void remap_and_issue_overwrite(struct thin_c *tc, struct bio *bio, + dm_block_t data_block, + struct dm_thin_new_mapping *m) +{ + struct pool *pool = tc->pool; + struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); + + h->overwrite_mapping = m; + m->bio = bio; + save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio); + inc_all_io_entry(pool, bio); + remap_and_issue(tc, bio, data_block); +} + /* * A partial copy also needs to zero the uncopied region. */ @@ -876,15 +1025,9 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block, * If the whole block of data is being overwritten, we can issue the * bio immediately. Otherwise we use kcopyd to clone the data first. */ - if (io_overwrites_block(pool, bio)) { - struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); - - h->overwrite_mapping = m; - m->bio = bio; - save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio); - inc_all_io_entry(pool, bio); - remap_and_issue(tc, bio, data_dest); - } else { + if (io_overwrites_block(pool, bio)) + remap_and_issue_overwrite(tc, bio, data_dest, m); + else { struct dm_io_region from, to; from.bdev = origin->bdev; @@ -953,16 +1096,10 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block, if (!pool->pf.zero_new_blocks) process_prepared_mapping(m); - else if (io_overwrites_block(pool, bio)) { - struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); - - h->overwrite_mapping = m; - m->bio = bio; - save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio); - inc_all_io_entry(pool, bio); - remap_and_issue(tc, bio, data_block); + else if (io_overwrites_block(pool, bio)) + remap_and_issue_overwrite(tc, bio, data_block, m); - } else + else ll_zero(tc, m, data_block * pool->sectors_per_block, (data_block + 1) * pool->sectors_per_block); @@ -1134,29 +1271,25 @@ static void retry_bios_on_resume(struct pool *pool, struct dm_bio_prison_cell *c bio_list_init(&bios); cell_release(pool, cell, &bios); - error = should_error_unserviceable_bio(pool); - if (error) - while ((bio = bio_list_pop(&bios))) - bio_endio(bio, error); - else - while ((bio = bio_list_pop(&bios))) - retry_on_resume(bio); + while ((bio = bio_list_pop(&bios))) + retry_on_resume(bio); } -static void process_discard(struct thin_c *tc, struct bio *bio) +static void process_discard_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell) { int r; - unsigned long flags; + struct bio *bio = cell->holder; struct pool *pool = tc->pool; - struct dm_bio_prison_cell *cell, *cell2; - struct dm_cell_key key, key2; + struct dm_bio_prison_cell *cell2; + struct dm_cell_key key2; dm_block_t block = get_bio_block(tc, bio); struct dm_thin_lookup_result lookup_result; struct dm_thin_new_mapping *m; - build_virtual_key(tc->td, block, &key); - if (bio_detain(tc->pool, &key, bio, &cell)) + if (tc->requeue_mode) { + cell_requeue(pool, cell); return; + } r = dm_thin_find_block(tc->td, block, 1, &lookup_result); switch (r) { @@ -1187,12 +1320,9 @@ static void process_discard(struct thin_c *tc, struct bio *bio) m->cell2 = cell2; m->bio = bio; - if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list)) { - spin_lock_irqsave(&pool->lock, flags); - list_add_tail(&m->list, &pool->prepared_discards); - spin_unlock_irqrestore(&pool->lock, flags); - wake_worker(pool); - } + if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list)) + pool->process_prepared_discard(m); + } else { inc_all_io_entry(pool, bio); cell_defer_no_holder(tc, cell); @@ -1227,6 +1357,19 @@ static void process_discard(struct thin_c *tc, struct bio *bio) } } +static void process_discard_bio(struct thin_c *tc, struct bio *bio) +{ + struct dm_bio_prison_cell *cell; + struct dm_cell_key key; + dm_block_t block = get_bio_block(tc, bio); + + build_virtual_key(tc->td, block, &key); + if (bio_detain(tc->pool, &key, bio, &cell)) + return; + + process_discard_cell(tc, cell); +} + static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block, struct dm_cell_key *key, struct dm_thin_lookup_result *lookup_result, @@ -1255,11 +1398,53 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block, } } +static void __remap_and_issue_shared_cell(void *context, + struct dm_bio_prison_cell *cell) +{ + struct remap_info *info = context; + struct bio *bio; + + while ((bio = bio_list_pop(&cell->bios))) { + if ((bio_data_dir(bio) == WRITE) || + (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA))) + bio_list_add(&info->defer_bios, bio); + else { + struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));; + + h->shared_read_entry = dm_deferred_entry_inc(info->tc->pool->shared_read_ds); + inc_all_io_entry(info->tc->pool, bio); + bio_list_add(&info->issue_bios, bio); + } + } +} + +static void remap_and_issue_shared_cell(struct thin_c *tc, + struct dm_bio_prison_cell *cell, + dm_block_t block) +{ + struct bio *bio; + struct remap_info info; + + info.tc = tc; + bio_list_init(&info.defer_bios); + bio_list_init(&info.issue_bios); + + cell_visit_release(tc->pool, __remap_and_issue_shared_cell, + &info, cell); + + while ((bio = bio_list_pop(&info.defer_bios))) + thin_defer_bio(tc, bio); + + while ((bio = bio_list_pop(&info.issue_bios))) + remap_and_issue(tc, bio, block); +} + static void process_shared_bio(struct thin_c *tc, struct bio *bio, dm_block_t block, - struct dm_thin_lookup_result *lookup_result) + struct dm_thin_lookup_result *lookup_result, + struct dm_bio_prison_cell *virt_cell) { - struct dm_bio_prison_cell *cell; + struct dm_bio_prison_cell *data_cell; struct pool *pool = tc->pool; struct dm_cell_key key; @@ -1268,19 +1453,23 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio, * of being broken so we have nothing further to do here. */ build_data_key(tc->td, lookup_result->block, &key); - if (bio_detain(pool, &key, bio, &cell)) + if (bio_detain(pool, &key, bio, &data_cell)) { + cell_defer_no_holder(tc, virt_cell); return; + } - if (bio_data_dir(bio) == WRITE && bio->bi_iter.bi_size) - break_sharing(tc, bio, block, &key, lookup_result, cell); - else { + if (bio_data_dir(bio) == WRITE && bio->bi_iter.bi_size) { + break_sharing(tc, bio, block, &key, lookup_result, data_cell); + cell_defer_no_holder(tc, virt_cell); + } else { struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); h->shared_read_entry = dm_deferred_entry_inc(pool->shared_read_ds); inc_all_io_entry(pool, bio); - cell_defer_no_holder(tc, cell); - remap_and_issue(tc, bio, lookup_result->block); + + remap_and_issue_shared_cell(tc, data_cell, lookup_result->block); + remap_and_issue_shared_cell(tc, virt_cell, lookup_result->block); } } @@ -1333,34 +1522,28 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block } } -static void process_bio(struct thin_c *tc, struct bio *bio) +static void process_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell) { int r; struct pool *pool = tc->pool; + struct bio *bio = cell->holder; dm_block_t block = get_bio_block(tc, bio); - struct dm_bio_prison_cell *cell; - struct dm_cell_key key; struct dm_thin_lookup_result lookup_result; - /* - * If cell is already occupied, then the block is already - * being provisioned so we have nothing further to do here. - */ - build_virtual_key(tc->td, block, &key); - if (bio_detain(pool, &key, bio, &cell)) + if (tc->requeue_mode) { + cell_requeue(pool, cell); return; + } r = dm_thin_find_block(tc->td, block, 1, &lookup_result); switch (r) { case 0: - if (lookup_result.shared) { - process_shared_bio(tc, bio, block, &lookup_result); - cell_defer_no_holder(tc, cell); /* FIXME: pass this cell into process_shared? */ - } else { + if (lookup_result.shared) + process_shared_bio(tc, bio, block, &lookup_result, cell); + else { inc_all_io_entry(pool, bio); - cell_defer_no_holder(tc, cell); - remap_and_issue(tc, bio, lookup_result.block); + inc_remap_and_issue_cell(tc, cell, lookup_result.block); } break; @@ -1394,7 +1577,26 @@ static void process_bio(struct thin_c *tc, struct bio *bio) } } -static void process_bio_read_only(struct thin_c *tc, struct bio *bio) +static void process_bio(struct thin_c *tc, struct bio *bio) +{ + struct pool *pool = tc->pool; + dm_block_t block = get_bio_block(tc, bio); + struct dm_bio_prison_cell *cell; + struct dm_cell_key key; + + /* + * If cell is already occupied, then the block is already + * being provisioned so we have nothing further to do here. + */ + build_virtual_key(tc->td, block, &key); + if (bio_detain(pool, &key, bio, &cell)) + return; + + process_cell(tc, cell); +} + +static void __process_bio_read_only(struct thin_c *tc, struct bio *bio, + struct dm_bio_prison_cell *cell) { int r; int rw = bio_data_dir(bio); @@ -1404,15 +1606,21 @@ static void process_bio_read_only(struct thin_c *tc, struct bio *bio) r = dm_thin_find_block(tc->td, block, 1, &lookup_result); switch (r) { case 0: - if (lookup_result.shared && (rw == WRITE) && bio->bi_iter.bi_size) + if (lookup_result.shared && (rw == WRITE) && bio->bi_iter.bi_size) { handle_unserviceable_bio(tc->pool, bio); - else { + if (cell) + cell_defer_no_holder(tc, cell); + } else { inc_all_io_entry(tc->pool, bio); remap_and_issue(tc, bio, lookup_result.block); + if (cell) + inc_remap_and_issue_cell(tc, cell, lookup_result.block); } break; case -ENODATA: + if (cell) + cell_defer_no_holder(tc, cell); if (rw != READ) { handle_unserviceable_bio(tc->pool, bio); break; @@ -1431,11 +1639,23 @@ static void process_bio_read_only(struct thin_c *tc, struct bio *bio) default: DMERR_LIMIT("%s: dm_thin_find_block() failed: error = %d", __func__, r); + if (cell) + cell_defer_no_holder(tc, cell); bio_io_error(bio); break; } } +static void process_bio_read_only(struct thin_c *tc, struct bio *bio) +{ + __process_bio_read_only(tc, bio, NULL); +} + +static void process_cell_read_only(struct thin_c *tc, struct dm_bio_prison_cell *cell) +{ + __process_bio_read_only(tc, cell->holder, cell); +} + static void process_bio_success(struct thin_c *tc, struct bio *bio) { bio_endio(bio, 0); @@ -1446,6 +1666,16 @@ static void process_bio_fail(struct thin_c *tc, struct bio *bio) bio_io_error(bio); } +static void process_cell_success(struct thin_c *tc, struct dm_bio_prison_cell *cell) +{ + cell_success(tc->pool, cell); +} + +static void process_cell_fail(struct thin_c *tc, struct dm_bio_prison_cell *cell) +{ + cell_error(tc->pool, cell); +} + /* * FIXME: should we also commit due to size of transaction, measured in * metadata blocks? @@ -1527,9 +1757,10 @@ static void process_thin_deferred_bios(struct thin_c *tc) struct bio *bio; struct bio_list bios; struct blk_plug plug; + unsigned count = 0; if (tc->requeue_mode) { - requeue_bio_list(tc, &tc->deferred_bio_list); + error_thin_bio_list(tc, &tc->deferred_bio_list, DM_ENDIO_REQUEUE); return; } @@ -1568,10 +1799,97 @@ static void process_thin_deferred_bios(struct thin_c *tc) pool->process_discard(tc, bio); else pool->process_bio(tc, bio); + + if ((count++ & 127) == 0) { + throttle_work_update(&pool->throttle); + dm_pool_issue_prefetches(pool->pmd); + } } blk_finish_plug(&plug); } +static int cmp_cells(const void *lhs, const void *rhs) +{ + struct dm_bio_prison_cell *lhs_cell = *((struct dm_bio_prison_cell **) lhs); + struct dm_bio_prison_cell *rhs_cell = *((struct dm_bio_prison_cell **) rhs); + + BUG_ON(!lhs_cell->holder); + BUG_ON(!rhs_cell->holder); + + if (lhs_cell->holder->bi_iter.bi_sector < rhs_cell->holder->bi_iter.bi_sector) + return -1; + + if (lhs_cell->holder->bi_iter.bi_sector > rhs_cell->holder->bi_iter.bi_sector) + return 1; + + return 0; +} + +static unsigned sort_cells(struct pool *pool, struct list_head *cells) +{ + unsigned count = 0; + struct dm_bio_prison_cell *cell, *tmp; + + list_for_each_entry_safe(cell, tmp, cells, user_list) { + if (count >= CELL_SORT_ARRAY_SIZE) + break; + + pool->cell_sort_array[count++] = cell; + list_del(&cell->user_list); + } + + sort(pool->cell_sort_array, count, sizeof(cell), cmp_cells, NULL); + + return count; +} + +static void process_thin_deferred_cells(struct thin_c *tc) +{ + struct pool *pool = tc->pool; + unsigned long flags; + struct list_head cells; + struct dm_bio_prison_cell *cell; + unsigned i, j, count; + + INIT_LIST_HEAD(&cells); + + spin_lock_irqsave(&tc->lock, flags); + list_splice_init(&tc->deferred_cells, &cells); + spin_unlock_irqrestore(&tc->lock, flags); + + if (list_empty(&cells)) + return; + + do { + count = sort_cells(tc->pool, &cells); + + for (i = 0; i < count; i++) { + cell = pool->cell_sort_array[i]; + BUG_ON(!cell->holder); + + /* + * If we've got no free new_mapping structs, and processing + * this bio might require one, we pause until there are some + * prepared mappings to process. + */ + if (ensure_next_mapping(pool)) { + for (j = i; j < count; j++) + list_add(&pool->cell_sort_array[j]->user_list, &cells); + + spin_lock_irqsave(&tc->lock, flags); + list_splice(&cells, &tc->deferred_cells); + spin_unlock_irqrestore(&tc->lock, flags); + return; + } + + if (cell->holder->bi_rw & REQ_DISCARD) + pool->process_discard_cell(tc, cell); + else + pool->process_cell(tc, cell); + } + } while (!list_empty(&cells)); +} + static void thin_get(struct thin_c *tc); static void thin_put(struct thin_c *tc); @@ -1620,6 +1938,7 @@ static void process_deferred_bios(struct pool *pool) tc = get_first_thin(pool); while (tc) { + process_thin_deferred_cells(tc); process_thin_deferred_bios(tc); tc = get_next_thin(pool, tc); } @@ -1653,9 +1972,15 @@ static void do_worker(struct work_struct *ws) { struct pool *pool = container_of(ws, struct pool, worker); + throttle_work_start(&pool->throttle); + dm_pool_issue_prefetches(pool->pmd); + throttle_work_update(&pool->throttle); process_prepared(pool, &pool->prepared_mappings, &pool->process_prepared_mapping); + throttle_work_update(&pool->throttle); process_prepared(pool, &pool->prepared_discards, &pool->process_prepared_discard); + throttle_work_update(&pool->throttle); process_deferred_bios(pool); + throttle_work_complete(&pool->throttle); } /* @@ -1792,6 +2117,8 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) dm_pool_metadata_read_only(pool->pmd); pool->process_bio = process_bio_fail; pool->process_discard = process_bio_fail; + pool->process_cell = process_cell_fail; + pool->process_discard_cell = process_cell_fail; pool->process_prepared_mapping = process_prepared_mapping_fail; pool->process_prepared_discard = process_prepared_discard_fail; @@ -1804,6 +2131,8 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) dm_pool_metadata_read_only(pool->pmd); pool->process_bio = process_bio_read_only; pool->process_discard = process_bio_success; + pool->process_cell = process_cell_read_only; + pool->process_discard_cell = process_cell_success; pool->process_prepared_mapping = process_prepared_mapping_fail; pool->process_prepared_discard = process_prepared_discard_passdown; @@ -1822,7 +2151,9 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) if (old_mode != new_mode) notify_of_pool_mode_change(pool, "out-of-data-space"); pool->process_bio = process_bio_read_only; - pool->process_discard = process_discard; + pool->process_discard = process_discard_bio; + pool->process_cell = process_cell_read_only; + pool->process_discard_cell = process_discard_cell; pool->process_prepared_mapping = process_prepared_mapping; pool->process_prepared_discard = process_prepared_discard_passdown; @@ -1835,7 +2166,9 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) notify_of_pool_mode_change(pool, "write"); dm_pool_metadata_read_write(pool->pmd); pool->process_bio = process_bio; - pool->process_discard = process_discard; + pool->process_discard = process_discard_bio; + pool->process_cell = process_cell; + pool->process_discard_cell = process_discard_cell; pool->process_prepared_mapping = process_prepared_mapping; pool->process_prepared_discard = process_prepared_discard; break; @@ -1895,6 +2228,29 @@ static void thin_defer_bio(struct thin_c *tc, struct bio *bio) wake_worker(pool); } +static void thin_defer_bio_with_throttle(struct thin_c *tc, struct bio *bio) +{ + struct pool *pool = tc->pool; + + throttle_lock(&pool->throttle); + thin_defer_bio(tc, bio); + throttle_unlock(&pool->throttle); +} + +static void thin_defer_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell) +{ + unsigned long flags; + struct pool *pool = tc->pool; + + throttle_lock(&pool->throttle); + spin_lock_irqsave(&tc->lock, flags); + list_add_tail(&cell->user_list, &tc->deferred_cells); + spin_unlock_irqrestore(&tc->lock, flags); + throttle_unlock(&pool->throttle); + + wake_worker(pool); +} + static void thin_hook_bio(struct thin_c *tc, struct bio *bio) { struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); @@ -1915,8 +2271,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) dm_block_t block = get_bio_block(tc, bio); struct dm_thin_device *td = tc->td; struct dm_thin_lookup_result result; - struct dm_bio_prison_cell cell1, cell2; - struct dm_bio_prison_cell *cell_result; + struct dm_bio_prison_cell *virt_cell, *data_cell; struct dm_cell_key key; thin_hook_bio(tc, bio); @@ -1932,7 +2287,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) } if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) { - thin_defer_bio(tc, bio); + thin_defer_bio_with_throttle(tc, bio); return DM_MAPIO_SUBMITTED; } @@ -1941,7 +2296,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) * there's a race with discard. */ build_virtual_key(tc->td, block, &key); - if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result)) + if (bio_detain(tc->pool, &key, bio, &virt_cell)) return DM_MAPIO_SUBMITTED; r = dm_thin_find_block(td, block, 0, &result); @@ -1966,20 +2321,19 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) * More distant ancestors are irrelevant. The * shared flag will be set in their case. */ - thin_defer_bio(tc, bio); - cell_defer_no_holder_no_free(tc, &cell1); + thin_defer_cell(tc, virt_cell); return DM_MAPIO_SUBMITTED; } build_data_key(tc->td, result.block, &key); - if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) { - cell_defer_no_holder_no_free(tc, &cell1); + if (bio_detain(tc->pool, &key, bio, &data_cell)) { + cell_defer_no_holder(tc, virt_cell); return DM_MAPIO_SUBMITTED; } inc_all_io_entry(tc->pool, bio); - cell_defer_no_holder_no_free(tc, &cell2); - cell_defer_no_holder_no_free(tc, &cell1); + cell_defer_no_holder(tc, data_cell); + cell_defer_no_holder(tc, virt_cell); remap(tc, bio, result.block); return DM_MAPIO_REMAPPED; @@ -1991,18 +2345,13 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) * of doing so. */ handle_unserviceable_bio(tc->pool, bio); - cell_defer_no_holder_no_free(tc, &cell1); + cell_defer_no_holder(tc, virt_cell); return DM_MAPIO_SUBMITTED; } /* fall through */ case -EWOULDBLOCK: - /* - * In future, the failed dm_thin_find_block above could - * provide the hint to load the metadata into cache. - */ - thin_defer_bio(tc, bio); - cell_defer_no_holder_no_free(tc, &cell1); + thin_defer_cell(tc, virt_cell); return DM_MAPIO_SUBMITTED; default: @@ -2012,7 +2361,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) * pool is switched to fail-io mode. */ bio_io_error(bio); - cell_defer_no_holder_no_free(tc, &cell1); + cell_defer_no_holder(tc, virt_cell); return DM_MAPIO_SUBMITTED; } } @@ -2193,7 +2542,7 @@ static struct pool *pool_create(struct mapped_device *pool_md, pool->sectors_per_block_shift = __ffs(block_size); pool->low_water_blocks = 0; pool_features_init(&pool->pf); - pool->prison = dm_bio_prison_create(PRISON_CELLS); + pool->prison = dm_bio_prison_create(); if (!pool->prison) { *error = "Error creating pool's bio prison"; err_p = ERR_PTR(-ENOMEM); @@ -2219,6 +2568,7 @@ static struct pool *pool_create(struct mapped_device *pool_md, goto bad_wq; } + throttle_init(&pool->throttle); INIT_WORK(&pool->worker, do_worker); INIT_DELAYED_WORK(&pool->waker, do_waker); INIT_DELAYED_WORK(&pool->no_space_timeout, do_no_space_timeout); @@ -2228,6 +2578,7 @@ static struct pool *pool_create(struct mapped_device *pool_md, INIT_LIST_HEAD(&pool->prepared_discards); INIT_LIST_HEAD(&pool->active_thins); pool->low_water_triggered = false; + pool->suspended = true; pool->shared_read_ds = dm_deferred_set_create(); if (!pool->shared_read_ds) { @@ -2764,20 +3115,77 @@ static int pool_preresume(struct dm_target *ti) return 0; } +static void pool_suspend_active_thins(struct pool *pool) +{ + struct thin_c *tc; + + /* Suspend all active thin devices */ + tc = get_first_thin(pool); + while (tc) { + dm_internal_suspend_noflush(tc->thin_md); + tc = get_next_thin(pool, tc); + } +} + +static void pool_resume_active_thins(struct pool *pool) +{ + struct thin_c *tc; + + /* Resume all active thin devices */ + tc = get_first_thin(pool); + while (tc) { + dm_internal_resume(tc->thin_md); + tc = get_next_thin(pool, tc); + } +} + static void pool_resume(struct dm_target *ti) { struct pool_c *pt = ti->private; struct pool *pool = pt->pool; unsigned long flags; + /* + * Must requeue active_thins' bios and then resume + * active_thins _before_ clearing 'suspend' flag. + */ + requeue_bios(pool); + pool_resume_active_thins(pool); + spin_lock_irqsave(&pool->lock, flags); pool->low_water_triggered = false; + pool->suspended = false; spin_unlock_irqrestore(&pool->lock, flags); - requeue_bios(pool); do_waker(&pool->waker.work); } +static void pool_presuspend(struct dm_target *ti) +{ + struct pool_c *pt = ti->private; + struct pool *pool = pt->pool; + unsigned long flags; + + spin_lock_irqsave(&pool->lock, flags); + pool->suspended = true; + spin_unlock_irqrestore(&pool->lock, flags); + + pool_suspend_active_thins(pool); +} + +static void pool_presuspend_undo(struct dm_target *ti) +{ + struct pool_c *pt = ti->private; + struct pool *pool = pt->pool; + unsigned long flags; + + pool_resume_active_thins(pool); + + spin_lock_irqsave(&pool->lock, flags); + pool->suspended = false; + spin_unlock_irqrestore(&pool->lock, flags); +} + static void pool_postsuspend(struct dm_target *ti) { struct pool_c *pt = ti->private; @@ -2949,7 +3357,6 @@ static int process_release_metadata_snap_mesg(unsigned argc, char **argv, struct * create_thin <dev_id> * create_snap <dev_id> <origin_id> * delete <dev_id> - * trim <dev_id> <new_size_in_sectors> * set_transaction_id <current_trans_id> <new_trans_id> * reserve_metadata_snap * release_metadata_snap @@ -3177,15 +3584,35 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits) { struct pool_c *pt = ti->private; struct pool *pool = pt->pool; - uint64_t io_opt_sectors = limits->io_opt >> SECTOR_SHIFT; + sector_t io_opt_sectors = limits->io_opt >> SECTOR_SHIFT; + + /* + * If max_sectors is smaller than pool->sectors_per_block adjust it + * to the highest possible power-of-2 factor of pool->sectors_per_block. + * This is especially beneficial when the pool's data device is a RAID + * device that has a full stripe width that matches pool->sectors_per_block + * -- because even though partial RAID stripe-sized IOs will be issued to a + * single RAID stripe; when aggregated they will end on a full RAID stripe + * boundary.. which avoids additional partial RAID stripe writes cascading + */ + if (limits->max_sectors < pool->sectors_per_block) { + while (!is_factor(pool->sectors_per_block, limits->max_sectors)) { + if ((limits->max_sectors & (limits->max_sectors - 1)) == 0) + limits->max_sectors--; + limits->max_sectors = rounddown_pow_of_two(limits->max_sectors); + } + } /* * If the system-determined stacked limits are compatible with the * pool's blocksize (io_opt is a factor) do not override them. */ if (io_opt_sectors < pool->sectors_per_block || - do_div(io_opt_sectors, pool->sectors_per_block)) { - blk_limits_io_min(limits, pool->sectors_per_block << SECTOR_SHIFT); + !is_factor(io_opt_sectors, pool->sectors_per_block)) { + if (is_factor(pool->sectors_per_block, limits->max_sectors)) + blk_limits_io_min(limits, limits->max_sectors << SECTOR_SHIFT); + else + blk_limits_io_min(limits, pool->sectors_per_block << SECTOR_SHIFT); blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT); } @@ -3214,11 +3641,13 @@ static struct target_type pool_target = { .name = "thin-pool", .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | DM_TARGET_IMMUTABLE, - .version = {1, 13, 0}, + .version = {1, 14, 0}, .module = THIS_MODULE, .ctr = pool_ctr, .dtr = pool_dtr, .map = pool_map, + .presuspend = pool_presuspend, + .presuspend_undo = pool_presuspend_undo, .postsuspend = pool_postsuspend, .preresume = pool_preresume, .resume = pool_resume, @@ -3248,14 +3677,14 @@ static void thin_dtr(struct dm_target *ti) struct thin_c *tc = ti->private; unsigned long flags; - thin_put(tc); - wait_for_completion(&tc->can_destroy); - spin_lock_irqsave(&tc->pool->lock, flags); list_del_rcu(&tc->list); spin_unlock_irqrestore(&tc->pool->lock, flags); synchronize_rcu(); + thin_put(tc); + wait_for_completion(&tc->can_destroy); + mutex_lock(&dm_thin_pool_table.mutex); __pool_dec(tc->pool); @@ -3302,7 +3731,9 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) r = -ENOMEM; goto out_unlock; } + tc->thin_md = dm_table_get_md(ti->table); spin_lock_init(&tc->lock); + INIT_LIST_HEAD(&tc->deferred_cells); bio_list_init(&tc->deferred_bio_list); bio_list_init(&tc->retry_on_resume_list); tc->sort_bio_list = RB_ROOT; @@ -3347,18 +3778,18 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) if (get_pool_mode(tc->pool) == PM_FAIL) { ti->error = "Couldn't open thin device, Pool is in fail mode"; r = -EINVAL; - goto bad_thin_open; + goto bad_pool; } r = dm_pool_open_thin_device(tc->pool->pmd, tc->dev_id, &tc->td); if (r) { ti->error = "Couldn't open thin internal device"; - goto bad_thin_open; + goto bad_pool; } r = dm_set_target_max_io_len(ti, tc->pool->sectors_per_block); if (r) - goto bad_target_max_io_len; + goto bad; ti->num_flush_bios = 1; ti->flush_supported = true; @@ -3373,14 +3804,16 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) ti->split_discard_bios = true; } - dm_put(pool_md); - mutex_unlock(&dm_thin_pool_table.mutex); - atomic_set(&tc->refcount, 1); - init_completion(&tc->can_destroy); - spin_lock_irqsave(&tc->pool->lock, flags); + if (tc->pool->suspended) { + spin_unlock_irqrestore(&tc->pool->lock, flags); + mutex_lock(&dm_thin_pool_table.mutex); /* reacquire for __pool_dec */ + ti->error = "Unable to activate thin device while pool is suspended"; + r = -EINVAL; + goto bad; + } list_add_tail_rcu(&tc->list, &tc->pool->active_thins); spin_unlock_irqrestore(&tc->pool->lock, flags); /* @@ -3391,11 +3824,16 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) */ synchronize_rcu(); + dm_put(pool_md); + + atomic_set(&tc->refcount, 1); + init_completion(&tc->can_destroy); + return 0; -bad_target_max_io_len: +bad: dm_pool_close_thin_device(tc->td); -bad_thin_open: +bad_pool: __pool_dec(tc->pool); bad_pool_lookup: dm_put(pool_md); @@ -3541,6 +3979,21 @@ err: DMEMIT("Error"); } +static int thin_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size) +{ + struct thin_c *tc = ti->private; + struct request_queue *q = bdev_get_queue(tc->pool_dev->bdev); + + if (!q->merge_bvec_fn) + return max_size; + + bvm->bi_bdev = tc->pool_dev->bdev; + bvm->bi_sector = dm_target_offset(ti, bvm->bi_sector); + + return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); +} + static int thin_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { @@ -3565,7 +4018,7 @@ static int thin_iterate_devices(struct dm_target *ti, static struct target_type thin_target = { .name = "thin", - .version = {1, 13, 0}, + .version = {1, 14, 0}, .module = THIS_MODULE, .ctr = thin_ctr, .dtr = thin_dtr, @@ -3575,6 +4028,7 @@ static struct target_type thin_target = { .presuspend = thin_presuspend, .postsuspend = thin_postsuspend, .status = thin_status, + .merge = thin_merge, .iterate_devices = thin_iterate_devices, }; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 58f3927fd7cc..8f37ed215b19 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -19,6 +19,7 @@ #include <linux/idr.h> #include <linux/hdreg.h> #include <linux/delay.h> +#include <linux/wait.h> #include <trace/events/block.h> @@ -117,6 +118,7 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo); #define DMF_NOFLUSH_SUSPENDING 5 #define DMF_MERGE_IS_OPTIONAL 6 #define DMF_DEFERRED_REMOVE 7 +#define DMF_SUSPENDED_INTERNALLY 8 /* * A dummy definition to make RCU happy. @@ -140,7 +142,7 @@ struct mapped_device { * Use dm_get_live_table{_fast} or take suspend_lock for * dereference. */ - struct dm_table *map; + struct dm_table __rcu *map; struct list_head table_devices; struct mutex table_devices_lock; @@ -525,14 +527,15 @@ retry: goto out; tgt = dm_table_get_target(map, 0); + if (!tgt->type->ioctl) + goto out; if (dm_suspended_md(md)) { r = -EAGAIN; goto out; } - if (tgt->type->ioctl) - r = tgt->type->ioctl(tgt, cmd, arg); + r = tgt->type->ioctl(tgt, cmd, arg); out: dm_put_live_table(md, srcu_idx); @@ -1607,9 +1610,9 @@ static int dm_merge_bvec(struct request_queue *q, * Find maximum amount of I/O that won't need splitting */ max_sectors = min(max_io_len(bvm->bi_sector, ti), - (sector_t) BIO_MAX_SECTORS); + (sector_t) queue_max_sectors(q)); max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size; - if (max_size < 0) + if (unlikely(max_size < 0)) /* this shouldn't _ever_ happen */ max_size = 0; /* @@ -1621,10 +1624,10 @@ static int dm_merge_bvec(struct request_queue *q, max_size = ti->type->merge(ti, bvm, biovec, max_size); /* * If the target doesn't support merge method and some of the devices - * provided their merge_bvec method (we know this by looking at - * queue_max_hw_sectors), then we can't allow bios with multiple vector - * entries. So always set max_size to 0, and the code below allows - * just one page. + * provided their merge_bvec method (we know this by looking for the + * max_hw_sectors that dm_set_device_limits may set), then we can't + * allow bios with multiple vector entries. So always set max_size + * to 0, and the code below allows just one page. */ else if (queue_max_hw_sectors(q) <= PAGE_SIZE >> 9) max_size = 0; @@ -2332,7 +2335,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t, merge_is_optional = dm_table_merge_is_optional(t); - old_map = md->map; + old_map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock)); rcu_assign_pointer(md->map, t); md->immutable_target_type = dm_table_get_immutable_target_type(t); @@ -2341,7 +2344,8 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t, set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags); else clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags); - dm_sync_table(md); + if (old_map) + dm_sync_table(md); return old_map; } @@ -2351,7 +2355,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t, */ static struct dm_table *__unbind(struct mapped_device *md) { - struct dm_table *map = md->map; + struct dm_table *map = rcu_dereference_protected(md->map, 1); if (!map) return NULL; @@ -2716,36 +2720,18 @@ static void unlock_fs(struct mapped_device *md) } /* - * We need to be able to change a mapping table under a mounted - * filesystem. For example we might want to move some data in - * the background. Before the table can be swapped with - * dm_bind_table, dm_suspend must be called to flush any in - * flight bios and ensure that any further io gets deferred. - */ -/* - * Suspend mechanism in request-based dm. + * If __dm_suspend returns 0, the device is completely quiescent + * now. There is no request-processing activity. All new requests + * are being added to md->deferred list. * - * 1. Flush all I/Os by lock_fs() if needed. - * 2. Stop dispatching any I/O by stopping the request_queue. - * 3. Wait for all in-flight I/Os to be completed or requeued. - * - * To abort suspend, start the request_queue. + * Caller must hold md->suspend_lock */ -int dm_suspend(struct mapped_device *md, unsigned suspend_flags) +static int __dm_suspend(struct mapped_device *md, struct dm_table *map, + unsigned suspend_flags, int interruptible) { - struct dm_table *map = NULL; - int r = 0; - int do_lockfs = suspend_flags & DM_SUSPEND_LOCKFS_FLAG ? 1 : 0; - int noflush = suspend_flags & DM_SUSPEND_NOFLUSH_FLAG ? 1 : 0; - - mutex_lock(&md->suspend_lock); - - if (dm_suspended_md(md)) { - r = -EINVAL; - goto out_unlock; - } - - map = md->map; + bool do_lockfs = suspend_flags & DM_SUSPEND_LOCKFS_FLAG; + bool noflush = suspend_flags & DM_SUSPEND_NOFLUSH_FLAG; + int r; /* * DMF_NOFLUSH_SUSPENDING must be set before presuspend. @@ -2754,7 +2740,10 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags) if (noflush) set_bit(DMF_NOFLUSH_SUSPENDING, &md->flags); - /* This does not get reverted if there's an error later. */ + /* + * This gets reverted if there's an error later and the targets + * provide the .presuspend_undo hook. + */ dm_table_presuspend_targets(map); /* @@ -2765,8 +2754,10 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags) */ if (!noflush && do_lockfs) { r = lock_fs(md); - if (r) - goto out_unlock; + if (r) { + dm_table_presuspend_undo_targets(map); + return r; + } } /* @@ -2782,7 +2773,8 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags) * flush_workqueue(md->wq). */ set_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags); - synchronize_srcu(&md->io_barrier); + if (map) + synchronize_srcu(&md->io_barrier); /* * Stop md->queue before flushing md->wq in case request-based @@ -2798,11 +2790,12 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags) * We call dm_wait_for_completion to wait for all existing requests * to finish. */ - r = dm_wait_for_completion(md, TASK_INTERRUPTIBLE); + r = dm_wait_for_completion(md, interruptible); if (noflush) clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags); - synchronize_srcu(&md->io_barrier); + if (map) + synchronize_srcu(&md->io_barrier); /* were we interrupted ? */ if (r < 0) { @@ -2812,14 +2805,56 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags) start_queue(md->queue); unlock_fs(md); - goto out_unlock; /* pushback list is already flushed, so skip flush */ + dm_table_presuspend_undo_targets(map); + /* pushback list is already flushed, so skip flush */ } - /* - * If dm_wait_for_completion returned 0, the device is completely - * quiescent now. There is no request-processing activity. All new - * requests are being added to md->deferred list. - */ + return r; +} + +/* + * We need to be able to change a mapping table under a mounted + * filesystem. For example we might want to move some data in + * the background. Before the table can be swapped with + * dm_bind_table, dm_suspend must be called to flush any in + * flight bios and ensure that any further io gets deferred. + */ +/* + * Suspend mechanism in request-based dm. + * + * 1. Flush all I/Os by lock_fs() if needed. + * 2. Stop dispatching any I/O by stopping the request_queue. + * 3. Wait for all in-flight I/Os to be completed or requeued. + * + * To abort suspend, start the request_queue. + */ +int dm_suspend(struct mapped_device *md, unsigned suspend_flags) +{ + struct dm_table *map = NULL; + int r = 0; + +retry: + mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING); + + if (dm_suspended_md(md)) { + r = -EINVAL; + goto out_unlock; + } + + if (dm_suspended_internally_md(md)) { + /* already internally suspended, wait for internal resume */ + mutex_unlock(&md->suspend_lock); + r = wait_on_bit(&md->flags, DMF_SUSPENDED_INTERNALLY, TASK_INTERRUPTIBLE); + if (r) + return r; + goto retry; + } + + map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock)); + + r = __dm_suspend(md, map, suspend_flags, TASK_INTERRUPTIBLE); + if (r) + goto out_unlock; set_bit(DMF_SUSPENDED, &md->flags); @@ -2830,22 +2865,13 @@ out_unlock: return r; } -int dm_resume(struct mapped_device *md) +static int __dm_resume(struct mapped_device *md, struct dm_table *map) { - int r = -EINVAL; - struct dm_table *map = NULL; - - mutex_lock(&md->suspend_lock); - if (!dm_suspended_md(md)) - goto out; - - map = md->map; - if (!map || !dm_table_get_size(map)) - goto out; - - r = dm_table_resume_targets(map); - if (r) - goto out; + if (map) { + int r = dm_table_resume_targets(map); + if (r) + return r; + } dm_queue_flush(md); @@ -2859,6 +2885,37 @@ int dm_resume(struct mapped_device *md) unlock_fs(md); + return 0; +} + +int dm_resume(struct mapped_device *md) +{ + int r = -EINVAL; + struct dm_table *map = NULL; + +retry: + mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING); + + if (!dm_suspended_md(md)) + goto out; + + if (dm_suspended_internally_md(md)) { + /* already internally suspended, wait for internal resume */ + mutex_unlock(&md->suspend_lock); + r = wait_on_bit(&md->flags, DMF_SUSPENDED_INTERNALLY, TASK_INTERRUPTIBLE); + if (r) + return r; + goto retry; + } + + map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock)); + if (!map || !dm_table_get_size(map)) + goto out; + + r = __dm_resume(md, map); + if (r) + goto out; + clear_bit(DMF_SUSPENDED, &md->flags); r = 0; @@ -2872,15 +2929,80 @@ out: * Internal suspend/resume works like userspace-driven suspend. It waits * until all bios finish and prevents issuing new bios to the target drivers. * It may be used only from the kernel. - * - * Internal suspend holds md->suspend_lock, which prevents interaction with - * userspace-driven suspend. */ -void dm_internal_suspend(struct mapped_device *md) +static void __dm_internal_suspend(struct mapped_device *md, unsigned suspend_flags) { - mutex_lock(&md->suspend_lock); + struct dm_table *map = NULL; + + if (dm_suspended_internally_md(md)) + return; /* nested internal suspend */ + + if (dm_suspended_md(md)) { + set_bit(DMF_SUSPENDED_INTERNALLY, &md->flags); + return; /* nest suspend */ + } + + map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock)); + + /* + * Using TASK_UNINTERRUPTIBLE because only NOFLUSH internal suspend is + * supported. Properly supporting a TASK_INTERRUPTIBLE internal suspend + * would require changing .presuspend to return an error -- avoid this + * until there is a need for more elaborate variants of internal suspend. + */ + (void) __dm_suspend(md, map, suspend_flags, TASK_UNINTERRUPTIBLE); + + set_bit(DMF_SUSPENDED_INTERNALLY, &md->flags); + + dm_table_postsuspend_targets(map); +} + +static void __dm_internal_resume(struct mapped_device *md) +{ + if (!dm_suspended_internally_md(md)) + return; /* resume from nested internal suspend */ + if (dm_suspended_md(md)) + goto done; /* resume from nested suspend */ + + /* + * NOTE: existing callers don't need to call dm_table_resume_targets + * (which may fail -- so best to avoid it for now by passing NULL map) + */ + (void) __dm_resume(md, NULL); + +done: + clear_bit(DMF_SUSPENDED_INTERNALLY, &md->flags); + smp_mb__after_atomic(); + wake_up_bit(&md->flags, DMF_SUSPENDED_INTERNALLY); +} + +void dm_internal_suspend_noflush(struct mapped_device *md) +{ + mutex_lock(&md->suspend_lock); + __dm_internal_suspend(md, DM_SUSPEND_NOFLUSH_FLAG); + mutex_unlock(&md->suspend_lock); +} +EXPORT_SYMBOL_GPL(dm_internal_suspend_noflush); + +void dm_internal_resume(struct mapped_device *md) +{ + mutex_lock(&md->suspend_lock); + __dm_internal_resume(md); + mutex_unlock(&md->suspend_lock); +} +EXPORT_SYMBOL_GPL(dm_internal_resume); + +/* + * Fast variants of internal suspend/resume hold md->suspend_lock, + * which prevents interaction with userspace-driven suspend. + */ + +void dm_internal_suspend_fast(struct mapped_device *md) +{ + mutex_lock(&md->suspend_lock); + if (dm_suspended_md(md) || dm_suspended_internally_md(md)) return; set_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags); @@ -2889,9 +3011,9 @@ void dm_internal_suspend(struct mapped_device *md) dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE); } -void dm_internal_resume(struct mapped_device *md) +void dm_internal_resume_fast(struct mapped_device *md) { - if (dm_suspended_md(md)) + if (dm_suspended_md(md) || dm_suspended_internally_md(md)) goto done; dm_queue_flush(md); @@ -2977,6 +3099,11 @@ int dm_suspended_md(struct mapped_device *md) return test_bit(DMF_SUSPENDED, &md->flags); } +int dm_suspended_internally_md(struct mapped_device *md) +{ + return test_bit(DMF_SUSPENDED_INTERNALLY, &md->flags); +} + int dm_test_deferred_remove_flag(struct mapped_device *md) { return test_bit(DMF_DEFERRED_REMOVE, &md->flags); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 988c7fb7b145..84b0f9e4ba6c 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -65,6 +65,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, struct queue_limits *limits); struct list_head *dm_table_get_devices(struct dm_table *t); void dm_table_presuspend_targets(struct dm_table *t); +void dm_table_presuspend_undo_targets(struct dm_table *t); void dm_table_postsuspend_targets(struct dm_table *t); int dm_table_resume_targets(struct dm_table *t); int dm_table_any_congested(struct dm_table *t, int bdi_bits); @@ -129,6 +130,15 @@ int dm_deleting_md(struct mapped_device *md); int dm_suspended_md(struct mapped_device *md); /* + * Internal suspend and resume methods. + */ +int dm_suspended_internally_md(struct mapped_device *md); +void dm_internal_suspend_fast(struct mapped_device *md); +void dm_internal_resume_fast(struct mapped_device *md); +void dm_internal_suspend_noflush(struct mapped_device *md); +void dm_internal_resume(struct mapped_device *md); + +/* * Test if the device is scheduled for deferred remove. */ int dm_test_deferred_remove_flag(struct mapped_device *md); diff --git a/drivers/md/persistent-data/dm-array.c b/drivers/md/persistent-data/dm-array.c index 1d75b1dc1e2e..e64b61ad0ef3 100644 --- a/drivers/md/persistent-data/dm-array.c +++ b/drivers/md/persistent-data/dm-array.c @@ -645,8 +645,10 @@ static int array_resize(struct dm_array_info *info, dm_block_t root, int r; struct resize resize; - if (old_size == new_size) + if (old_size == new_size) { + *new_root = root; return 0; + } resize.info = info; resize.root = root; diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index 786b689bdfc7..e8a904298887 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c @@ -564,7 +564,9 @@ static int sm_bootstrap_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count { struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); - return smm->ll.nr_blocks; + *count = smm->ll.nr_blocks; + + return 0; } static int sm_bootstrap_get_nr_free(struct dm_space_map *sm, dm_block_t *count) @@ -581,7 +583,9 @@ static int sm_bootstrap_get_count(struct dm_space_map *sm, dm_block_t b, { struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); - return b < smm->begin ? 1 : 0; + *result = (b < smm->begin) ? 1 : 0; + + return 0; } static int sm_bootstrap_count_is_more_than_one(struct dm_space_map *sm, diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index 3bc30a0ae3d6..9cb797d800cf 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c @@ -10,6 +10,8 @@ #include "dm-persistent-data-internal.h" #include <linux/export.h> +#include <linux/mutex.h> +#include <linux/hash.h> #include <linux/slab.h> #include <linux/device-mapper.h> @@ -17,6 +19,61 @@ /*----------------------------------------------------------------*/ +#define PREFETCH_SIZE 128 +#define PREFETCH_BITS 7 +#define PREFETCH_SENTINEL ((dm_block_t) -1ULL) + +struct prefetch_set { + struct mutex lock; + dm_block_t blocks[PREFETCH_SIZE]; +}; + +static unsigned prefetch_hash(dm_block_t b) +{ + return hash_64(b, PREFETCH_BITS); +} + +static void prefetch_wipe(struct prefetch_set *p) +{ + unsigned i; + for (i = 0; i < PREFETCH_SIZE; i++) + p->blocks[i] = PREFETCH_SENTINEL; +} + +static void prefetch_init(struct prefetch_set *p) +{ + mutex_init(&p->lock); + prefetch_wipe(p); +} + +static void prefetch_add(struct prefetch_set *p, dm_block_t b) +{ + unsigned h = prefetch_hash(b); + + mutex_lock(&p->lock); + if (p->blocks[h] == PREFETCH_SENTINEL) + p->blocks[h] = b; + + mutex_unlock(&p->lock); +} + +static void prefetch_issue(struct prefetch_set *p, struct dm_block_manager *bm) +{ + unsigned i; + + mutex_lock(&p->lock); + + for (i = 0; i < PREFETCH_SIZE; i++) + if (p->blocks[i] != PREFETCH_SENTINEL) { + dm_bm_prefetch(bm, p->blocks[i]); + p->blocks[i] = PREFETCH_SENTINEL; + } + + mutex_unlock(&p->lock); +} + +/*----------------------------------------------------------------*/ + struct shadow_info { struct hlist_node hlist; dm_block_t where; @@ -37,6 +94,8 @@ struct dm_transaction_manager { spinlock_t lock; struct hlist_head buckets[DM_HASH_SIZE]; + + struct prefetch_set prefetches; }; /*----------------------------------------------------------------*/ @@ -117,6 +176,8 @@ static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm, for (i = 0; i < DM_HASH_SIZE; i++) INIT_HLIST_HEAD(tm->buckets + i); + prefetch_init(&tm->prefetches); + return tm; } @@ -268,8 +329,14 @@ int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b, struct dm_block_validator *v, struct dm_block **blk) { - if (tm->is_clone) - return dm_bm_read_try_lock(tm->real->bm, b, v, blk); + if (tm->is_clone) { + int r = dm_bm_read_try_lock(tm->real->bm, b, v, blk); + + if (r == -EWOULDBLOCK) + prefetch_add(&tm->real->prefetches, b); + + return r; + } return dm_bm_read_lock(tm->bm, b, v, blk); } @@ -317,6 +384,12 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm) return tm->bm; } +void dm_tm_issue_prefetches(struct dm_transaction_manager *tm) +{ + prefetch_issue(&tm->prefetches, tm->bm); +} +EXPORT_SYMBOL_GPL(dm_tm_issue_prefetches); + /*----------------------------------------------------------------*/ static int dm_tm_create_internal(struct dm_block_manager *bm, diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h index 2772ed2a781a..2e0d4d66fb1b 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.h +++ b/drivers/md/persistent-data/dm-transaction-manager.h @@ -109,6 +109,13 @@ int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm); /* + * If you're using a non-blocking clone the tm will build up a list of + * requested blocks that weren't in core. This call will request those + * blocks to be prefetched. + */ +void dm_tm_issue_prefetches(struct dm_transaction_manager *tm); + +/* * A little utility that ties the knot by producing a transaction manager * that has a space map managed by the transaction manager... * diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 932ed9be9ff3..b10aaeda2bb4 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2190,7 +2190,7 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, ret = smiapp_set_compose(subdev, fh, sel); break; default: - BUG(); + ret = -EINVAL; } mutex_unlock(&sensor->mutex); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 331eddac7222..3bd386c371f7 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1078,7 +1078,7 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, for (line = 0; line < lines; line++) { while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); - sg++; + sg = sg_next(sg); } if (lpi && line > 0 && !(line % lpi)) @@ -1101,14 +1101,14 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, *(rp++) = cpu_to_le32(0); /* bits 63-32 */ todo -= (sg_dma_len(sg)-offset); offset = 0; - sg++; + sg = sg_next(sg); while (todo > sg_dma_len(sg)) { *(rp++) = cpu_to_le32(RISC_WRITE| sg_dma_len(sg)); *(rp++) = cpu_to_le32(sg_dma_address(sg)); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ todo -= sg_dma_len(sg); - sg++; + sg = sg_next(sg); } *(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo); *(rp++) = cpu_to_le32(sg_dma_address(sg)); diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 172583d736fe..8cbe6b49f4c2 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -105,11 +105,8 @@ static irqreturn_t solo_isr(int irq, void *data) if (!status) return IRQ_NONE; - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } + /* Acknowledge all interrupts immediately */ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); if (status & SOLO_IRQ_PCI_ERR) solo_p2m_error_isr(solo_dev); @@ -132,9 +129,6 @@ static irqreturn_t solo_isr(int irq, void *data) if (status & SOLO_IRQ_G723) solo_g723_isr(solo_dev); - /* Clear all interrupts handled */ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); - return IRQ_HANDLED; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index f1f098e22f7e..d16bc67af732 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -259,8 +259,8 @@ again: case 32: if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { protocol = RC_TYPE_RC6_MCE; - scancode &= ~RC6_6A_MCE_TOGGLE_MASK; toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; } else { protocol = RC_BIT_RC6_6A_32; toggle = 0; diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index ccc00099b261..1c0dbf428a3a 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -632,7 +632,7 @@ static void s2255_fillbuff(struct s2255_vc *vc, break; case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: - buf->vb.v4l2_buf.length = jpgsize; + vb2_set_plane_payload(&buf->vb, 0, jpgsize); memcpy(vbuf, tmpbuf, jpgsize); break; case V4L2_PIX_FMT_YUV422P: diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 6d91c27fd4c8..191383d8c94d 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -41,6 +41,14 @@ config TI_EMIF parameters and other settings during frequency, voltage and temperature changes +config OMAP_GPMC + bool + help + This driver is for the General Purpose Memory Controller (GPMC) + present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows + interfacing to a variety of asynchronous as well as synchronous + memory drives like NOR, NAND, OneNAND, SRAM. + config MVEBU_DEVBUS bool "Marvell EBU Device Bus Controller" default y @@ -61,16 +69,6 @@ config TEGRA20_MC analysis, especially for IOMMU/GART(Graphics Address Relocation Table) module. -config TEGRA30_MC - bool "Tegra30 Memory Controller(MC) driver" - default y - depends on ARCH_TEGRA_3x_SOC - help - This driver is for the Memory Controller(MC) module available - in Tegra30 SoCs, mainly for a address translation fault - analysis, especially for IOMMU/SMMU(System Memory Management - Unit) module. - config FSL_CORENET_CF tristate "Freescale CoreNet Error Reporting" depends on FSL_SOC_BOOKE @@ -85,4 +83,6 @@ config FSL_IFC bool depends on FSL_SOC +source "drivers/memory/tegra/Kconfig" + endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index c32d31981be3..6b6548124473 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -8,8 +8,10 @@ endif obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o obj-$(CONFIG_TI_AEMIF) += ti-aemif.o obj-$(CONFIG_TI_EMIF) += emif.o +obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o -obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o + +obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c new file mode 100644 index 000000000000..ffc5e60c0664 --- /dev/null +++ b/drivers/memory/omap-gpmc.c @@ -0,0 +1,2092 @@ +/* + * GPMC support functions + * + * Copyright (C) 2005-2006 Nokia Corporation + * + * Author: Juha Yrjola + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#undef DEBUG + +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_mtd.h> +#include <linux/of_device.h> +#include <linux/omap-gpmc.h> +#include <linux/mtd/nand.h> +#include <linux/pm_runtime.h> + +#include <linux/platform_data/mtd-nand-omap2.h> +#include <linux/platform_data/mtd-onenand-omap2.h> + +#include <asm/mach-types.h> + +#define DEVICE_NAME "omap-gpmc" + +/* GPMC register offsets */ +#define GPMC_REVISION 0x00 +#define GPMC_SYSCONFIG 0x10 +#define GPMC_SYSSTATUS 0x14 +#define GPMC_IRQSTATUS 0x18 +#define GPMC_IRQENABLE 0x1c +#define GPMC_TIMEOUT_CONTROL 0x40 +#define GPMC_ERR_ADDRESS 0x44 +#define GPMC_ERR_TYPE 0x48 +#define GPMC_CONFIG 0x50 +#define GPMC_STATUS 0x54 +#define GPMC_PREFETCH_CONFIG1 0x1e0 +#define GPMC_PREFETCH_CONFIG2 0x1e4 +#define GPMC_PREFETCH_CONTROL 0x1ec +#define GPMC_PREFETCH_STATUS 0x1f0 +#define GPMC_ECC_CONFIG 0x1f4 +#define GPMC_ECC_CONTROL 0x1f8 +#define GPMC_ECC_SIZE_CONFIG 0x1fc +#define GPMC_ECC1_RESULT 0x200 +#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_4 0x300 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_5 0x304 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_6 0x308 /* not available on OMAP2 */ + +/* GPMC ECC control settings */ +#define GPMC_ECC_CTRL_ECCCLEAR 0x100 +#define GPMC_ECC_CTRL_ECCDISABLE 0x000 +#define GPMC_ECC_CTRL_ECCREG1 0x001 +#define GPMC_ECC_CTRL_ECCREG2 0x002 +#define GPMC_ECC_CTRL_ECCREG3 0x003 +#define GPMC_ECC_CTRL_ECCREG4 0x004 +#define GPMC_ECC_CTRL_ECCREG5 0x005 +#define GPMC_ECC_CTRL_ECCREG6 0x006 +#define GPMC_ECC_CTRL_ECCREG7 0x007 +#define GPMC_ECC_CTRL_ECCREG8 0x008 +#define GPMC_ECC_CTRL_ECCREG9 0x009 + +#define GPMC_CONFIG_LIMITEDADDRESS BIT(1) + +#define GPMC_CONFIG2_CSEXTRADELAY BIT(7) +#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7) +#define GPMC_CONFIG4_OEEXTRADELAY BIT(7) +#define GPMC_CONFIG4_WEEXTRADELAY BIT(23) +#define GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN BIT(6) +#define GPMC_CONFIG6_CYCLE2CYCLESAMECSEN BIT(7) + +#define GPMC_CS0_OFFSET 0x60 +#define GPMC_CS_SIZE 0x30 +#define GPMC_BCH_SIZE 0x10 + +#define GPMC_MEM_END 0x3FFFFFFF + +#define GPMC_CHUNK_SHIFT 24 /* 16 MB */ +#define GPMC_SECTION_SHIFT 28 /* 128 MB */ + +#define CS_NUM_SHIFT 24 +#define ENABLE_PREFETCH (0x1 << 7) +#define DMA_MPU_MODE 2 + +#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf) +#define GPMC_REVISION_MINOR(l) (l & 0xf) + +#define GPMC_HAS_WR_ACCESS 0x1 +#define GPMC_HAS_WR_DATA_MUX_BUS 0x2 +#define GPMC_HAS_MUX_AAD 0x4 + +#define GPMC_NR_WAITPINS 4 + +#define GPMC_CS_CONFIG1 0x00 +#define GPMC_CS_CONFIG2 0x04 +#define GPMC_CS_CONFIG3 0x08 +#define GPMC_CS_CONFIG4 0x0c +#define GPMC_CS_CONFIG5 0x10 +#define GPMC_CS_CONFIG6 0x14 +#define GPMC_CS_CONFIG7 0x18 +#define GPMC_CS_NAND_COMMAND 0x1c +#define GPMC_CS_NAND_ADDRESS 0x20 +#define GPMC_CS_NAND_DATA 0x24 + +/* Control Commands */ +#define GPMC_CONFIG_RDY_BSY 0x00000001 +#define GPMC_CONFIG_DEV_SIZE 0x00000002 +#define GPMC_CONFIG_DEV_TYPE 0x00000003 +#define GPMC_SET_IRQ_STATUS 0x00000004 + +#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31) +#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30) +#define GPMC_CONFIG1_READTYPE_ASYNC (0 << 29) +#define GPMC_CONFIG1_READTYPE_SYNC (1 << 29) +#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP (1 << 28) +#define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27) +#define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27) +#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) ((val & 3) << 25) +#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) +#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) +#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) +#define GPMC_CONFIG1_WAIT_MON_IIME(val) ((val & 3) << 18) +#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) +#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) +#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) +#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10) +#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) +#define GPMC_CONFIG1_MUXTYPE(val) ((val & 3) << 8) +#define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4) +#define GPMC_CONFIG1_FCLK_DIV(val) (val & 3) +#define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1)) +#define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2)) +#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3)) +#define GPMC_CONFIG7_CSVALID (1 << 6) + +#define GPMC_DEVICETYPE_NOR 0 +#define GPMC_DEVICETYPE_NAND 2 +#define GPMC_CONFIG_WRITEPROTECT 0x00000010 +#define WR_RD_PIN_MONITORING 0x00600000 + +#define GPMC_ENABLE_IRQ 0x0000000d + +/* ECC commands */ +#define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */ +#define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */ +#define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */ + +/* XXX: Only NAND irq has been considered,currently these are the only ones used + */ +#define GPMC_NR_IRQ 2 + +struct gpmc_cs_data { + const char *name; + +#define GPMC_CS_RESERVED (1 << 0) + u32 flags; + + struct resource mem; +}; + +struct gpmc_client_irq { + unsigned irq; + u32 bitmask; +}; + +/* Structure to save gpmc cs context */ +struct gpmc_cs_config { + u32 config1; + u32 config2; + u32 config3; + u32 config4; + u32 config5; + u32 config6; + u32 config7; + int is_valid; +}; + +/* + * Structure to save/restore gpmc context + * to support core off on OMAP3 + */ +struct omap3_gpmc_regs { + u32 sysconfig; + u32 irqenable; + u32 timeout_ctrl; + u32 config; + u32 prefetch_config1; + u32 prefetch_config2; + u32 prefetch_control; + struct gpmc_cs_config cs_context[GPMC_CS_NUM]; +}; + +static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ]; +static struct irq_chip gpmc_irq_chip; +static int gpmc_irq_start; + +static struct resource gpmc_mem_root; +static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM]; +static DEFINE_SPINLOCK(gpmc_mem_lock); +/* Define chip-selects as reserved by default until probe completes */ +static unsigned int gpmc_cs_num = GPMC_CS_NUM; +static unsigned int gpmc_nr_waitpins; +static struct device *gpmc_dev; +static int gpmc_irq; +static resource_size_t phys_base, mem_size; +static unsigned gpmc_capability; +static void __iomem *gpmc_base; + +static struct clk *gpmc_l3_clk; + +static irqreturn_t gpmc_handle_irq(int irq, void *dev); + +static void gpmc_write_reg(int idx, u32 val) +{ + writel_relaxed(val, gpmc_base + idx); +} + +static u32 gpmc_read_reg(int idx) +{ + return readl_relaxed(gpmc_base + idx); +} + +void gpmc_cs_write_reg(int cs, int idx, u32 val) +{ + void __iomem *reg_addr; + + reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; + writel_relaxed(val, reg_addr); +} + +static u32 gpmc_cs_read_reg(int cs, int idx) +{ + void __iomem *reg_addr; + + reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; + return readl_relaxed(reg_addr); +} + +/* TODO: Add support for gpmc_fck to clock framework and use it */ +static unsigned long gpmc_get_fclk_period(void) +{ + unsigned long rate = clk_get_rate(gpmc_l3_clk); + + rate /= 1000; + rate = 1000000000 / rate; /* In picoseconds */ + + return rate; +} + +static unsigned int gpmc_ns_to_ticks(unsigned int time_ns) +{ + unsigned long tick_ps; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = gpmc_get_fclk_period(); + + return (time_ns * 1000 + tick_ps - 1) / tick_ps; +} + +static unsigned int gpmc_ps_to_ticks(unsigned int time_ps) +{ + unsigned long tick_ps; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = gpmc_get_fclk_period(); + + return (time_ps + tick_ps - 1) / tick_ps; +} + +unsigned int gpmc_ticks_to_ns(unsigned int ticks) +{ + return ticks * gpmc_get_fclk_period() / 1000; +} + +static unsigned int gpmc_ticks_to_ps(unsigned int ticks) +{ + return ticks * gpmc_get_fclk_period(); +} + +static unsigned int gpmc_round_ps_to_ticks(unsigned int time_ps) +{ + unsigned long ticks = gpmc_ps_to_ticks(time_ps); + + return ticks * gpmc_get_fclk_period(); +} + +static inline void gpmc_cs_modify_reg(int cs, int reg, u32 mask, bool value) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, reg); + if (value) + l |= mask; + else + l &= ~mask; + gpmc_cs_write_reg(cs, reg, l); +} + +static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p) +{ + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG1, + GPMC_CONFIG1_TIME_PARA_GRAN, + p->time_para_granularity); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG2, + GPMC_CONFIG2_CSEXTRADELAY, p->cs_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG3, + GPMC_CONFIG3_ADVEXTRADELAY, p->adv_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4, + GPMC_CONFIG4_OEEXTRADELAY, p->oe_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4, + GPMC_CONFIG4_OEEXTRADELAY, p->we_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6, + GPMC_CONFIG6_CYCLE2CYCLESAMECSEN, + p->cycle2cyclesamecsen); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6, + GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN, + p->cycle2cyclediffcsen); +} + +#ifdef DEBUG +static int get_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, + bool raw, bool noval, int shift, + const char *name) +{ + u32 l; + int nr_bits, max_value, mask; + + l = gpmc_cs_read_reg(cs, reg); + nr_bits = end_bit - st_bit + 1; + max_value = (1 << nr_bits) - 1; + mask = max_value << st_bit; + l = (l & mask) >> st_bit; + if (shift) + l = (shift << l); + if (noval && (l == 0)) + return 0; + if (!raw) { + unsigned int time_ns_min, time_ns, time_ns_max; + + time_ns_min = gpmc_ticks_to_ns(l ? l - 1 : 0); + time_ns = gpmc_ticks_to_ns(l); + time_ns_max = gpmc_ticks_to_ns(l + 1 > max_value ? + max_value : l + 1); + pr_info("gpmc,%s = <%u> (%u - %u ns, %i ticks)\n", + name, time_ns, time_ns_min, time_ns_max, l); + } else { + pr_info("gpmc,%s = <%u>\n", name, l); + } + + return l; +} + +#define GPMC_PRINT_CONFIG(cs, config) \ + pr_info("cs%i %s: 0x%08x\n", cs, #config, \ + gpmc_cs_read_reg(cs, config)) +#define GPMC_GET_RAW(reg, st, end, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 0, 0, field) +#define GPMC_GET_RAW_BOOL(reg, st, end, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, 0, field) +#define GPMC_GET_RAW_SHIFT(reg, st, end, shift, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, (shift), field) +#define GPMC_GET_TICKS(reg, st, end, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 0, 0, 0, field) + +static void gpmc_show_regs(int cs, const char *desc) +{ + pr_info("gpmc cs%i %s:\n", cs, desc); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG1); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG2); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG3); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG4); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG5); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG6); +} + +/* + * Note that gpmc,wait-pin handing wrongly assumes bit 8 is available, + * see commit c9fb809. + */ +static void gpmc_cs_show_timings(int cs, const char *desc) +{ + gpmc_show_regs(cs, desc); + + pr_info("gpmc cs%i access configuration:\n", cs); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity"); + GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data"); + GPMC_GET_RAW(GPMC_CS_CONFIG1, 12, 13, "device-width"); + GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read"); + GPMC_GET_RAW_SHIFT(GPMC_CS_CONFIG1, 23, 24, 4, "burst-length"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 27, 27, "sync-write"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 28, 28, "burst-write"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 29, 29, "gpmc,sync-read"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 30, 30, "burst-read"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 31, 31, "burst-wrap"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG2, 7, 7, "cs-extra-delay"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG3, 7, 7, "adv-extra-delay"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 23, 23, "we-extra-delay"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 7, 7, "oe-extra-delay"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 7, 7, "cycle2cycle-samecsen"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 6, 6, "cycle2cycle-diffcsen"); + + pr_info("gpmc cs%i timings configuration:\n", cs); + GPMC_GET_TICKS(GPMC_CS_CONFIG2, 0, 3, "cs-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG2, 8, 12, "cs-rd-off-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG2, 16, 20, "cs-wr-off-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 0, 3, "adv-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 8, 12, "adv-rd-off-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 16, 20, "adv-wr-off-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 0, 3, "oe-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 8, 12, "oe-off-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 16, 19, "we-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 24, 28, "we-off-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 0, 4, "rd-cycle-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 8, 12, "wr-cycle-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 16, 20, "access-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 24, 27, "page-burst-access-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG1, 18, 19, "wait-monitoring-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG1, 25, 26, "clk-activation-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 24, 28, "wr-access-ns"); +} +#else +static inline void gpmc_cs_show_timings(int cs, const char *desc) +{ +} +#endif + +static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, + int time, const char *name) +{ + u32 l; + int ticks, mask, nr_bits; + + if (time == 0) + ticks = 0; + else + ticks = gpmc_ns_to_ticks(time); + nr_bits = end_bit - st_bit + 1; + mask = (1 << nr_bits) - 1; + + if (ticks > mask) { + pr_err("%s: GPMC error! CS%d: %s: %d ns, %d ticks > %d\n", + __func__, cs, name, time, ticks, mask); + + return -1; + } + + l = gpmc_cs_read_reg(cs, reg); +#ifdef DEBUG + printk(KERN_INFO + "GPMC CS%d: %-10s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n", + cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000, + (l >> st_bit) & mask, time); +#endif + l &= ~(mask << st_bit); + l |= ticks << st_bit; + gpmc_cs_write_reg(cs, reg, l); + + return 0; +} + +#define GPMC_SET_ONE(reg, st, end, field) \ + if (set_gpmc_timing_reg(cs, (reg), (st), (end), \ + t->field, #field) < 0) \ + return -1 + +int gpmc_calc_divider(unsigned int sync_clk) +{ + int div; + u32 l; + + l = sync_clk + (gpmc_get_fclk_period() - 1); + div = l / gpmc_get_fclk_period(); + if (div > 4) + return -1; + if (div <= 0) + div = 1; + + return div; +} + +int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) +{ + int div; + u32 l; + + gpmc_cs_show_timings(cs, "before gpmc_cs_set_timings"); + div = gpmc_calc_divider(t->sync_clk); + if (div < 0) + return div; + + GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on); + GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off); + GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off); + + GPMC_SET_ONE(GPMC_CS_CONFIG3, 0, 3, adv_on); + GPMC_SET_ONE(GPMC_CS_CONFIG3, 8, 12, adv_rd_off); + GPMC_SET_ONE(GPMC_CS_CONFIG3, 16, 20, adv_wr_off); + + GPMC_SET_ONE(GPMC_CS_CONFIG4, 0, 3, oe_on); + GPMC_SET_ONE(GPMC_CS_CONFIG4, 8, 12, oe_off); + GPMC_SET_ONE(GPMC_CS_CONFIG4, 16, 19, we_on); + GPMC_SET_ONE(GPMC_CS_CONFIG4, 24, 28, we_off); + + GPMC_SET_ONE(GPMC_CS_CONFIG5, 0, 4, rd_cycle); + GPMC_SET_ONE(GPMC_CS_CONFIG5, 8, 12, wr_cycle); + GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access); + + GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access); + + GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround); + GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay); + + GPMC_SET_ONE(GPMC_CS_CONFIG1, 18, 19, wait_monitoring); + GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation); + + if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) + GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus); + if (gpmc_capability & GPMC_HAS_WR_ACCESS) + GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access); + + /* caller is expected to have initialized CONFIG1 to cover + * at least sync vs async + */ + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + if (l & (GPMC_CONFIG1_READTYPE_SYNC | GPMC_CONFIG1_WRITETYPE_SYNC)) { +#ifdef DEBUG + printk(KERN_INFO "GPMC CS%d CLK period is %lu ns (div %d)\n", + cs, (div * gpmc_get_fclk_period()) / 1000, div); +#endif + l &= ~0x03; + l |= (div - 1); + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l); + } + + gpmc_cs_bool_timings(cs, &t->bool_timings); + gpmc_cs_show_timings(cs, "after gpmc_cs_set_timings"); + + return 0; +} + +static int gpmc_cs_set_memconf(int cs, u32 base, u32 size) +{ + u32 l; + u32 mask; + + /* + * Ensure that base address is aligned on a + * boundary equal to or greater than size. + */ + if (base & (size - 1)) + return -EINVAL; + + mask = (1 << GPMC_SECTION_SHIFT) - size; + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l &= ~0x3f; + l = (base >> GPMC_CHUNK_SHIFT) & 0x3f; + l &= ~(0x0f << 8); + l |= ((mask >> GPMC_CHUNK_SHIFT) & 0x0f) << 8; + l |= GPMC_CONFIG7_CSVALID; + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); + + return 0; +} + +static void gpmc_cs_enable_mem(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l |= GPMC_CONFIG7_CSVALID; + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); +} + +static void gpmc_cs_disable_mem(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l &= ~GPMC_CONFIG7_CSVALID; + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); +} + +static void gpmc_cs_get_memconf(int cs, u32 *base, u32 *size) +{ + u32 l; + u32 mask; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + *base = (l & 0x3f) << GPMC_CHUNK_SHIFT; + mask = (l >> 8) & 0x0f; + *size = (1 << GPMC_SECTION_SHIFT) - (mask << GPMC_CHUNK_SHIFT); +} + +static int gpmc_cs_mem_enabled(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + return l & GPMC_CONFIG7_CSVALID; +} + +static void gpmc_cs_set_reserved(int cs, int reserved) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + gpmc->flags |= GPMC_CS_RESERVED; +} + +static bool gpmc_cs_reserved(int cs) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + return gpmc->flags & GPMC_CS_RESERVED; +} + +static void gpmc_cs_set_name(int cs, const char *name) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + gpmc->name = name; +} + +const char *gpmc_cs_get_name(int cs) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + return gpmc->name; +} + +static unsigned long gpmc_mem_align(unsigned long size) +{ + int order; + + size = (size - 1) >> (GPMC_CHUNK_SHIFT - 1); + order = GPMC_CHUNK_SHIFT - 1; + do { + size >>= 1; + order++; + } while (size); + size = 1 << order; + return size; +} + +static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + struct resource *res = &gpmc->mem; + int r; + + size = gpmc_mem_align(size); + spin_lock(&gpmc_mem_lock); + res->start = base; + res->end = base + size - 1; + r = request_resource(&gpmc_mem_root, res); + spin_unlock(&gpmc_mem_lock); + + return r; +} + +static int gpmc_cs_delete_mem(int cs) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + struct resource *res = &gpmc->mem; + int r; + + spin_lock(&gpmc_mem_lock); + r = release_resource(res); + res->start = 0; + res->end = 0; + spin_unlock(&gpmc_mem_lock); + + return r; +} + +/** + * gpmc_cs_remap - remaps a chip-select physical base address + * @cs: chip-select to remap + * @base: physical base address to re-map chip-select to + * + * Re-maps a chip-select to a new physical base address specified by + * "base". Returns 0 on success and appropriate negative error code + * on failure. + */ +static int gpmc_cs_remap(int cs, u32 base) +{ + int ret; + u32 old_base, size; + + if (cs > gpmc_cs_num) { + pr_err("%s: requested chip-select is disabled\n", __func__); + return -ENODEV; + } + + /* + * Make sure we ignore any device offsets from the GPMC partition + * allocated for the chip select and that the new base confirms + * to the GPMC 16MB minimum granularity. + */ + base &= ~(SZ_16M - 1); + + gpmc_cs_get_memconf(cs, &old_base, &size); + if (base == old_base) + return 0; + + ret = gpmc_cs_delete_mem(cs); + if (ret < 0) + return ret; + + ret = gpmc_cs_insert_mem(cs, base, size); + if (ret < 0) + return ret; + + ret = gpmc_cs_set_memconf(cs, base, size); + + return ret; +} + +int gpmc_cs_request(int cs, unsigned long size, unsigned long *base) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + struct resource *res = &gpmc->mem; + int r = -1; + + if (cs > gpmc_cs_num) { + pr_err("%s: requested chip-select is disabled\n", __func__); + return -ENODEV; + } + size = gpmc_mem_align(size); + if (size > (1 << GPMC_SECTION_SHIFT)) + return -ENOMEM; + + spin_lock(&gpmc_mem_lock); + if (gpmc_cs_reserved(cs)) { + r = -EBUSY; + goto out; + } + if (gpmc_cs_mem_enabled(cs)) + r = adjust_resource(res, res->start & ~(size - 1), size); + if (r < 0) + r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0, + size, NULL, NULL); + if (r < 0) + goto out; + + /* Disable CS while changing base address and size mask */ + gpmc_cs_disable_mem(cs); + + r = gpmc_cs_set_memconf(cs, res->start, resource_size(res)); + if (r < 0) { + release_resource(res); + goto out; + } + + /* Enable CS */ + gpmc_cs_enable_mem(cs); + *base = res->start; + gpmc_cs_set_reserved(cs, 1); +out: + spin_unlock(&gpmc_mem_lock); + return r; +} +EXPORT_SYMBOL(gpmc_cs_request); + +void gpmc_cs_free(int cs) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + struct resource *res = &gpmc->mem; + + spin_lock(&gpmc_mem_lock); + if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) { + printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs); + BUG(); + spin_unlock(&gpmc_mem_lock); + return; + } + gpmc_cs_disable_mem(cs); + if (res->flags) + release_resource(res); + gpmc_cs_set_reserved(cs, 0); + spin_unlock(&gpmc_mem_lock); +} +EXPORT_SYMBOL(gpmc_cs_free); + +/** + * gpmc_configure - write request to configure gpmc + * @cmd: command type + * @wval: value to write + * @return status of the operation + */ +int gpmc_configure(int cmd, int wval) +{ + u32 regval; + + switch (cmd) { + case GPMC_ENABLE_IRQ: + gpmc_write_reg(GPMC_IRQENABLE, wval); + break; + + case GPMC_SET_IRQ_STATUS: + gpmc_write_reg(GPMC_IRQSTATUS, wval); + break; + + case GPMC_CONFIG_WP: + regval = gpmc_read_reg(GPMC_CONFIG); + if (wval) + regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */ + else + regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */ + gpmc_write_reg(GPMC_CONFIG, regval); + break; + + default: + pr_err("%s: command not supported\n", __func__); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(gpmc_configure); + +void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) +{ + int i; + + reg->gpmc_status = gpmc_base + GPMC_STATUS; + reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + + GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; + reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET + + GPMC_CS_NAND_ADDRESS + GPMC_CS_SIZE * cs; + reg->gpmc_nand_data = gpmc_base + GPMC_CS0_OFFSET + + GPMC_CS_NAND_DATA + GPMC_CS_SIZE * cs; + reg->gpmc_prefetch_config1 = gpmc_base + GPMC_PREFETCH_CONFIG1; + reg->gpmc_prefetch_config2 = gpmc_base + GPMC_PREFETCH_CONFIG2; + reg->gpmc_prefetch_control = gpmc_base + GPMC_PREFETCH_CONTROL; + reg->gpmc_prefetch_status = gpmc_base + GPMC_PREFETCH_STATUS; + reg->gpmc_ecc_config = gpmc_base + GPMC_ECC_CONFIG; + reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL; + reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG; + reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT; + + for (i = 0; i < GPMC_BCH_NUM_REMAINDER; i++) { + reg->gpmc_bch_result0[i] = gpmc_base + GPMC_ECC_BCH_RESULT_0 + + GPMC_BCH_SIZE * i; + reg->gpmc_bch_result1[i] = gpmc_base + GPMC_ECC_BCH_RESULT_1 + + GPMC_BCH_SIZE * i; + reg->gpmc_bch_result2[i] = gpmc_base + GPMC_ECC_BCH_RESULT_2 + + GPMC_BCH_SIZE * i; + reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 + + GPMC_BCH_SIZE * i; + reg->gpmc_bch_result4[i] = gpmc_base + GPMC_ECC_BCH_RESULT_4 + + i * GPMC_BCH_SIZE; + reg->gpmc_bch_result5[i] = gpmc_base + GPMC_ECC_BCH_RESULT_5 + + i * GPMC_BCH_SIZE; + reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 + + i * GPMC_BCH_SIZE; + } +} + +int gpmc_get_client_irq(unsigned irq_config) +{ + int i; + + if (hweight32(irq_config) > 1) + return 0; + + for (i = 0; i < GPMC_NR_IRQ; i++) + if (gpmc_client_irq[i].bitmask & irq_config) + return gpmc_client_irq[i].irq; + + return 0; +} + +static int gpmc_irq_endis(unsigned irq, bool endis) +{ + int i; + u32 regval; + + for (i = 0; i < GPMC_NR_IRQ; i++) + if (irq == gpmc_client_irq[i].irq) { + regval = gpmc_read_reg(GPMC_IRQENABLE); + if (endis) + regval |= gpmc_client_irq[i].bitmask; + else + regval &= ~gpmc_client_irq[i].bitmask; + gpmc_write_reg(GPMC_IRQENABLE, regval); + break; + } + + return 0; +} + +static void gpmc_irq_disable(struct irq_data *p) +{ + gpmc_irq_endis(p->irq, false); +} + +static void gpmc_irq_enable(struct irq_data *p) +{ + gpmc_irq_endis(p->irq, true); +} + +static void gpmc_irq_noop(struct irq_data *data) { } + +static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; } + +static int gpmc_setup_irq(void) +{ + int i; + u32 regval; + + if (!gpmc_irq) + return -EINVAL; + + gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0); + if (gpmc_irq_start < 0) { + pr_err("irq_alloc_descs failed\n"); + return gpmc_irq_start; + } + + gpmc_irq_chip.name = "gpmc"; + gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret; + gpmc_irq_chip.irq_enable = gpmc_irq_enable; + gpmc_irq_chip.irq_disable = gpmc_irq_disable; + gpmc_irq_chip.irq_shutdown = gpmc_irq_noop; + gpmc_irq_chip.irq_ack = gpmc_irq_noop; + gpmc_irq_chip.irq_mask = gpmc_irq_noop; + gpmc_irq_chip.irq_unmask = gpmc_irq_noop; + + gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE; + gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT; + + for (i = 0; i < GPMC_NR_IRQ; i++) { + gpmc_client_irq[i].irq = gpmc_irq_start + i; + irq_set_chip_and_handler(gpmc_client_irq[i].irq, + &gpmc_irq_chip, handle_simple_irq); + set_irq_flags(gpmc_client_irq[i].irq, + IRQF_VALID | IRQF_NOAUTOEN); + } + + /* Disable interrupts */ + gpmc_write_reg(GPMC_IRQENABLE, 0); + + /* clear interrupts */ + regval = gpmc_read_reg(GPMC_IRQSTATUS); + gpmc_write_reg(GPMC_IRQSTATUS, regval); + + return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL); +} + +static int gpmc_free_irq(void) +{ + int i; + + if (gpmc_irq) + free_irq(gpmc_irq, NULL); + + for (i = 0; i < GPMC_NR_IRQ; i++) { + irq_set_handler(gpmc_client_irq[i].irq, NULL); + irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip); + irq_modify_status(gpmc_client_irq[i].irq, 0, 0); + } + + irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ); + + return 0; +} + +static void gpmc_mem_exit(void) +{ + int cs; + + for (cs = 0; cs < gpmc_cs_num; cs++) { + if (!gpmc_cs_mem_enabled(cs)) + continue; + gpmc_cs_delete_mem(cs); + } + +} + +static void gpmc_mem_init(void) +{ + int cs; + + /* + * The first 1MB of GPMC address space is typically mapped to + * the internal ROM. Never allocate the first page, to + * facilitate bug detection; even if we didn't boot from ROM. + */ + gpmc_mem_root.start = SZ_1M; + gpmc_mem_root.end = GPMC_MEM_END; + + /* Reserve all regions that has been set up by bootloader */ + for (cs = 0; cs < gpmc_cs_num; cs++) { + u32 base, size; + + if (!gpmc_cs_mem_enabled(cs)) + continue; + gpmc_cs_get_memconf(cs, &base, &size); + if (gpmc_cs_insert_mem(cs, base, size)) { + pr_warn("%s: disabling cs %d mapped at 0x%x-0x%x\n", + __func__, cs, base, base + size); + gpmc_cs_disable_mem(cs); + } + } +} + +static u32 gpmc_round_ps_to_sync_clk(u32 time_ps, u32 sync_clk) +{ + u32 temp; + int div; + + div = gpmc_calc_divider(sync_clk); + temp = gpmc_ps_to_ticks(time_ps); + temp = (temp + div - 1) / div; + return gpmc_ticks_to_ps(temp * div); +} + +/* XXX: can the cycles be avoided ? */ +static int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t, + bool mux) +{ + u32 temp; + + /* adv_rd_off */ + temp = dev_t->t_avdp_r; + /* XXX: mux check required ? */ + if (mux) { + /* XXX: t_avdp not to be required for sync, only added for tusb + * this indirectly necessitates requirement of t_avdp_r and + * t_avdp_w instead of having a single t_avdp + */ + temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_avdh); + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + } + gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp); + + /* oe_on */ + temp = dev_t->t_oeasu; /* XXX: remove this ? */ + if (mux) { + temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_ach); + temp = max_t(u32, temp, gpmc_t->adv_rd_off + + gpmc_ticks_to_ps(dev_t->cyc_aavdh_oe)); + } + gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp); + + /* access */ + /* XXX: any scope for improvement ?, by combining oe_on + * and clk_activation, need to check whether + * access = clk_activation + round to sync clk ? + */ + temp = max_t(u32, dev_t->t_iaa, dev_t->cyc_iaa * gpmc_t->sync_clk); + temp += gpmc_t->clk_activation; + if (dev_t->cyc_oe) + temp = max_t(u32, temp, gpmc_t->oe_on + + gpmc_ticks_to_ps(dev_t->cyc_oe)); + gpmc_t->access = gpmc_round_ps_to_ticks(temp); + + gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1); + gpmc_t->cs_rd_off = gpmc_t->oe_off; + + /* rd_cycle */ + temp = max_t(u32, dev_t->t_cez_r, dev_t->t_oez); + temp = gpmc_round_ps_to_sync_clk(temp, gpmc_t->sync_clk) + + gpmc_t->access; + /* XXX: barter t_ce_rdyz with t_cez_r ? */ + if (dev_t->t_ce_rdyz) + temp = max_t(u32, temp, gpmc_t->cs_rd_off + dev_t->t_ce_rdyz); + gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t, + bool mux) +{ + u32 temp; + + /* adv_wr_off */ + temp = dev_t->t_avdp_w; + if (mux) { + temp = max_t(u32, temp, + gpmc_t->clk_activation + dev_t->t_avdh); + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + } + gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp); + + /* wr_data_mux_bus */ + temp = max_t(u32, dev_t->t_weasu, + gpmc_t->clk_activation + dev_t->t_rdyo); + /* XXX: shouldn't mux be kept as a whole for wr_data_mux_bus ?, + * and in that case remember to handle we_on properly + */ + if (mux) { + temp = max_t(u32, temp, + gpmc_t->adv_wr_off + dev_t->t_aavdh); + temp = max_t(u32, temp, gpmc_t->adv_wr_off + + gpmc_ticks_to_ps(dev_t->cyc_aavdh_we)); + } + gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp); + + /* we_on */ + if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) + gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu); + else + gpmc_t->we_on = gpmc_t->wr_data_mux_bus; + + /* wr_access */ + /* XXX: gpmc_capability check reqd ? , even if not, will not harm */ + gpmc_t->wr_access = gpmc_t->access; + + /* we_off */ + temp = gpmc_t->we_on + dev_t->t_wpl; + temp = max_t(u32, temp, + gpmc_t->wr_access + gpmc_ticks_to_ps(1)); + temp = max_t(u32, temp, + gpmc_t->we_on + gpmc_ticks_to_ps(dev_t->cyc_wpl)); + gpmc_t->we_off = gpmc_round_ps_to_ticks(temp); + + gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off + + dev_t->t_wph); + + /* wr_cycle */ + temp = gpmc_round_ps_to_sync_clk(dev_t->t_cez_w, gpmc_t->sync_clk); + temp += gpmc_t->wr_access; + /* XXX: barter t_ce_rdyz with t_cez_w ? */ + if (dev_t->t_ce_rdyz) + temp = max_t(u32, temp, + gpmc_t->cs_wr_off + dev_t->t_ce_rdyz); + gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t, + bool mux) +{ + u32 temp; + + /* adv_rd_off */ + temp = dev_t->t_avdp_r; + if (mux) + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp); + + /* oe_on */ + temp = dev_t->t_oeasu; + if (mux) + temp = max_t(u32, temp, + gpmc_t->adv_rd_off + dev_t->t_aavdh); + gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp); + + /* access */ + temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */ + gpmc_t->oe_on + dev_t->t_oe); + temp = max_t(u32, temp, + gpmc_t->cs_on + dev_t->t_ce); + temp = max_t(u32, temp, + gpmc_t->adv_on + dev_t->t_aa); + gpmc_t->access = gpmc_round_ps_to_ticks(temp); + + gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1); + gpmc_t->cs_rd_off = gpmc_t->oe_off; + + /* rd_cycle */ + temp = max_t(u32, dev_t->t_rd_cycle, + gpmc_t->cs_rd_off + dev_t->t_cez_r); + temp = max_t(u32, temp, gpmc_t->oe_off + dev_t->t_oez); + gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_async_write_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t, + bool mux) +{ + u32 temp; + + /* adv_wr_off */ + temp = dev_t->t_avdp_w; + if (mux) + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp); + + /* wr_data_mux_bus */ + temp = dev_t->t_weasu; + if (mux) { + temp = max_t(u32, temp, gpmc_t->adv_wr_off + dev_t->t_aavdh); + temp = max_t(u32, temp, gpmc_t->adv_wr_off + + gpmc_ticks_to_ps(dev_t->cyc_aavdh_we)); + } + gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp); + + /* we_on */ + if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) + gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu); + else + gpmc_t->we_on = gpmc_t->wr_data_mux_bus; + + /* we_off */ + temp = gpmc_t->we_on + dev_t->t_wpl; + gpmc_t->we_off = gpmc_round_ps_to_ticks(temp); + + gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off + + dev_t->t_wph); + + /* wr_cycle */ + temp = max_t(u32, dev_t->t_wr_cycle, + gpmc_t->cs_wr_off + dev_t->t_cez_w); + gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_sync_common_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + u32 temp; + + gpmc_t->sync_clk = gpmc_calc_divider(dev_t->clk) * + gpmc_get_fclk_period(); + + gpmc_t->page_burst_access = gpmc_round_ps_to_sync_clk( + dev_t->t_bacc, + gpmc_t->sync_clk); + + temp = max_t(u32, dev_t->t_ces, dev_t->t_avds); + gpmc_t->clk_activation = gpmc_round_ps_to_ticks(temp); + + if (gpmc_calc_divider(gpmc_t->sync_clk) != 1) + return 0; + + if (dev_t->ce_xdelay) + gpmc_t->bool_timings.cs_extra_delay = true; + if (dev_t->avd_xdelay) + gpmc_t->bool_timings.adv_extra_delay = true; + if (dev_t->oe_xdelay) + gpmc_t->bool_timings.oe_extra_delay = true; + if (dev_t->we_xdelay) + gpmc_t->bool_timings.we_extra_delay = true; + + return 0; +} + +static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t, + bool sync) +{ + u32 temp; + + /* cs_on */ + gpmc_t->cs_on = gpmc_round_ps_to_ticks(dev_t->t_ceasu); + + /* adv_on */ + temp = dev_t->t_avdasu; + if (dev_t->t_ce_avd) + temp = max_t(u32, temp, + gpmc_t->cs_on + dev_t->t_ce_avd); + gpmc_t->adv_on = gpmc_round_ps_to_ticks(temp); + + if (sync) + gpmc_calc_sync_common_timings(gpmc_t, dev_t); + + return 0; +} + +/* TODO: remove this function once all peripherals are confirmed to + * work with generic timing. Simultaneously gpmc_cs_set_timings() + * has to be modified to handle timings in ps instead of ns +*/ +static void gpmc_convert_ps_to_ns(struct gpmc_timings *t) +{ + t->cs_on /= 1000; + t->cs_rd_off /= 1000; + t->cs_wr_off /= 1000; + t->adv_on /= 1000; + t->adv_rd_off /= 1000; + t->adv_wr_off /= 1000; + t->we_on /= 1000; + t->we_off /= 1000; + t->oe_on /= 1000; + t->oe_off /= 1000; + t->page_burst_access /= 1000; + t->access /= 1000; + t->rd_cycle /= 1000; + t->wr_cycle /= 1000; + t->bus_turnaround /= 1000; + t->cycle2cycle_delay /= 1000; + t->wait_monitoring /= 1000; + t->clk_activation /= 1000; + t->wr_access /= 1000; + t->wr_data_mux_bus /= 1000; +} + +int gpmc_calc_timings(struct gpmc_timings *gpmc_t, + struct gpmc_settings *gpmc_s, + struct gpmc_device_timings *dev_t) +{ + bool mux = false, sync = false; + + if (gpmc_s) { + mux = gpmc_s->mux_add_data ? true : false; + sync = (gpmc_s->sync_read || gpmc_s->sync_write); + } + + memset(gpmc_t, 0, sizeof(*gpmc_t)); + + gpmc_calc_common_timings(gpmc_t, dev_t, sync); + + if (gpmc_s && gpmc_s->sync_read) + gpmc_calc_sync_read_timings(gpmc_t, dev_t, mux); + else + gpmc_calc_async_read_timings(gpmc_t, dev_t, mux); + + if (gpmc_s && gpmc_s->sync_write) + gpmc_calc_sync_write_timings(gpmc_t, dev_t, mux); + else + gpmc_calc_async_write_timings(gpmc_t, dev_t, mux); + + /* TODO: remove, see function definition */ + gpmc_convert_ps_to_ns(gpmc_t); + + return 0; +} + +/** + * gpmc_cs_program_settings - programs non-timing related settings + * @cs: GPMC chip-select to program + * @p: pointer to GPMC settings structure + * + * Programs non-timing related settings for a GPMC chip-select, such as + * bus-width, burst configuration, etc. Function should be called once + * for each chip-select that is being used and must be called before + * calling gpmc_cs_set_timings() as timing parameters in the CONFIG1 + * register will be initialised to zero by this function. Returns 0 on + * success and appropriate negative error code on failure. + */ +int gpmc_cs_program_settings(int cs, struct gpmc_settings *p) +{ + u32 config1; + + if ((!p->device_width) || (p->device_width > GPMC_DEVWIDTH_16BIT)) { + pr_err("%s: invalid width %d!", __func__, p->device_width); + return -EINVAL; + } + + /* Address-data multiplexing not supported for NAND devices */ + if (p->device_nand && p->mux_add_data) { + pr_err("%s: invalid configuration!\n", __func__); + return -EINVAL; + } + + if ((p->mux_add_data > GPMC_MUX_AD) || + ((p->mux_add_data == GPMC_MUX_AAD) && + !(gpmc_capability & GPMC_HAS_MUX_AAD))) { + pr_err("%s: invalid multiplex configuration!\n", __func__); + return -EINVAL; + } + + /* Page/burst mode supports lengths of 4, 8 and 16 bytes */ + if (p->burst_read || p->burst_write) { + switch (p->burst_len) { + case GPMC_BURST_4: + case GPMC_BURST_8: + case GPMC_BURST_16: + break; + default: + pr_err("%s: invalid page/burst-length (%d)\n", + __func__, p->burst_len); + return -EINVAL; + } + } + + if (p->wait_pin > gpmc_nr_waitpins) { + pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin); + return -EINVAL; + } + + config1 = GPMC_CONFIG1_DEVICESIZE((p->device_width - 1)); + + if (p->sync_read) + config1 |= GPMC_CONFIG1_READTYPE_SYNC; + if (p->sync_write) + config1 |= GPMC_CONFIG1_WRITETYPE_SYNC; + if (p->wait_on_read) + config1 |= GPMC_CONFIG1_WAIT_READ_MON; + if (p->wait_on_write) + config1 |= GPMC_CONFIG1_WAIT_WRITE_MON; + if (p->wait_on_read || p->wait_on_write) + config1 |= GPMC_CONFIG1_WAIT_PIN_SEL(p->wait_pin); + if (p->device_nand) + config1 |= GPMC_CONFIG1_DEVICETYPE(GPMC_DEVICETYPE_NAND); + if (p->mux_add_data) + config1 |= GPMC_CONFIG1_MUXTYPE(p->mux_add_data); + if (p->burst_read) + config1 |= GPMC_CONFIG1_READMULTIPLE_SUPP; + if (p->burst_write) + config1 |= GPMC_CONFIG1_WRITEMULTIPLE_SUPP; + if (p->burst_read || p->burst_write) { + config1 |= GPMC_CONFIG1_PAGE_LEN(p->burst_len >> 3); + config1 |= p->burst_wrap ? GPMC_CONFIG1_WRAPBURST_SUPP : 0; + } + + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, config1); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id gpmc_dt_ids[] = { + { .compatible = "ti,omap2420-gpmc" }, + { .compatible = "ti,omap2430-gpmc" }, + { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ + { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ + { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ + { } +}; +MODULE_DEVICE_TABLE(of, gpmc_dt_ids); + +/** + * gpmc_read_settings_dt - read gpmc settings from device-tree + * @np: pointer to device-tree node for a gpmc child device + * @p: pointer to gpmc settings structure + * + * Reads the GPMC settings for a GPMC child device from device-tree and + * stores them in the GPMC settings structure passed. The GPMC settings + * structure is initialised to zero by this function and so any + * previously stored settings will be cleared. + */ +void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p) +{ + memset(p, 0, sizeof(struct gpmc_settings)); + + p->sync_read = of_property_read_bool(np, "gpmc,sync-read"); + p->sync_write = of_property_read_bool(np, "gpmc,sync-write"); + of_property_read_u32(np, "gpmc,device-width", &p->device_width); + of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data); + + if (!of_property_read_u32(np, "gpmc,burst-length", &p->burst_len)) { + p->burst_wrap = of_property_read_bool(np, "gpmc,burst-wrap"); + p->burst_read = of_property_read_bool(np, "gpmc,burst-read"); + p->burst_write = of_property_read_bool(np, "gpmc,burst-write"); + if (!p->burst_read && !p->burst_write) + pr_warn("%s: page/burst-length set but not used!\n", + __func__); + } + + if (!of_property_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) { + p->wait_on_read = of_property_read_bool(np, + "gpmc,wait-on-read"); + p->wait_on_write = of_property_read_bool(np, + "gpmc,wait-on-write"); + if (!p->wait_on_read && !p->wait_on_write) + pr_debug("%s: rd/wr wait monitoring not enabled!\n", + __func__); + } +} + +static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, + struct gpmc_timings *gpmc_t) +{ + struct gpmc_bool_timings *p; + + if (!np || !gpmc_t) + return; + + memset(gpmc_t, 0, sizeof(*gpmc_t)); + + /* minimum clock period for syncronous mode */ + of_property_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk); + + /* chip select timtings */ + of_property_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on); + of_property_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off); + of_property_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off); + + /* ADV signal timings */ + of_property_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on); + of_property_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off); + of_property_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off); + + /* WE signal timings */ + of_property_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on); + of_property_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off); + + /* OE signal timings */ + of_property_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on); + of_property_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off); + + /* access and cycle timings */ + of_property_read_u32(np, "gpmc,page-burst-access-ns", + &gpmc_t->page_burst_access); + of_property_read_u32(np, "gpmc,access-ns", &gpmc_t->access); + of_property_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle); + of_property_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle); + of_property_read_u32(np, "gpmc,bus-turnaround-ns", + &gpmc_t->bus_turnaround); + of_property_read_u32(np, "gpmc,cycle2cycle-delay-ns", + &gpmc_t->cycle2cycle_delay); + of_property_read_u32(np, "gpmc,wait-monitoring-ns", + &gpmc_t->wait_monitoring); + of_property_read_u32(np, "gpmc,clk-activation-ns", + &gpmc_t->clk_activation); + + /* only applicable to OMAP3+ */ + of_property_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access); + of_property_read_u32(np, "gpmc,wr-data-mux-bus-ns", + &gpmc_t->wr_data_mux_bus); + + /* bool timing parameters */ + p = &gpmc_t->bool_timings; + + p->cycle2cyclediffcsen = + of_property_read_bool(np, "gpmc,cycle2cycle-diffcsen"); + p->cycle2cyclesamecsen = + of_property_read_bool(np, "gpmc,cycle2cycle-samecsen"); + p->we_extra_delay = of_property_read_bool(np, "gpmc,we-extra-delay"); + p->oe_extra_delay = of_property_read_bool(np, "gpmc,oe-extra-delay"); + p->adv_extra_delay = of_property_read_bool(np, "gpmc,adv-extra-delay"); + p->cs_extra_delay = of_property_read_bool(np, "gpmc,cs-extra-delay"); + p->time_para_granularity = + of_property_read_bool(np, "gpmc,time-para-granularity"); +} + +#if IS_ENABLED(CONFIG_MTD_NAND) + +static const char * const nand_xfer_types[] = { + [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", + [NAND_OMAP_POLLED] = "polled", + [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", + [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", +}; + +static int gpmc_probe_nand_child(struct platform_device *pdev, + struct device_node *child) +{ + u32 val; + const char *s; + struct gpmc_timings gpmc_t; + struct omap_nand_platform_data *gpmc_nand_data; + + if (of_property_read_u32(child, "reg", &val) < 0) { + dev_err(&pdev->dev, "%s has no 'reg' property\n", + child->full_name); + return -ENODEV; + } + + gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data), + GFP_KERNEL); + if (!gpmc_nand_data) + return -ENOMEM; + + gpmc_nand_data->cs = val; + gpmc_nand_data->of_node = child; + + /* Detect availability of ELM module */ + gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); + if (gpmc_nand_data->elm_of_node == NULL) + gpmc_nand_data->elm_of_node = + of_parse_phandle(child, "elm_id", 0); + + /* select ecc-scheme for NAND */ + if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { + pr_err("%s: ti,nand-ecc-opt not found\n", __func__); + return -ENODEV; + } + + if (!strcmp(s, "sw")) + gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW; + else if (!strcmp(s, "ham1") || + !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) + gpmc_nand_data->ecc_opt = + OMAP_ECC_HAM1_CODE_HW; + else if (!strcmp(s, "bch4")) + if (gpmc_nand_data->elm_of_node) + gpmc_nand_data->ecc_opt = + OMAP_ECC_BCH4_CODE_HW; + else + gpmc_nand_data->ecc_opt = + OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; + else if (!strcmp(s, "bch8")) + if (gpmc_nand_data->elm_of_node) + gpmc_nand_data->ecc_opt = + OMAP_ECC_BCH8_CODE_HW; + else + gpmc_nand_data->ecc_opt = + OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; + else if (!strcmp(s, "bch16")) + if (gpmc_nand_data->elm_of_node) + gpmc_nand_data->ecc_opt = + OMAP_ECC_BCH16_CODE_HW; + else + pr_err("%s: BCH16 requires ELM support\n", __func__); + else + pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__); + + /* select data transfer mode for NAND controller */ + if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) + for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++) + if (!strcasecmp(s, nand_xfer_types[val])) { + gpmc_nand_data->xfer_type = val; + break; + } + + gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child); + + val = of_get_nand_bus_width(child); + if (val == 16) + gpmc_nand_data->devsize = NAND_BUSWIDTH_16; + + gpmc_read_timings_dt(child, &gpmc_t); + gpmc_nand_init(gpmc_nand_data, &gpmc_t); + + return 0; +} +#else +static int gpmc_probe_nand_child(struct platform_device *pdev, + struct device_node *child) +{ + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_MTD_ONENAND) +static int gpmc_probe_onenand_child(struct platform_device *pdev, + struct device_node *child) +{ + u32 val; + struct omap_onenand_platform_data *gpmc_onenand_data; + + if (of_property_read_u32(child, "reg", &val) < 0) { + dev_err(&pdev->dev, "%s has no 'reg' property\n", + child->full_name); + return -ENODEV; + } + + gpmc_onenand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_onenand_data), + GFP_KERNEL); + if (!gpmc_onenand_data) + return -ENOMEM; + + gpmc_onenand_data->cs = val; + gpmc_onenand_data->of_node = child; + gpmc_onenand_data->dma_channel = -1; + + if (!of_property_read_u32(child, "dma-channel", &val)) + gpmc_onenand_data->dma_channel = val; + + gpmc_onenand_init(gpmc_onenand_data); + + return 0; +} +#else +static int gpmc_probe_onenand_child(struct platform_device *pdev, + struct device_node *child) +{ + return 0; +} +#endif + +/** + * gpmc_probe_generic_child - configures the gpmc for a child device + * @pdev: pointer to gpmc platform device + * @child: pointer to device-tree node for child device + * + * Allocates and configures a GPMC chip-select for a child device. + * Returns 0 on success and appropriate negative error code on failure. + */ +static int gpmc_probe_generic_child(struct platform_device *pdev, + struct device_node *child) +{ + struct gpmc_settings gpmc_s; + struct gpmc_timings gpmc_t; + struct resource res; + unsigned long base; + const char *name; + int ret, cs; + u32 val; + + if (of_property_read_u32(child, "reg", &cs) < 0) { + dev_err(&pdev->dev, "%s has no 'reg' property\n", + child->full_name); + return -ENODEV; + } + + if (of_address_to_resource(child, 0, &res) < 0) { + dev_err(&pdev->dev, "%s has malformed 'reg' property\n", + child->full_name); + return -ENODEV; + } + + /* + * Check if we have multiple instances of the same device + * on a single chip select. If so, use the already initialized + * timings. + */ + name = gpmc_cs_get_name(cs); + if (name && child->name && of_node_cmp(child->name, name) == 0) + goto no_timings; + + ret = gpmc_cs_request(cs, resource_size(&res), &base); + if (ret < 0) { + dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs); + return ret; + } + gpmc_cs_set_name(cs, child->name); + + gpmc_read_settings_dt(child, &gpmc_s); + gpmc_read_timings_dt(child, &gpmc_t); + + /* + * For some GPMC devices we still need to rely on the bootloader + * timings because the devices can be connected via FPGA. + * REVISIT: Add timing support from slls644g.pdf. + */ + if (!gpmc_t.cs_rd_off) { + WARN(1, "enable GPMC debug to configure .dts timings for CS%i\n", + cs); + gpmc_cs_show_timings(cs, + "please add GPMC bootloader timings to .dts"); + goto no_timings; + } + + /* CS must be disabled while making changes to gpmc configuration */ + gpmc_cs_disable_mem(cs); + + /* + * FIXME: gpmc_cs_request() will map the CS to an arbitary + * location in the gpmc address space. When booting with + * device-tree we want the NOR flash to be mapped to the + * location specified in the device-tree blob. So remap the + * CS to this location. Once DT migration is complete should + * just make gpmc_cs_request() map a specific address. + */ + ret = gpmc_cs_remap(cs, res.start); + if (ret < 0) { + dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n", + cs, &res.start); + goto err; + } + + ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); + if (ret < 0) + goto err; + + ret = gpmc_cs_program_settings(cs, &gpmc_s); + if (ret < 0) + goto err; + + ret = gpmc_cs_set_timings(cs, &gpmc_t); + if (ret) { + dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n", + child->name); + goto err; + } + + /* Clear limited address i.e. enable A26-A11 */ + val = gpmc_read_reg(GPMC_CONFIG); + val &= ~GPMC_CONFIG_LIMITEDADDRESS; + gpmc_write_reg(GPMC_CONFIG, val); + + /* Enable CS region */ + gpmc_cs_enable_mem(cs); + +no_timings: + if (of_platform_device_create(child, NULL, &pdev->dev)) + return 0; + + dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name); + ret = -ENODEV; + +err: + gpmc_cs_free(cs); + + return ret; +} + +static int gpmc_probe_dt(struct platform_device *pdev) +{ + int ret; + struct device_node *child; + const struct of_device_id *of_id = + of_match_device(gpmc_dt_ids, &pdev->dev); + + if (!of_id) + return 0; + + ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-cs", + &gpmc_cs_num); + if (ret < 0) { + pr_err("%s: number of chip-selects not defined\n", __func__); + return ret; + } else if (gpmc_cs_num < 1) { + pr_err("%s: all chip-selects are disabled\n", __func__); + return -EINVAL; + } else if (gpmc_cs_num > GPMC_CS_NUM) { + pr_err("%s: number of supported chip-selects cannot be > %d\n", + __func__, GPMC_CS_NUM); + return -EINVAL; + } + + ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins", + &gpmc_nr_waitpins); + if (ret < 0) { + pr_err("%s: number of wait pins not found!\n", __func__); + return ret; + } + + for_each_available_child_of_node(pdev->dev.of_node, child) { + + if (!child->name) + continue; + + if (of_node_cmp(child->name, "nand") == 0) + ret = gpmc_probe_nand_child(pdev, child); + else if (of_node_cmp(child->name, "onenand") == 0) + ret = gpmc_probe_onenand_child(pdev, child); + else if (of_node_cmp(child->name, "ethernet") == 0 || + of_node_cmp(child->name, "nor") == 0 || + of_node_cmp(child->name, "uart") == 0) + ret = gpmc_probe_generic_child(pdev, child); + + if (WARN(ret < 0, "%s: probing gpmc child %s failed\n", + __func__, child->full_name)) + of_node_put(child); + } + + return 0; +} +#else +static int gpmc_probe_dt(struct platform_device *pdev) +{ + return 0; +} +#endif + +static int gpmc_probe(struct platform_device *pdev) +{ + int rc; + u32 l; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -ENOENT; + + phys_base = res->start; + mem_size = resource_size(res); + + gpmc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpmc_base)) + return PTR_ERR(gpmc_base); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) + dev_warn(&pdev->dev, "Failed to get resource: irq\n"); + else + gpmc_irq = res->start; + + gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(gpmc_l3_clk)) { + dev_err(&pdev->dev, "Failed to get GPMC fck\n"); + gpmc_irq = 0; + return PTR_ERR(gpmc_l3_clk); + } + + if (!clk_get_rate(gpmc_l3_clk)) { + dev_err(&pdev->dev, "Invalid GPMC fck clock rate\n"); + return -EINVAL; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + gpmc_dev = &pdev->dev; + + l = gpmc_read_reg(GPMC_REVISION); + + /* + * FIXME: Once device-tree migration is complete the below flags + * should be populated based upon the device-tree compatible + * string. For now just use the IP revision. OMAP3+ devices have + * the wr_access and wr_data_mux_bus register fields. OMAP4+ + * devices support the addr-addr-data multiplex protocol. + * + * GPMC IP revisions: + * - OMAP24xx = 2.0 + * - OMAP3xxx = 5.0 + * - OMAP44xx/54xx/AM335x = 6.0 + */ + if (GPMC_REVISION_MAJOR(l) > 0x4) + gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS; + if (GPMC_REVISION_MAJOR(l) > 0x5) + gpmc_capability |= GPMC_HAS_MUX_AAD; + dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), + GPMC_REVISION_MINOR(l)); + + gpmc_mem_init(); + + if (gpmc_setup_irq() < 0) + dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); + + if (!pdev->dev.of_node) { + gpmc_cs_num = GPMC_CS_NUM; + gpmc_nr_waitpins = GPMC_NR_WAITPINS; + } + + rc = gpmc_probe_dt(pdev); + if (rc < 0) { + pm_runtime_put_sync(&pdev->dev); + dev_err(gpmc_dev, "failed to probe DT parameters\n"); + return rc; + } + + return 0; +} + +static int gpmc_remove(struct platform_device *pdev) +{ + gpmc_free_irq(); + gpmc_mem_exit(); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + gpmc_dev = NULL; + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gpmc_suspend(struct device *dev) +{ + omap3_gpmc_save_context(); + pm_runtime_put_sync(dev); + return 0; +} + +static int gpmc_resume(struct device *dev) +{ + pm_runtime_get_sync(dev); + omap3_gpmc_restore_context(); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume); + +static struct platform_driver gpmc_driver = { + .probe = gpmc_probe, + .remove = gpmc_remove, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpmc_dt_ids), + .pm = &gpmc_pm_ops, + }, +}; + +static __init int gpmc_init(void) +{ + return platform_driver_register(&gpmc_driver); +} + +static __exit void gpmc_exit(void) +{ + platform_driver_unregister(&gpmc_driver); + +} + +postcore_initcall(gpmc_init); +module_exit(gpmc_exit); + +static irqreturn_t gpmc_handle_irq(int irq, void *dev) +{ + int i; + u32 regval; + + regval = gpmc_read_reg(GPMC_IRQSTATUS); + + if (!regval) + return IRQ_NONE; + + for (i = 0; i < GPMC_NR_IRQ; i++) + if (regval & gpmc_client_irq[i].bitmask) + generic_handle_irq(gpmc_client_irq[i].irq); + + gpmc_write_reg(GPMC_IRQSTATUS, regval); + + return IRQ_HANDLED; +} + +static struct omap3_gpmc_regs gpmc_context; + +void omap3_gpmc_save_context(void) +{ + int i; + + gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG); + gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE); + gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL); + gpmc_context.config = gpmc_read_reg(GPMC_CONFIG); + gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1); + gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2); + gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL); + for (i = 0; i < gpmc_cs_num; i++) { + gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i); + if (gpmc_context.cs_context[i].is_valid) { + gpmc_context.cs_context[i].config1 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG1); + gpmc_context.cs_context[i].config2 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG2); + gpmc_context.cs_context[i].config3 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG3); + gpmc_context.cs_context[i].config4 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG4); + gpmc_context.cs_context[i].config5 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG5); + gpmc_context.cs_context[i].config6 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG6); + gpmc_context.cs_context[i].config7 = + gpmc_cs_read_reg(i, GPMC_CS_CONFIG7); + } + } +} + +void omap3_gpmc_restore_context(void) +{ + int i; + + gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig); + gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable); + gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl); + gpmc_write_reg(GPMC_CONFIG, gpmc_context.config); + gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1); + gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2); + gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control); + for (i = 0; i < gpmc_cs_num; i++) { + if (gpmc_context.cs_context[i].is_valid) { + gpmc_cs_write_reg(i, GPMC_CS_CONFIG1, + gpmc_context.cs_context[i].config1); + gpmc_cs_write_reg(i, GPMC_CS_CONFIG2, + gpmc_context.cs_context[i].config2); + gpmc_cs_write_reg(i, GPMC_CS_CONFIG3, + gpmc_context.cs_context[i].config3); + gpmc_cs_write_reg(i, GPMC_CS_CONFIG4, + gpmc_context.cs_context[i].config4); + gpmc_cs_write_reg(i, GPMC_CS_CONFIG5, + gpmc_context.cs_context[i].config5); + gpmc_cs_write_reg(i, GPMC_CS_CONFIG6, + gpmc_context.cs_context[i].config6); + gpmc_cs_write_reg(i, GPMC_CS_CONFIG7, + gpmc_context.cs_context[i].config7); + } + } +} diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig new file mode 100644 index 000000000000..571087621827 --- /dev/null +++ b/drivers/memory/tegra/Kconfig @@ -0,0 +1,7 @@ +config TEGRA_MC + bool "NVIDIA Tegra Memory Controller support" + default y + depends on ARCH_TEGRA + help + This driver supports the Memory Controller (MC) hardware found on + NVIDIA Tegra SoCs. diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile new file mode 100644 index 000000000000..0d9f497b786c --- /dev/null +++ b/drivers/memory/tegra/Makefile @@ -0,0 +1,7 @@ +tegra-mc-y := mc.o + +tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30.o +tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o +tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o + +obj-$(CONFIG_TEGRA_MC) += tegra-mc.o diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c new file mode 100644 index 000000000000..fe3c44e7e1d1 --- /dev/null +++ b/drivers/memory/tegra/mc.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "mc.h" + +#define MC_INTSTATUS 0x000 +#define MC_INT_DECERR_MTS (1 << 16) +#define MC_INT_SECERR_SEC (1 << 13) +#define MC_INT_DECERR_VPR (1 << 12) +#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) +#define MC_INT_INVALID_SMMU_PAGE (1 << 10) +#define MC_INT_ARBITRATION_EMEM (1 << 9) +#define MC_INT_SECURITY_VIOLATION (1 << 8) +#define MC_INT_DECERR_EMEM (1 << 6) + +#define MC_INTMASK 0x004 + +#define MC_ERR_STATUS 0x08 +#define MC_ERR_STATUS_TYPE_SHIFT 28 +#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT) +#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT) +#define MC_ERR_STATUS_READABLE (1 << 27) +#define MC_ERR_STATUS_WRITABLE (1 << 26) +#define MC_ERR_STATUS_NONSECURE (1 << 25) +#define MC_ERR_STATUS_ADR_HI_SHIFT 20 +#define MC_ERR_STATUS_ADR_HI_MASK 0x3 +#define MC_ERR_STATUS_SECURITY (1 << 17) +#define MC_ERR_STATUS_RW (1 << 16) +#define MC_ERR_STATUS_CLIENT_MASK 0x7f + +#define MC_ERR_ADR 0x0c + +#define MC_EMEM_ARB_CFG 0x90 +#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0) +#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff +#define MC_EMEM_ARB_MISC0 0xd8 + +static const struct of_device_id tegra_mc_of_match[] = { +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, +#endif +#ifdef CONFIG_ARCH_TEGRA_114_SOC + { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc }, +#endif +#ifdef CONFIG_ARCH_TEGRA_124_SOC + { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc }, +#endif + { } +}; +MODULE_DEVICE_TABLE(of, tegra_mc_of_match); + +static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) +{ + unsigned long long tick; + unsigned int i; + u32 value; + + /* compute the number of MC clock cycles per tick */ + tick = mc->tick * clk_get_rate(mc->clk); + do_div(tick, NSEC_PER_SEC); + + value = readl(mc->regs + MC_EMEM_ARB_CFG); + value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; + value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); + writel(value, mc->regs + MC_EMEM_ARB_CFG); + + /* write latency allowance defaults */ + for (i = 0; i < mc->soc->num_clients; i++) { + const struct tegra_mc_la *la = &mc->soc->clients[i].la; + u32 value; + + value = readl(mc->regs + la->reg); + value &= ~(la->mask << la->shift); + value |= (la->def & la->mask) << la->shift; + writel(value, mc->regs + la->reg); + } + + return 0; +} + +static const char *const status_names[32] = { + [ 1] = "External interrupt", + [ 6] = "EMEM address decode error", + [ 8] = "Security violation", + [ 9] = "EMEM arbitration error", + [10] = "Page fault", + [11] = "Invalid APB ASID update", + [12] = "VPR violation", + [13] = "Secure carveout violation", + [16] = "MTS carveout violation", +}; + +static const char *const error_names[8] = { + [2] = "EMEM decode error", + [3] = "TrustZone violation", + [4] = "Carveout violation", + [6] = "SMMU translation error", +}; + +static irqreturn_t tegra_mc_irq(int irq, void *data) +{ + struct tegra_mc *mc = data; + unsigned long status, mask; + unsigned int bit; + + /* mask all interrupts to avoid flooding */ + status = mc_readl(mc, MC_INTSTATUS); + mask = mc_readl(mc, MC_INTMASK); + + for_each_set_bit(bit, &status, 32) { + const char *error = status_names[bit] ?: "unknown"; + const char *client = "unknown", *desc; + const char *direction, *secure; + phys_addr_t addr = 0; + unsigned int i; + char perm[7]; + u8 id, type; + u32 value; + + value = mc_readl(mc, MC_ERR_STATUS); + +#ifdef CONFIG_PHYS_ADDR_T_64BIT + if (mc->soc->num_address_bits > 32) { + addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & + MC_ERR_STATUS_ADR_HI_MASK); + addr <<= 32; + } +#endif + + if (value & MC_ERR_STATUS_RW) + direction = "write"; + else + direction = "read"; + + if (value & MC_ERR_STATUS_SECURITY) + secure = "secure "; + else + secure = ""; + + id = value & MC_ERR_STATUS_CLIENT_MASK; + + for (i = 0; i < mc->soc->num_clients; i++) { + if (mc->soc->clients[i].id == id) { + client = mc->soc->clients[i].name; + break; + } + } + + type = (value & MC_ERR_STATUS_TYPE_MASK) >> + MC_ERR_STATUS_TYPE_SHIFT; + desc = error_names[type]; + + switch (value & MC_ERR_STATUS_TYPE_MASK) { + case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: + perm[0] = ' '; + perm[1] = '['; + + if (value & MC_ERR_STATUS_READABLE) + perm[2] = 'R'; + else + perm[2] = '-'; + + if (value & MC_ERR_STATUS_WRITABLE) + perm[3] = 'W'; + else + perm[3] = '-'; + + if (value & MC_ERR_STATUS_NONSECURE) + perm[4] = '-'; + else + perm[4] = 'S'; + + perm[5] = ']'; + perm[6] = '\0'; + break; + + default: + perm[0] = '\0'; + break; + } + + value = mc_readl(mc, MC_ERR_ADR); + addr |= value; + + dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", + client, secure, direction, &addr, error, + desc, perm); + } + + /* clear interrupts */ + mc_writel(mc, status, MC_INTSTATUS); + + return IRQ_HANDLED; +} + +static int tegra_mc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct resource *res; + struct tegra_mc *mc; + u32 value; + int err; + + match = of_match_node(tegra_mc_of_match, pdev->dev.of_node); + if (!match) + return -ENODEV; + + mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + platform_set_drvdata(pdev, mc); + mc->soc = match->data; + mc->dev = &pdev->dev; + + /* length of MC tick in nanoseconds */ + mc->tick = 30; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mc->regs)) + return PTR_ERR(mc->regs); + + mc->clk = devm_clk_get(&pdev->dev, "mc"); + if (IS_ERR(mc->clk)) { + dev_err(&pdev->dev, "failed to get MC clock: %ld\n", + PTR_ERR(mc->clk)); + return PTR_ERR(mc->clk); + } + + err = tegra_mc_setup_latency_allowance(mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to setup latency allowance: %d\n", + err); + return err; + } + + if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) { + mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); + if (IS_ERR(mc->smmu)) { + dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", + PTR_ERR(mc->smmu)); + return PTR_ERR(mc->smmu); + } + } + + mc->irq = platform_get_irq(pdev, 0); + if (mc->irq < 0) { + dev_err(&pdev->dev, "interrupt not specified\n"); + return mc->irq; + } + + err = devm_request_irq(&pdev->dev, mc->irq, tegra_mc_irq, IRQF_SHARED, + dev_name(&pdev->dev), mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, + err); + return err; + } + + value = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | + MC_INT_ARBITRATION_EMEM | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM; + mc_writel(mc, value, MC_INTMASK); + + return 0; +} + +static struct platform_driver tegra_mc_driver = { + .driver = { + .name = "tegra-mc", + .of_match_table = tegra_mc_of_match, + .suppress_bind_attrs = true, + }, + .prevent_deferred_probe = true, + .probe = tegra_mc_probe, +}; + +static int tegra_mc_init(void) +{ + return platform_driver_register(&tegra_mc_driver); +} +arch_initcall(tegra_mc_init); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h new file mode 100644 index 000000000000..d5d21147fc77 --- /dev/null +++ b/drivers/memory/tegra/mc.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MEMORY_TEGRA_MC_H +#define MEMORY_TEGRA_MC_H + +#include <linux/io.h> +#include <linux/types.h> + +#include <soc/tegra/mc.h> + +static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) +{ + return readl(mc->regs + offset); +} + +static inline void mc_writel(struct tegra_mc *mc, u32 value, + unsigned long offset) +{ + writel(value, mc->regs + offset); +} + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +extern const struct tegra_mc_soc tegra30_mc_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +extern const struct tegra_mc_soc tegra114_mc_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_124_SOC +extern const struct tegra_mc_soc tegra124_mc_soc; +#endif + +#endif /* MEMORY_TEGRA_MC_H */ diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c new file mode 100644 index 000000000000..511e9a25c151 --- /dev/null +++ b/drivers/memory/tegra/tegra114.c @@ -0,0 +1,948 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +#include <dt-bindings/memory/tegra114-mc.h> + +#include "mc.h" + +static const struct tegra_mc_client tegra114_mc_clients[] = { + { + .id = 0x00, + .name = "ptcr", + .swgroup = TEGRA_SWGROUP_PTC, + }, { + .id = 0x01, + .name = "display0a", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 1, + }, + .la = { + .reg = 0x2e8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x02, + .name = "display0ab", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 2, + }, + .la = { + .reg = 0x2f4, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x03, + .name = "display0b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 3, + }, + .la = { + .reg = 0x2e8, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x04, + .name = "display0bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 4, + }, + .la = { + .reg = 0x2f4, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x05, + .name = "display0c", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 5, + }, + .la = { + .reg = 0x2ec, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x06, + .name = "display0cb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 6, + }, + .la = { + .reg = 0x2f8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x09, + .name = "eppup", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x228, + .bit = 9, + }, + .la = { + .reg = 0x300, + .shift = 0, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x0a, + .name = "g2pr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 10, + }, + .la = { + .reg = 0x308, + .shift = 0, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0b, + .name = "g2sr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 11, + }, + .la = { + .reg = 0x308, + .shift = 16, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0f, + .name = "avpcarm7r", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x228, + .bit = 15, + }, + .la = { + .reg = 0x2e4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x10, + .name = "displayhc", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 16, + }, + .la = { + .reg = 0x2f0, + .shift = 0, + .mask = 0xff, + .def = 0x68, + }, + }, { + .id = 0x11, + .name = "displayhcb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 17, + }, + .la = { + .reg = 0x2fc, + .shift = 0, + .mask = 0xff, + .def = 0x68, + }, + }, { + .id = 0x12, + .name = "fdcdrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 18, + }, + .la = { + .reg = 0x334, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x13, + .name = "fdcdrd2", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 19, + }, + .la = { + .reg = 0x33c, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x14, + .name = "g2dr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 20, + }, + .la = { + .reg = 0x30c, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x15, + .name = "hdar", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x228, + .bit = 21, + }, + .la = { + .reg = 0x318, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x16, + .name = "host1xdmar", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 22, + }, + .la = { + .reg = 0x310, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x17, + .name = "host1xr", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 23, + }, + .la = { + .reg = 0x310, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x18, + .name = "idxsrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 24, + }, + .la = { + .reg = 0x334, + .shift = 16, + .mask = 0xff, + .def = 0x0b, + }, + }, { + .id = 0x1c, + .name = "msencsrd", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x228, + .bit = 28, + }, + .la = { + .reg = 0x328, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x1d, + .name = "ppcsahbdmar", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 29, + }, + .la = { + .reg = 0x344, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x1e, + .name = "ppcsahbslvr", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 30, + }, + .la = { + .reg = 0x344, + .shift = 16, + .mask = 0xff, + .def = 0xe8, + }, + }, { + .id = 0x20, + .name = "texl2srd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 0, + }, + .la = { + .reg = 0x338, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x22, + .name = "vdebsevr", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 2, + }, + .la = { + .reg = 0x354, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x23, + .name = "vdember", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 3, + }, + .la = { + .reg = 0x354, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x24, + .name = "vdemcer", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 4, + }, + .la = { + .reg = 0x358, + .shift = 0, + .mask = 0xff, + .def = 0xb8, + }, + }, { + .id = 0x25, + .name = "vdetper", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 5, + }, + .la = { + .reg = 0x358, + .shift = 16, + .mask = 0xff, + .def = 0xee, + }, + }, { + .id = 0x26, + .name = "mpcorelpr", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .la = { + .reg = 0x324, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x27, + .name = "mpcorer", + .swgroup = TEGRA_SWGROUP_MPCORE, + .la = { + .reg = 0x320, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x28, + .name = "eppu", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 8, + }, + .la = { + .reg = 0x300, + .shift = 16, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x29, + .name = "eppv", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 9, + }, + .la = { + .reg = 0x304, + .shift = 0, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2a, + .name = "eppy", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 10, + }, + .la = { + .reg = 0x304, + .shift = 16, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2b, + .name = "msencswr", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x22c, + .bit = 11, + }, + .la = { + .reg = 0x328, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x2c, + .name = "viwsb", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 12, + }, + .la = { + .reg = 0x364, + .shift = 0, + .mask = 0xff, + .def = 0x47, + }, + }, { + .id = 0x2d, + .name = "viwu", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 13, + }, + .la = { + .reg = 0x368, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x2e, + .name = "viwv", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 14, + }, + .la = { + .reg = 0x368, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x2f, + .name = "viwy", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 15, + }, + .la = { + .reg = 0x36c, + .shift = 0, + .mask = 0xff, + .def = 0x47, + }, + }, { + .id = 0x30, + .name = "g2dw", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x22c, + .bit = 16, + }, + .la = { + .reg = 0x30c, + .shift = 16, + .mask = 0xff, + .def = 0x9, + }, + }, { + .id = 0x32, + .name = "avpcarm7w", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x22c, + .bit = 18, + }, + .la = { + .reg = 0x2e4, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x33, + .name = "fdcdwr", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 19, + }, + .la = { + .reg = 0x338, + .shift = 16, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x34, + .name = "fdcwr2", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 20, + }, + .la = { + .reg = 0x340, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x35, + .name = "hdaw", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x22c, + .bit = 21, + }, + .la = { + .reg = 0x318, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x36, + .name = "host1xw", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x22c, + .bit = 22, + }, + .la = { + .reg = 0x314, + .shift = 0, + .mask = 0xff, + .def = 0x25, + }, + }, { + .id = 0x37, + .name = "ispw", + .swgroup = TEGRA_SWGROUP_ISP, + .smmu = { + .reg = 0x22c, + .bit = 23, + }, + .la = { + .reg = 0x31c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x38, + .name = "mpcorelpw", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .la = { + .reg = 0x324, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x39, + .name = "mpcorew", + .swgroup = TEGRA_SWGROUP_MPCORE, + .la = { + .reg = 0x320, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x3b, + .name = "ppcsahbdmaw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 27, + }, + .la = { + .reg = 0x348, + .shift = 0, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x3c, + .name = "ppcsahbslvw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 28, + }, + .la = { + .reg = 0x348, + .shift = 16, + .mask = 0xff, + .def = 0xe8, + }, + }, { + .id = 0x3e, + .name = "vdebsevw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 30, + }, + .la = { + .reg = 0x35c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x3f, + .name = "vdedbgw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 31, + }, + .la = { + .reg = 0x35c, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x40, + .name = "vdembew", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 0, + }, + .la = { + .reg = 0x360, + .shift = 0, + .mask = 0xff, + .def = 0x89, + }, + }, { + .id = 0x41, + .name = "vdetpmw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 1, + }, + .la = { + .reg = 0x360, + .shift = 16, + .mask = 0xff, + .def = 0x59, + }, + }, { + .id = 0x4a, + .name = "xusb_hostr", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 10, + }, + .la = { + .reg = 0x37c, + .shift = 0, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4b, + .name = "xusb_hostw", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 11, + }, + .la = { + .reg = 0x37c, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4c, + .name = "xusb_devr", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 12, + }, + .la = { + .reg = 0x380, + .shift = 0, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4d, + .name = "xusb_devw", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 13, + }, + .la = { + .reg = 0x380, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4e, + .name = "fdcdwr3", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 14, + }, + .la = { + .reg = 0x388, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x4f, + .name = "fdcdrd3", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 15, + }, + .la = { + .reg = 0x384, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x50, + .name = "fdcwr4", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 16, + }, + .la = { + .reg = 0x388, + .shift = 16, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x51, + .name = "fdcrd4", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 17, + }, + .la = { + .reg = 0x384, + .shift = 16, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x52, + .name = "emucifr", + .swgroup = TEGRA_SWGROUP_EMUCIF, + .la = { + .reg = 0x38c, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x53, + .name = "emucifw", + .swgroup = TEGRA_SWGROUP_EMUCIF, + .la = { + .reg = 0x38c, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x54, + .name = "tsecsrd", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 20, + }, + .la = { + .reg = 0x390, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x55, + .name = "tsecswr", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 21, + }, + .la = { + .reg = 0x390, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, +}; + +static const struct tegra_smmu_swgroup tegra114_swgroups[] = { + { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, + { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, + { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 }, + { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c }, + { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, + { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 }, + { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, + { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, + { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 }, + { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, + { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, + { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, + { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, + { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 }, + { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c }, + { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, +}; + +static void tegra114_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + phys_addr_t phys = page_to_phys(page) + offset; + void *virt = page_address(page) + offset; + + __cpuc_flush_dcache_area(virt, size); + outer_flush_range(phys, phys + size); +} + +static const struct tegra_smmu_ops tegra114_smmu_ops = { + .flush_dcache = tegra114_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra114_smmu_soc = { + .clients = tegra114_mc_clients, + .num_clients = ARRAY_SIZE(tegra114_mc_clients), + .swgroups = tegra114_swgroups, + .num_swgroups = ARRAY_SIZE(tegra114_swgroups), + .supports_round_robin_arbitration = false, + .supports_request_limit = false, + .num_asids = 4, + .ops = &tegra114_smmu_ops, +}; + +const struct tegra_mc_soc tegra114_mc_soc = { + .clients = tegra114_mc_clients, + .num_clients = ARRAY_SIZE(tegra114_mc_clients), + .num_address_bits = 32, + .atom_size = 32, + .smmu = &tegra114_smmu_soc, +}; diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c new file mode 100644 index 000000000000..278d40b854c1 --- /dev/null +++ b/drivers/memory/tegra/tegra124.c @@ -0,0 +1,995 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +#include <dt-bindings/memory/tegra124-mc.h> + +#include "mc.h" + +static const struct tegra_mc_client tegra124_mc_clients[] = { + { + .id = 0x00, + .name = "ptcr", + .swgroup = TEGRA_SWGROUP_PTC, + }, { + .id = 0x01, + .name = "display0a", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 1, + }, + .la = { + .reg = 0x2e8, + .shift = 0, + .mask = 0xff, + .def = 0xc2, + }, + }, { + .id = 0x02, + .name = "display0ab", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 2, + }, + .la = { + .reg = 0x2f4, + .shift = 0, + .mask = 0xff, + .def = 0xc6, + }, + }, { + .id = 0x03, + .name = "display0b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 3, + }, + .la = { + .reg = 0x2e8, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x04, + .name = "display0bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 4, + }, + .la = { + .reg = 0x2f4, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x05, + .name = "display0c", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 5, + }, + .la = { + .reg = 0x2ec, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x06, + .name = "display0cb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 6, + }, + .la = { + .reg = 0x2f8, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x0e, + .name = "afir", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x228, + .bit = 14, + }, + .la = { + .reg = 0x2e0, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x0f, + .name = "avpcarm7r", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x228, + .bit = 15, + }, + .la = { + .reg = 0x2e4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x10, + .name = "displayhc", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 16, + }, + .la = { + .reg = 0x2f0, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x11, + .name = "displayhcb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 17, + }, + .la = { + .reg = 0x2fc, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x15, + .name = "hdar", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x228, + .bit = 21, + }, + .la = { + .reg = 0x318, + .shift = 0, + .mask = 0xff, + .def = 0x24, + }, + }, { + .id = 0x16, + .name = "host1xdmar", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 22, + }, + .la = { + .reg = 0x310, + .shift = 0, + .mask = 0xff, + .def = 0x1e, + }, + }, { + .id = 0x17, + .name = "host1xr", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 23, + }, + .la = { + .reg = 0x310, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x1c, + .name = "msencsrd", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x228, + .bit = 28, + }, + .la = { + .reg = 0x328, + .shift = 0, + .mask = 0xff, + .def = 0x23, + }, + }, { + .id = 0x1d, + .name = "ppcsahbdmar", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 29, + }, + .la = { + .reg = 0x344, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x1e, + .name = "ppcsahbslvr", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 30, + }, + .la = { + .reg = 0x344, + .shift = 16, + .mask = 0xff, + .def = 0x1a, + }, + }, { + .id = 0x1f, + .name = "satar", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x228, + .bit = 31, + }, + .la = { + .reg = 0x350, + .shift = 0, + .mask = 0xff, + .def = 0x65, + }, + }, { + .id = 0x22, + .name = "vdebsevr", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 2, + }, + .la = { + .reg = 0x354, + .shift = 0, + .mask = 0xff, + .def = 0x4f, + }, + }, { + .id = 0x23, + .name = "vdember", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 3, + }, + .la = { + .reg = 0x354, + .shift = 16, + .mask = 0xff, + .def = 0x3d, + }, + }, { + .id = 0x24, + .name = "vdemcer", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 4, + }, + .la = { + .reg = 0x358, + .shift = 0, + .mask = 0xff, + .def = 0x66, + }, + }, { + .id = 0x25, + .name = "vdetper", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 5, + }, + .la = { + .reg = 0x358, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x26, + .name = "mpcorelpr", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .la = { + .reg = 0x324, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x27, + .name = "mpcorer", + .swgroup = TEGRA_SWGROUP_MPCORE, + .la = { + .reg = 0x320, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x2b, + .name = "msencswr", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x22c, + .bit = 11, + }, + .la = { + .reg = 0x328, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x31, + .name = "afiw", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x22c, + .bit = 17, + }, + .la = { + .reg = 0x2e0, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x32, + .name = "avpcarm7w", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x22c, + .bit = 18, + }, + .la = { + .reg = 0x2e4, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x35, + .name = "hdaw", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x22c, + .bit = 21, + }, + .la = { + .reg = 0x318, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x36, + .name = "host1xw", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x22c, + .bit = 22, + }, + .la = { + .reg = 0x314, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x38, + .name = "mpcorelpw", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .la = { + .reg = 0x324, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x39, + .name = "mpcorew", + .swgroup = TEGRA_SWGROUP_MPCORE, + .la = { + .reg = 0x320, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3b, + .name = "ppcsahbdmaw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 27, + }, + .la = { + .reg = 0x348, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3c, + .name = "ppcsahbslvw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 28, + }, + .la = { + .reg = 0x348, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3d, + .name = "sataw", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x22c, + .bit = 29, + }, + .la = { + .reg = 0x350, + .shift = 16, + .mask = 0xff, + .def = 0x65, + }, + }, { + .id = 0x3e, + .name = "vdebsevw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 30, + }, + .la = { + .reg = 0x35c, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3f, + .name = "vdedbgw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 31, + }, + .la = { + .reg = 0x35c, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x40, + .name = "vdembew", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 0, + }, + .la = { + .reg = 0x360, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x41, + .name = "vdetpmw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 1, + }, + .la = { + .reg = 0x360, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x44, + .name = "ispra", + .swgroup = TEGRA_SWGROUP_ISP2, + .smmu = { + .reg = 0x230, + .bit = 4, + }, + .la = { + .reg = 0x370, + .shift = 0, + .mask = 0xff, + .def = 0x18, + }, + }, { + .id = 0x46, + .name = "ispwa", + .swgroup = TEGRA_SWGROUP_ISP2, + .smmu = { + .reg = 0x230, + .bit = 6, + }, + .la = { + .reg = 0x374, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x47, + .name = "ispwb", + .swgroup = TEGRA_SWGROUP_ISP2, + .smmu = { + .reg = 0x230, + .bit = 7, + }, + .la = { + .reg = 0x374, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x4a, + .name = "xusb_hostr", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 10, + }, + .la = { + .reg = 0x37c, + .shift = 0, + .mask = 0xff, + .def = 0x39, + }, + }, { + .id = 0x4b, + .name = "xusb_hostw", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 11, + }, + .la = { + .reg = 0x37c, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x4c, + .name = "xusb_devr", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 12, + }, + .la = { + .reg = 0x380, + .shift = 0, + .mask = 0xff, + .def = 0x39, + }, + }, { + .id = 0x4d, + .name = "xusb_devw", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 13, + }, + .la = { + .reg = 0x380, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x4e, + .name = "isprab", + .swgroup = TEGRA_SWGROUP_ISP2B, + .smmu = { + .reg = 0x230, + .bit = 14, + }, + .la = { + .reg = 0x384, + .shift = 0, + .mask = 0xff, + .def = 0x18, + }, + }, { + .id = 0x50, + .name = "ispwab", + .swgroup = TEGRA_SWGROUP_ISP2B, + .smmu = { + .reg = 0x230, + .bit = 16, + }, + .la = { + .reg = 0x388, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x51, + .name = "ispwbb", + .swgroup = TEGRA_SWGROUP_ISP2B, + .smmu = { + .reg = 0x230, + .bit = 17, + }, + .la = { + .reg = 0x388, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x54, + .name = "tsecsrd", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 20, + }, + .la = { + .reg = 0x390, + .shift = 0, + .mask = 0xff, + .def = 0x9b, + }, + }, { + .id = 0x55, + .name = "tsecswr", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 21, + }, + .la = { + .reg = 0x390, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x56, + .name = "a9avpscr", + .swgroup = TEGRA_SWGROUP_A9AVP, + .smmu = { + .reg = 0x230, + .bit = 22, + }, + .la = { + .reg = 0x3a4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x57, + .name = "a9avpscw", + .swgroup = TEGRA_SWGROUP_A9AVP, + .smmu = { + .reg = 0x230, + .bit = 23, + }, + .la = { + .reg = 0x3a4, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x58, + .name = "gpusrd", + .swgroup = TEGRA_SWGROUP_GPU, + .smmu = { + /* read-only */ + .reg = 0x230, + .bit = 24, + }, + .la = { + .reg = 0x3c8, + .shift = 0, + .mask = 0xff, + .def = 0x1a, + }, + }, { + .id = 0x59, + .name = "gpuswr", + .swgroup = TEGRA_SWGROUP_GPU, + .smmu = { + /* read-only */ + .reg = 0x230, + .bit = 25, + }, + .la = { + .reg = 0x3c8, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x5a, + .name = "displayt", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x230, + .bit = 26, + }, + .la = { + .reg = 0x2f0, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x60, + .name = "sdmmcra", + .swgroup = TEGRA_SWGROUP_SDMMC1A, + .smmu = { + .reg = 0x234, + .bit = 0, + }, + .la = { + .reg = 0x3b8, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x61, + .name = "sdmmcraa", + .swgroup = TEGRA_SWGROUP_SDMMC2A, + .smmu = { + .reg = 0x234, + .bit = 1, + }, + .la = { + .reg = 0x3bc, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x62, + .name = "sdmmcr", + .swgroup = TEGRA_SWGROUP_SDMMC3A, + .smmu = { + .reg = 0x234, + .bit = 2, + }, + .la = { + .reg = 0x3c0, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x63, + .swgroup = TEGRA_SWGROUP_SDMMC4A, + .name = "sdmmcrab", + .smmu = { + .reg = 0x234, + .bit = 3, + }, + .la = { + .reg = 0x3c4, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x64, + .name = "sdmmcwa", + .swgroup = TEGRA_SWGROUP_SDMMC1A, + .smmu = { + .reg = 0x234, + .bit = 4, + }, + .la = { + .reg = 0x3b8, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x65, + .name = "sdmmcwaa", + .swgroup = TEGRA_SWGROUP_SDMMC2A, + .smmu = { + .reg = 0x234, + .bit = 5, + }, + .la = { + .reg = 0x3bc, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x66, + .name = "sdmmcw", + .swgroup = TEGRA_SWGROUP_SDMMC3A, + .smmu = { + .reg = 0x234, + .bit = 6, + }, + .la = { + .reg = 0x3c0, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x67, + .name = "sdmmcwab", + .swgroup = TEGRA_SWGROUP_SDMMC4A, + .smmu = { + .reg = 0x234, + .bit = 7, + }, + .la = { + .reg = 0x3c4, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x6c, + .name = "vicsrd", + .swgroup = TEGRA_SWGROUP_VIC, + .smmu = { + .reg = 0x234, + .bit = 12, + }, + .la = { + .reg = 0x394, + .shift = 0, + .mask = 0xff, + .def = 0x1a, + }, + }, { + .id = 0x6d, + .name = "vicswr", + .swgroup = TEGRA_SWGROUP_VIC, + .smmu = { + .reg = 0x234, + .bit = 13, + }, + .la = { + .reg = 0x394, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x72, + .name = "viw", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x234, + .bit = 18, + }, + .la = { + .reg = 0x398, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x73, + .name = "displayd", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x234, + .bit = 19, + }, + .la = { + .reg = 0x3c8, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, +}; + +static const struct tegra_smmu_swgroup tegra124_swgroups[] = { + { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, + { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, + { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 }, + { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, + { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, + { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, + { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 }, + { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, + { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 }, + { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, + { .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 }, + { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 }, + { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c }, + { .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 }, + { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, + { .swgroup = TEGRA_SWGROUP_A9AVP, .reg = 0x290 }, + { .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaac }, + { .swgroup = TEGRA_SWGROUP_SDMMC1A, .reg = 0xa94 }, + { .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 }, + { .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c }, + { .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 }, + { .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 }, + { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, +}; + +#ifdef CONFIG_ARCH_TEGRA_124_SOC +static void tegra124_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + phys_addr_t phys = page_to_phys(page) + offset; + void *virt = page_address(page) + offset; + + __cpuc_flush_dcache_area(virt, size); + outer_flush_range(phys, phys + size); +} + +static const struct tegra_smmu_ops tegra124_smmu_ops = { + .flush_dcache = tegra124_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra124_smmu_soc = { + .clients = tegra124_mc_clients, + .num_clients = ARRAY_SIZE(tegra124_mc_clients), + .swgroups = tegra124_swgroups, + .num_swgroups = ARRAY_SIZE(tegra124_swgroups), + .supports_round_robin_arbitration = true, + .supports_request_limit = true, + .num_asids = 128, + .ops = &tegra124_smmu_ops, +}; + +const struct tegra_mc_soc tegra124_mc_soc = { + .clients = tegra124_mc_clients, + .num_clients = ARRAY_SIZE(tegra124_mc_clients), + .num_address_bits = 34, + .atom_size = 32, + .smmu = &tegra124_smmu_soc, +}; +#endif /* CONFIG_ARCH_TEGRA_124_SOC */ diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c new file mode 100644 index 000000000000..71fe9376fe53 --- /dev/null +++ b/drivers/memory/tegra/tegra30.c @@ -0,0 +1,970 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +#include <dt-bindings/memory/tegra30-mc.h> + +#include "mc.h" + +static const struct tegra_mc_client tegra30_mc_clients[] = { + { + .id = 0x00, + .name = "ptcr", + .swgroup = TEGRA_SWGROUP_PTC, + }, { + .id = 0x01, + .name = "display0a", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 1, + }, + .la = { + .reg = 0x2e8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x02, + .name = "display0ab", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 2, + }, + .la = { + .reg = 0x2f4, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x03, + .name = "display0b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 3, + }, + .la = { + .reg = 0x2e8, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x04, + .name = "display0bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 4, + }, + .la = { + .reg = 0x2f4, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x05, + .name = "display0c", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 5, + }, + .la = { + .reg = 0x2ec, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x06, + .name = "display0cb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 6, + }, + .la = { + .reg = 0x2f8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x07, + .name = "display1b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 7, + }, + .la = { + .reg = 0x2ec, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x08, + .name = "display1bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 8, + }, + .la = { + .reg = 0x2f8, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x09, + .name = "eppup", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x228, + .bit = 9, + }, + .la = { + .reg = 0x300, + .shift = 0, + .mask = 0xff, + .def = 0x17, + }, + }, { + .id = 0x0a, + .name = "g2pr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 10, + }, + .la = { + .reg = 0x308, + .shift = 0, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0b, + .name = "g2sr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 11, + }, + .la = { + .reg = 0x308, + .shift = 16, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0c, + .name = "mpeunifbr", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 12, + }, + .la = { + .reg = 0x328, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x0d, + .name = "viruv", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x228, + .bit = 13, + }, + .la = { + .reg = 0x364, + .shift = 0, + .mask = 0xff, + .def = 0x2c, + }, + }, { + .id = 0x0e, + .name = "afir", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x228, + .bit = 14, + }, + .la = { + .reg = 0x2e0, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x0f, + .name = "avpcarm7r", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x228, + .bit = 15, + }, + .la = { + .reg = 0x2e4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x10, + .name = "displayhc", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 16, + }, + .la = { + .reg = 0x2f0, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x11, + .name = "displayhcb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 17, + }, + .la = { + .reg = 0x2fc, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x12, + .name = "fdcdrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 18, + }, + .la = { + .reg = 0x334, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x13, + .name = "fdcdrd2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x228, + .bit = 19, + }, + .la = { + .reg = 0x33c, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x14, + .name = "g2dr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 20, + }, + .la = { + .reg = 0x30c, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x15, + .name = "hdar", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x228, + .bit = 21, + }, + .la = { + .reg = 0x318, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x16, + .name = "host1xdmar", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 22, + }, + .la = { + .reg = 0x310, + .shift = 0, + .mask = 0xff, + .def = 0x05, + }, + }, { + .id = 0x17, + .name = "host1xr", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 23, + }, + .la = { + .reg = 0x310, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x18, + .name = "idxsrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 24, + }, + .la = { + .reg = 0x334, + .shift = 16, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x19, + .name = "idxsrd2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x228, + .bit = 25, + }, + .la = { + .reg = 0x33c, + .shift = 16, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x1a, + .name = "mpe_ipred", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 26, + }, + .la = { + .reg = 0x328, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x1b, + .name = "mpeamemrd", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 27, + }, + .la = { + .reg = 0x32c, + .shift = 0, + .mask = 0xff, + .def = 0x42, + }, + }, { + .id = 0x1c, + .name = "mpecsrd", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 28, + }, + .la = { + .reg = 0x32c, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x1d, + .name = "ppcsahbdmar", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 29, + }, + .la = { + .reg = 0x344, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x1e, + .name = "ppcsahbslvr", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 30, + }, + .la = { + .reg = 0x344, + .shift = 16, + .mask = 0xff, + .def = 0x12, + }, + }, { + .id = 0x1f, + .name = "satar", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x228, + .bit = 31, + }, + .la = { + .reg = 0x350, + .shift = 0, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x20, + .name = "texsrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 0, + }, + .la = { + .reg = 0x338, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x21, + .name = "texsrd2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x22c, + .bit = 1, + }, + .la = { + .reg = 0x340, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x22, + .name = "vdebsevr", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 2, + }, + .la = { + .reg = 0x354, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x23, + .name = "vdember", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 3, + }, + .la = { + .reg = 0x354, + .shift = 16, + .mask = 0xff, + .def = 0xd0, + }, + }, { + .id = 0x24, + .name = "vdemcer", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 4, + }, + .la = { + .reg = 0x358, + .shift = 0, + .mask = 0xff, + .def = 0x2a, + }, + }, { + .id = 0x25, + .name = "vdetper", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 5, + }, + .la = { + .reg = 0x358, + .shift = 16, + .mask = 0xff, + .def = 0x74, + }, + }, { + .id = 0x26, + .name = "mpcorelpr", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .la = { + .reg = 0x324, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x27, + .name = "mpcorer", + .swgroup = TEGRA_SWGROUP_MPCORE, + .la = { + .reg = 0x320, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x28, + .name = "eppu", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 8, + }, + .la = { + .reg = 0x300, + .shift = 16, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x29, + .name = "eppv", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 9, + }, + .la = { + .reg = 0x304, + .shift = 0, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2a, + .name = "eppy", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 10, + }, + .la = { + .reg = 0x304, + .shift = 16, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2b, + .name = "mpeunifbw", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x22c, + .bit = 11, + }, + .la = { + .reg = 0x330, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x2c, + .name = "viwsb", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 12, + }, + .la = { + .reg = 0x364, + .shift = 16, + .mask = 0xff, + .def = 0x12, + }, + }, { + .id = 0x2d, + .name = "viwu", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 13, + }, + .la = { + .reg = 0x368, + .shift = 0, + .mask = 0xff, + .def = 0xb2, + }, + }, { + .id = 0x2e, + .name = "viwv", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 14, + }, + .la = { + .reg = 0x368, + .shift = 16, + .mask = 0xff, + .def = 0xb2, + }, + }, { + .id = 0x2f, + .name = "viwy", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 15, + }, + .la = { + .reg = 0x36c, + .shift = 0, + .mask = 0xff, + .def = 0x12, + }, + }, { + .id = 0x30, + .name = "g2dw", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x22c, + .bit = 16, + }, + .la = { + .reg = 0x30c, + .shift = 16, + .mask = 0xff, + .def = 0x9, + }, + }, { + .id = 0x31, + .name = "afiw", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x22c, + .bit = 17, + }, + .la = { + .reg = 0x2e0, + .shift = 16, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x32, + .name = "avpcarm7w", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x22c, + .bit = 18, + }, + .la = { + .reg = 0x2e4, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x33, + .name = "fdcdwr", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 19, + }, + .la = { + .reg = 0x338, + .shift = 16, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x34, + .name = "fdcwr2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x22c, + .bit = 20, + }, + .la = { + .reg = 0x340, + .shift = 16, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x35, + .name = "hdaw", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x22c, + .bit = 21, + }, + .la = { + .reg = 0x318, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x36, + .name = "host1xw", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x22c, + .bit = 22, + }, + .la = { + .reg = 0x314, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x37, + .name = "ispw", + .swgroup = TEGRA_SWGROUP_ISP, + .smmu = { + .reg = 0x22c, + .bit = 23, + }, + .la = { + .reg = 0x31c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x38, + .name = "mpcorelpw", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .la = { + .reg = 0x324, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x39, + .name = "mpcorew", + .swgroup = TEGRA_SWGROUP_MPCORE, + .la = { + .reg = 0x320, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x3a, + .name = "mpecswr", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x22c, + .bit = 26, + }, + .la = { + .reg = 0x330, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x3b, + .name = "ppcsahbdmaw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 27, + }, + .la = { + .reg = 0x348, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x3c, + .name = "ppcsahbslvw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 28, + }, + .la = { + .reg = 0x348, + .shift = 16, + .mask = 0xff, + .def = 0x06, + }, + }, { + .id = 0x3d, + .name = "sataw", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x22c, + .bit = 29, + }, + .la = { + .reg = 0x350, + .shift = 16, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x3e, + .name = "vdebsevw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 30, + }, + .la = { + .reg = 0x35c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x3f, + .name = "vdedbgw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 31, + }, + .la = { + .reg = 0x35c, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x40, + .name = "vdembew", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 0, + }, + .la = { + .reg = 0x360, + .shift = 0, + .mask = 0xff, + .def = 0x42, + }, + }, { + .id = 0x41, + .name = "vdetpmw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 1, + }, + .la = { + .reg = 0x360, + .shift = 16, + .mask = 0xff, + .def = 0x2a, + }, + }, +}; + +static const struct tegra_smmu_swgroup tegra30_swgroups[] = { + { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, + { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, + { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 }, + { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c }, + { .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 }, + { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, + { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 }, + { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, + { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 }, + { .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c }, + { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, + { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, + { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, + { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 }, + { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, + { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, +}; + +static void tegra30_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + phys_addr_t phys = page_to_phys(page) + offset; + void *virt = page_address(page) + offset; + + __cpuc_flush_dcache_area(virt, size); + outer_flush_range(phys, phys + size); +} + +static const struct tegra_smmu_ops tegra30_smmu_ops = { + .flush_dcache = tegra30_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra30_smmu_soc = { + .clients = tegra30_mc_clients, + .num_clients = ARRAY_SIZE(tegra30_mc_clients), + .swgroups = tegra30_swgroups, + .num_swgroups = ARRAY_SIZE(tegra30_swgroups), + .supports_round_robin_arbitration = false, + .supports_request_limit = false, + .num_asids = 4, + .ops = &tegra30_smmu_ops, +}; + +const struct tegra_mc_soc tegra30_mc_soc = { + .clients = tegra30_mc_clients, + .num_clients = ARRAY_SIZE(tegra30_mc_clients), + .num_address_bits = 32, + .atom_size = 16, + .smmu = &tegra30_smmu_soc, +}; diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c deleted file mode 100644 index ef7934535fd1..000000000000 --- a/drivers/memory/tegra30-mc.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Tegra30 Memory Controller - * - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/ratelimit.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/io.h> - -#define DRV_NAME "tegra30-mc" - -#define MC_INTSTATUS 0x0 -#define MC_INTMASK 0x4 - -#define MC_INT_ERR_SHIFT 6 -#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT) -#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT) -#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2) -#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3) -#define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4) - -#define MC_ERR_STATUS 0x8 -#define MC_ERR_ADR 0xc - -#define MC_ERR_TYPE_SHIFT 28 -#define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT) -#define MC_ERR_TYPE_DECERR_EMEM 2 -#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3 -#define MC_ERR_TYPE_SECURITY_CARVEOUT 4 -#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6 - -#define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25 -#define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT) -#define MC_ERR_RW_SHIFT 16 -#define MC_ERR_RW BIT(MC_ERR_RW_SHIFT) -#define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1) - -#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */ - -#define MC_EMEM_ARB_CFG 0x90 -#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 -#define MC_EMEM_ARB_TIMING_RCD 0x98 -#define MC_EMEM_ARB_TIMING_RP 0x9c -#define MC_EMEM_ARB_TIMING_RC 0xa0 -#define MC_EMEM_ARB_TIMING_RAS 0xa4 -#define MC_EMEM_ARB_TIMING_FAW 0xa8 -#define MC_EMEM_ARB_TIMING_RRD 0xac -#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 -#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 -#define MC_EMEM_ARB_TIMING_R2R 0xb8 -#define MC_EMEM_ARB_TIMING_W2W 0xbc -#define MC_EMEM_ARB_TIMING_R2W 0xc0 -#define MC_EMEM_ARB_TIMING_W2R 0xc4 - -#define MC_EMEM_ARB_DA_TURNS 0xd0 -#define MC_EMEM_ARB_DA_COVERS 0xd4 -#define MC_EMEM_ARB_MISC0 0xd8 -#define MC_EMEM_ARB_MISC1 0xdc - -#define MC_EMEM_ARB_RING3_THROTTLE 0xe4 -#define MC_EMEM_ARB_OVERRIDE 0xe8 - -#define MC_TIMING_CONTROL 0xfc - -#define MC_CLIENT_ID_MASK 0x7f - -#define NUM_MC_REG_BANKS 4 - -struct tegra30_mc { - void __iomem *regs[NUM_MC_REG_BANKS]; - struct device *dev; - u32 ctx[0]; -}; - -static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs) -{ - u32 val = 0; - - if (offs < 0x10) - val = readl(mc->regs[0] + offs); - else if (offs < 0x1f0) - val = readl(mc->regs[1] + offs - 0x3c); - else if (offs < 0x228) - val = readl(mc->regs[2] + offs - 0x200); - else if (offs < 0x400) - val = readl(mc->regs[3] + offs - 0x284); - - return val; -} - -static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs) -{ - if (offs < 0x10) - writel(val, mc->regs[0] + offs); - else if (offs < 0x1f0) - writel(val, mc->regs[1] + offs - 0x3c); - else if (offs < 0x228) - writel(val, mc->regs[2] + offs - 0x200); - else if (offs < 0x400) - writel(val, mc->regs[3] + offs - 0x284); -} - -static const char * const tegra30_mc_client[] = { - "csr_ptcr", - "cbr_display0a", - "cbr_display0ab", - "cbr_display0b", - "cbr_display0bb", - "cbr_display0c", - "cbr_display0cb", - "cbr_display1b", - "cbr_display1bb", - "cbr_eppup", - "cbr_g2pr", - "cbr_g2sr", - "cbr_mpeunifbr", - "cbr_viruv", - "csr_afir", - "csr_avpcarm7r", - "csr_displayhc", - "csr_displayhcb", - "csr_fdcdrd", - "csr_fdcdrd2", - "csr_g2dr", - "csr_hdar", - "csr_host1xdmar", - "csr_host1xr", - "csr_idxsrd", - "csr_idxsrd2", - "csr_mpe_ipred", - "csr_mpeamemrd", - "csr_mpecsrd", - "csr_ppcsahbdmar", - "csr_ppcsahbslvr", - "csr_satar", - "csr_texsrd", - "csr_texsrd2", - "csr_vdebsevr", - "csr_vdember", - "csr_vdemcer", - "csr_vdetper", - "csr_mpcorelpr", - "csr_mpcorer", - "cbw_eppu", - "cbw_eppv", - "cbw_eppy", - "cbw_mpeunifbw", - "cbw_viwsb", - "cbw_viwu", - "cbw_viwv", - "cbw_viwy", - "ccw_g2dw", - "csw_afiw", - "csw_avpcarm7w", - "csw_fdcdwr", - "csw_fdcdwr2", - "csw_hdaw", - "csw_host1xw", - "csw_ispw", - "csw_mpcorelpw", - "csw_mpcorew", - "csw_mpecswr", - "csw_ppcsahbdmaw", - "csw_ppcsahbslvw", - "csw_sataw", - "csw_vdebsevw", - "csw_vdedbgw", - "csw_vdembew", - "csw_vdetpmw", -}; - -static void tegra30_mc_decode(struct tegra30_mc *mc, int n) -{ - u32 err, addr; - const char * const mc_int_err[] = { - "MC_DECERR", - "Unknown", - "MC_SECURITY_ERR", - "MC_ARBITRATION_EMEM", - "MC_SMMU_ERR", - }; - const char * const err_type[] = { - "Unknown", - "Unknown", - "DECERR_EMEM", - "SECURITY_TRUSTZONE", - "SECURITY_CARVEOUT", - "Unknown", - "INVALID_SMMU_PAGE", - "Unknown", - }; - char attr[6]; - int cid, perm, type, idx; - const char *client = "Unknown"; - - idx = n - MC_INT_ERR_SHIFT; - if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) { - dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n", - BIT(n)); - return; - } - - err = mc_readl(mc, MC_ERR_STATUS); - - type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT; - perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >> - MC_ERR_INVALID_SMMU_PAGE_SHIFT; - if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE) - sprintf(attr, "%c-%c-%c", - (perm & BIT(2)) ? 'R' : '-', - (perm & BIT(1)) ? 'W' : '-', - (perm & BIT(0)) ? 'S' : '-'); - else - attr[0] = '\0'; - - cid = err & MC_CLIENT_ID_MASK; - if (cid < ARRAY_SIZE(tegra30_mc_client)) - client = tegra30_mc_client[cid]; - - addr = mc_readl(mc, MC_ERR_ADR); - - dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n", - mc_int_err[idx], err, addr, client, - (err & MC_ERR_SECURITY) ? "secure" : "non-secure", - (err & MC_ERR_RW) ? "write" : "read", - err_type[type], attr); -} - -static const u32 tegra30_mc_ctx[] = { - MC_EMEM_ARB_CFG, - MC_EMEM_ARB_OUTSTANDING_REQ, - MC_EMEM_ARB_TIMING_RCD, - MC_EMEM_ARB_TIMING_RP, - MC_EMEM_ARB_TIMING_RC, - MC_EMEM_ARB_TIMING_RAS, - MC_EMEM_ARB_TIMING_FAW, - MC_EMEM_ARB_TIMING_RRD, - MC_EMEM_ARB_TIMING_RAP2PRE, - MC_EMEM_ARB_TIMING_WAP2PRE, - MC_EMEM_ARB_TIMING_R2R, - MC_EMEM_ARB_TIMING_W2W, - MC_EMEM_ARB_TIMING_R2W, - MC_EMEM_ARB_TIMING_W2R, - MC_EMEM_ARB_DA_TURNS, - MC_EMEM_ARB_DA_COVERS, - MC_EMEM_ARB_MISC0, - MC_EMEM_ARB_MISC1, - MC_EMEM_ARB_RING3_THROTTLE, - MC_EMEM_ARB_OVERRIDE, - MC_INTMASK, -}; - -#ifdef CONFIG_PM -static int tegra30_mc_suspend(struct device *dev) -{ - int i; - struct tegra30_mc *mc = dev_get_drvdata(dev); - - for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) - mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]); - return 0; -} - -static int tegra30_mc_resume(struct device *dev) -{ - int i; - struct tegra30_mc *mc = dev_get_drvdata(dev); - - for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) - mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]); - - mc_writel(mc, 1, MC_TIMING_CONTROL); - /* Read-back to ensure that write reached */ - mc_readl(mc, MC_TIMING_CONTROL); - return 0; -} -#endif - -static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm, - tegra30_mc_suspend, - tegra30_mc_resume, NULL); - -static const struct of_device_id tegra30_mc_of_match[] = { - { .compatible = "nvidia,tegra30-mc", }, - {}, -}; - -static irqreturn_t tegra30_mc_isr(int irq, void *data) -{ - u32 stat, mask, bit; - struct tegra30_mc *mc = data; - - stat = mc_readl(mc, MC_INTSTATUS); - mask = mc_readl(mc, MC_INTMASK); - mask &= stat; - if (!mask) - return IRQ_NONE; - while ((bit = ffs(mask)) != 0) { - tegra30_mc_decode(mc, bit - 1); - mask &= ~BIT(bit - 1); - } - - mc_writel(mc, stat, MC_INTSTATUS); - return IRQ_HANDLED; -} - -static int tegra30_mc_probe(struct platform_device *pdev) -{ - struct resource *irq; - struct tegra30_mc *mc; - size_t bytes; - int err, i; - u32 intmask; - - bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx); - mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL); - if (!mc) - return -ENOMEM; - mc->dev = &pdev->dev; - - for (i = 0; i < ARRAY_SIZE(mc->regs); i++) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - mc->regs[i] = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mc->regs[i])) - return PTR_ERR(mc->regs[i]); - } - - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) - return -ENODEV; - err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr, - IRQF_SHARED, dev_name(&pdev->dev), mc); - if (err) - return -ENODEV; - - platform_set_drvdata(pdev, mc); - - intmask = MC_INT_INVALID_SMMU_PAGE | - MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION; - mc_writel(mc, intmask, MC_INTMASK); - return 0; -} - -static struct platform_driver tegra30_mc_driver = { - .probe = tegra30_mc_probe, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .of_match_table = tegra30_mc_of_match, - .pm = &tegra30_mc_pm, - }, -}; -module_platform_driver(tegra30_mc_driver); - -MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); -MODULE_DESCRIPTION("Tegra30 MC driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 0707fa2c701b..5bdaae15a742 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -1994,6 +1994,7 @@ static struct scsi_host_template mptsas_driver_template = { .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = mptscsih_host_attrs, + .use_blk_tags = 1, }; static int mptsas_get_linkerrors(struct sas_phy *phy) diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index e7dcb2583369..6c9fc11efb87 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -2311,26 +2311,21 @@ mptscsih_slave_destroy(struct scsi_device *sdev) * mptscsih_change_queue_depth - This function will set a devices queue depth * @sdev: per scsi_device pointer * @qdepth: requested queue depth - * @reason: calling context * * Adding support for new 'change_queue_depth' api. */ int -mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) +mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth) { MPT_SCSI_HOST *hd = shost_priv(sdev->host); VirtTarget *vtarget; struct scsi_target *starget; int max_depth; - int tagged; MPT_ADAPTER *ioc = hd->ioc; starget = scsi_target(sdev); vtarget = starget->hostdata; - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (ioc->bus_type == SPI) { if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)) max_depth = 1; @@ -2347,13 +2342,8 @@ mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) if (qdepth > max_depth) qdepth = max_depth; - if (qdepth == 1) - tagged = 0; - else - tagged = MSG_SIMPLE_TAG; - scsi_adjust_queue_depth(sdev, tagged, qdepth); - return sdev->queue_depth; + return scsi_change_queue_depth(sdev, qdepth); } /* @@ -2397,12 +2387,10 @@ mptscsih_slave_configure(struct scsi_device *sdev) ioc->name, vtarget->negoFlags, vtarget->maxOffset, vtarget->minSyncFactor)); - mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH, - SCSI_QDEPTH_DEFAULT); + mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH); dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT - "tagged %d, simple %d, ordered %d\n", - ioc->name,sdev->tagged_supported, sdev->simple_tags, - sdev->ordered_tags)); + "tagged %d, simple %d\n", + ioc->name,sdev->tagged_supported, sdev->simple_tags)); blk_queue_dma_alignment (sdev->request_queue, 512 - 1); diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index e1b1a198a62a..2baeefd9be7a 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -128,8 +128,7 @@ extern int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_F extern int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); -extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason); +extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth); extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id); extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id); extern struct device_attribute *mptscsih_host_attrs[]; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1456ea70bbc7..2e6b7311fabc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -59,6 +59,17 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. +config MFD_ATMEL_HLCDC + tristate "Atmel HLCDC (High-end LCD Controller)" + select MFD_CORE + select REGMAP_MMIO + depends on OF + help + If you say yes here you get support for the HLCDC block. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config MFD_BCM590XX tristate "Broadcom BCM590xx PMUs" select MFD_CORE @@ -74,7 +85,8 @@ config MFD_AXP20X select REGMAP_IRQ depends on I2C=y help - If you say Y here you get support for the X-Powers AXP202 and AXP209. + If you say Y here you get support for the X-Powers AXP202, AXP209 and + AXP288 power management IC (PMIC). This driver include only the core APIs. You have to select individual components like regulators or the PEK (Power Enable Key) under the corresponding menus. @@ -183,6 +195,16 @@ config MFD_DA9063 Additional drivers must be enabled in order to use the functionality of the device. +config MFD_DLN2 + tristate "Diolan DLN2 support" + select MFD_CORE + depends on USB + help + This adds support for Diolan USB-I2C/SPI/GPIO Master Adapter + DLN-2. Additional drivers such as I2C_DLN2, GPIO_DLN2, + etc. must be enabled in order to use the functionality of + the device. + config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) @@ -655,7 +677,6 @@ config MFD_SEC_CORE select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - select REGULATOR help Support for the Samsung Electronics MFD series. This driver provides common support for accessing the device, diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8bd54b1253af..53467e211381 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o +rtsx_pci-objs := rtsx_pcr.o rtsx_gops.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o @@ -157,6 +157,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o +obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o @@ -174,6 +175,7 @@ obj-$(CONFIG_MFD_STW481X) += stw481x.o obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o +obj-$(CONFIG_MFD_DLN2) += dln2.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 8e0dae59844d..94dbcdd2a1ff 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -85,63 +85,6 @@ shutdown: } } -/* - * Use the AB WD to reset the platform. It will perform a hard - * reset instead of a soft reset. Write the reset reason to - * the AB before reset, which can be read upon restart. - */ -void ab8500_restart(char mode, const char *cmd) -{ - struct ab8500_platform_data *plat; - struct ab8500_sysctrl_platform_data *pdata; - u16 reason = 0; - u8 val; - - if (sysctrl_dev == NULL) { - pr_err("%s: sysctrl not initialized\n", __func__); - return; - } - - plat = dev_get_platdata(sysctrl_dev->parent); - pdata = plat->sysctrl; - if (pdata && pdata->reboot_reason_code) - reason = pdata->reboot_reason_code(cmd); - else - pr_warn("[%s] No reboot reason set. Default reason %d\n", - __func__, reason); - - /* - * Disable RTC alarm, just a precaution so that no alarm - * is running when WD reset is executed. - */ - abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC, - RTC_CTRL , &val); - abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, - RTC_CTRL , (val & ~RTC_ALARM_ENABLE)); - - /* - * Android is not using the RTC alarm registers during reboot - * so we borrow them for writing the reason of reset - */ - - /* reason[8 LSB] */ - val = reason & 0xFF; - abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, - AB8500_ALARM_MIN_LOW , val); - - /* reason[8 MSB] */ - val = (reason>>8) & 0xFF; - abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, - AB8500_ALARM_MIN_MID , val); - - /* Setting WD timeout to 0 */ - ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0); - - /* Setting the parameters to AB8500 WD*/ - ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD | - AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD)); -} - static inline bool valid_bank(u8 bank) { return ((bank == AB8500_SYS_CTRL1_BLOCK) || diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 5145d78bf07e..8ef58bcff193 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -75,7 +75,9 @@ static int arizona_spi_probe(struct spi_device *spi) static int arizona_spi_remove(struct spi_device *spi) { struct arizona *arizona = spi_get_drvdata(spi); + arizona_dev_exit(arizona); + return 0; } diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c new file mode 100644 index 000000000000..cfd58f4cc5c3 --- /dev/null +++ b/drivers/mfd/atmel-hlcdc.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014 Free Electrons + * Copyright (C) 2014 Atmel + * + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/mfd/atmel-hlcdc.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4) + +static const struct mfd_cell atmel_hlcdc_cells[] = { + { + .name = "atmel-hlcdc-pwm", + .of_compatible = "atmel,hlcdc-pwm", + }, + { + .name = "atmel-hlcdc-dc", + .of_compatible = "atmel,hlcdc-display-controller", + }, +}; + +static const struct regmap_config atmel_hlcdc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = ATMEL_HLCDC_REG_MAX, +}; + +static int atmel_hlcdc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atmel_hlcdc *hlcdc; + struct resource *res; + void __iomem *regs; + + hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL); + if (!hlcdc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + hlcdc->irq = platform_get_irq(pdev, 0); + if (hlcdc->irq < 0) + return hlcdc->irq; + + hlcdc->periph_clk = devm_clk_get(dev, "periph_clk"); + if (IS_ERR(hlcdc->periph_clk)) { + dev_err(dev, "failed to get peripheral clock\n"); + return PTR_ERR(hlcdc->periph_clk); + } + + hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); + if (IS_ERR(hlcdc->sys_clk)) { + dev_err(dev, "failed to get system clock\n"); + return PTR_ERR(hlcdc->sys_clk); + } + + hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); + if (IS_ERR(hlcdc->slow_clk)) { + dev_err(dev, "failed to get slow clock\n"); + return PTR_ERR(hlcdc->slow_clk); + } + + hlcdc->regmap = devm_regmap_init_mmio(dev, regs, + &atmel_hlcdc_regmap_config); + if (IS_ERR(hlcdc->regmap)) + return PTR_ERR(hlcdc->regmap); + + dev_set_drvdata(dev, hlcdc); + + return mfd_add_devices(dev, -1, atmel_hlcdc_cells, + ARRAY_SIZE(atmel_hlcdc_cells), + NULL, 0, NULL); +} + +static int atmel_hlcdc_remove(struct platform_device *pdev) +{ + mfd_remove_devices(&pdev->dev); + + return 0; +} + +static const struct of_device_id atmel_hlcdc_match[] = { + { .compatible = "atmel,sama5d3-hlcdc" }, + { /* sentinel */ }, +}; + +static struct platform_driver atmel_hlcdc_driver = { + .probe = atmel_hlcdc_probe, + .remove = atmel_hlcdc_remove, + .driver = { + .name = "atmel-hlcdc", + .of_match_table = atmel_hlcdc_match, + }, +}; +module_platform_driver(atmel_hlcdc_driver); + +MODULE_ALIAS("platform:atmel-hlcdc"); +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); +MODULE_DESCRIPTION("Atmel HLCDC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 6231adbb295d..c522ee22b1c0 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -1,9 +1,9 @@ /* - * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209 + * axp20x.c - MFD core driver for the X-Powers' Power Management ICs * - * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC - * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature - * as well as 4 configurable GPIOs. + * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC + * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature + * as well as configurable GPIOs. * * Author: Carlo Caione <carlo@caione.org> * @@ -25,9 +25,16 @@ #include <linux/mfd/core.h> #include <linux/of_device.h> #include <linux/of_irq.h> +#include <linux/acpi.h> #define AXP20X_OFF 0x80 +static const char const *axp20x_model_names[] = { + "AXP202", + "AXP209", + "AXP288", +}; + static const struct regmap_range axp20x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), @@ -47,6 +54,25 @@ static const struct regmap_access_table axp20x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), }; +static const struct regmap_range axp288_writeable_ranges[] = { + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), + regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), +}; + +static const struct regmap_range axp288_volatile_ranges[] = { + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), +}; + +static const struct regmap_access_table axp288_writeable_table = { + .yes_ranges = axp288_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp288_writeable_ranges), +}; + +static const struct regmap_access_table axp288_volatile_table = { + .yes_ranges = axp288_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges), +}; + static struct resource axp20x_pek_resources[] = { { .name = "PEK_DBR", @@ -61,6 +87,39 @@ static struct resource axp20x_pek_resources[] = { }, }; +static struct resource axp288_battery_resources[] = { + { + .start = AXP288_IRQ_QWBTU, + .end = AXP288_IRQ_QWBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WBTU, + .end = AXP288_IRQ_WBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_QWBTO, + .end = AXP288_IRQ_QWBTO, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WBTO, + .end = AXP288_IRQ_WBTO, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WL2, + .end = AXP288_IRQ_WL2, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_WL1, + .end = AXP288_IRQ_WL1, + .flags = IORESOURCE_IRQ, + }, +}; + static const struct regmap_config axp20x_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -70,47 +129,96 @@ static const struct regmap_config axp20x_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -#define AXP20X_IRQ(_irq, _off, _mask) \ - [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } +static const struct regmap_config axp288_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp288_writeable_table, + .volatile_table = &axp288_volatile_table, + .max_register = AXP288_FG_TUNE5, + .cache_type = REGCACHE_RBTREE, +}; + +#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ + [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } static const struct regmap_irq axp20x_regmap_irqs[] = { - AXP20X_IRQ(ACIN_OVER_V, 0, 7), - AXP20X_IRQ(ACIN_PLUGIN, 0, 6), - AXP20X_IRQ(ACIN_REMOVAL, 0, 5), - AXP20X_IRQ(VBUS_OVER_V, 0, 4), - AXP20X_IRQ(VBUS_PLUGIN, 0, 3), - AXP20X_IRQ(VBUS_REMOVAL, 0, 2), - AXP20X_IRQ(VBUS_V_LOW, 0, 1), - AXP20X_IRQ(BATT_PLUGIN, 1, 7), - AXP20X_IRQ(BATT_REMOVAL, 1, 6), - AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5), - AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4), - AXP20X_IRQ(CHARG, 1, 3), - AXP20X_IRQ(CHARG_DONE, 1, 2), - AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1), - AXP20X_IRQ(BATT_TEMP_LOW, 1, 0), - AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7), - AXP20X_IRQ(CHARG_I_LOW, 2, 6), - AXP20X_IRQ(DCDC1_V_LONG, 2, 5), - AXP20X_IRQ(DCDC2_V_LONG, 2, 4), - AXP20X_IRQ(DCDC3_V_LONG, 2, 3), - AXP20X_IRQ(PEK_SHORT, 2, 1), - AXP20X_IRQ(PEK_LONG, 2, 0), - AXP20X_IRQ(N_OE_PWR_ON, 3, 7), - AXP20X_IRQ(N_OE_PWR_OFF, 3, 6), - AXP20X_IRQ(VBUS_VALID, 3, 5), - AXP20X_IRQ(VBUS_NOT_VALID, 3, 4), - AXP20X_IRQ(VBUS_SESS_VALID, 3, 3), - AXP20X_IRQ(VBUS_SESS_END, 3, 2), - AXP20X_IRQ(LOW_PWR_LVL1, 3, 1), - AXP20X_IRQ(LOW_PWR_LVL2, 3, 0), - AXP20X_IRQ(TIMER, 4, 7), - AXP20X_IRQ(PEK_RIS_EDGE, 4, 6), - AXP20X_IRQ(PEK_FAL_EDGE, 4, 5), - AXP20X_IRQ(GPIO3_INPUT, 4, 3), - AXP20X_IRQ(GPIO2_INPUT, 4, 2), - AXP20X_IRQ(GPIO1_INPUT, 4, 1), - AXP20X_IRQ(GPIO0_INPUT, 4, 0), + INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP20X, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP20X, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP20X, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP20X, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP20X, VBUS_V_LOW, 0, 1), + INIT_REGMAP_IRQ(AXP20X, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP20X, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP20X, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP20X, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP20X, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP20X, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_HIGH, 1, 1), + INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_LOW, 1, 0), + INIT_REGMAP_IRQ(AXP20X, DIE_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP20X, CHARG_I_LOW, 2, 6), + INIT_REGMAP_IRQ(AXP20X, DCDC1_V_LONG, 2, 5), + INIT_REGMAP_IRQ(AXP20X, DCDC2_V_LONG, 2, 4), + INIT_REGMAP_IRQ(AXP20X, DCDC3_V_LONG, 2, 3), + INIT_REGMAP_IRQ(AXP20X, PEK_SHORT, 2, 1), + INIT_REGMAP_IRQ(AXP20X, PEK_LONG, 2, 0), + INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_ON, 3, 7), + INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_OFF, 3, 6), + INIT_REGMAP_IRQ(AXP20X, VBUS_VALID, 3, 5), + INIT_REGMAP_IRQ(AXP20X, VBUS_NOT_VALID, 3, 4), + INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_VALID, 3, 3), + INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_END, 3, 2), + INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL1, 3, 1), + INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL2, 3, 0), + INIT_REGMAP_IRQ(AXP20X, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP20X, PEK_RIS_EDGE, 4, 6), + INIT_REGMAP_IRQ(AXP20X, PEK_FAL_EDGE, 4, 5), + INIT_REGMAP_IRQ(AXP20X, GPIO3_INPUT, 4, 3), + INIT_REGMAP_IRQ(AXP20X, GPIO2_INPUT, 4, 2), + INIT_REGMAP_IRQ(AXP20X, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0), +}; + +/* some IRQs are compatible with axp20x models */ +static const struct regmap_irq axp288_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2), + INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3), + INIT_REGMAP_IRQ(AXP288, OV, 0, 4), + + INIT_REGMAP_IRQ(AXP288, DONE, 1, 2), + INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3), + INIT_REGMAP_IRQ(AXP288, SAFE_QUIT, 1, 4), + INIT_REGMAP_IRQ(AXP288, SAFE_ENTER, 1, 5), + INIT_REGMAP_IRQ(AXP288, ABSENT, 1, 6), + INIT_REGMAP_IRQ(AXP288, APPEND, 1, 7), + + INIT_REGMAP_IRQ(AXP288, QWBTU, 2, 0), + INIT_REGMAP_IRQ(AXP288, WBTU, 2, 1), + INIT_REGMAP_IRQ(AXP288, QWBTO, 2, 2), + INIT_REGMAP_IRQ(AXP288, WBTO, 2, 3), + INIT_REGMAP_IRQ(AXP288, QCBTU, 2, 4), + INIT_REGMAP_IRQ(AXP288, CBTU, 2, 5), + INIT_REGMAP_IRQ(AXP288, QCBTO, 2, 6), + INIT_REGMAP_IRQ(AXP288, CBTO, 2, 7), + + INIT_REGMAP_IRQ(AXP288, WL2, 3, 0), + INIT_REGMAP_IRQ(AXP288, WL1, 3, 1), + INIT_REGMAP_IRQ(AXP288, GPADC, 3, 2), + INIT_REGMAP_IRQ(AXP288, OT, 3, 7), + + INIT_REGMAP_IRQ(AXP288, GPIO0, 4, 0), + INIT_REGMAP_IRQ(AXP288, GPIO1, 4, 1), + INIT_REGMAP_IRQ(AXP288, POKO, 4, 2), + INIT_REGMAP_IRQ(AXP288, POKL, 4, 3), + INIT_REGMAP_IRQ(AXP288, POKS, 4, 4), + INIT_REGMAP_IRQ(AXP288, POKN, 4, 5), + INIT_REGMAP_IRQ(AXP288, POKP, 4, 6), + INIT_REGMAP_IRQ(AXP288, TIMER, 4, 7), + + INIT_REGMAP_IRQ(AXP288, MV_CHNG, 5, 0), + INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; static const struct of_device_id axp20x_of_match[] = { @@ -128,16 +236,39 @@ static const struct i2c_device_id axp20x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); +static const struct acpi_device_id axp20x_acpi_match[] = { + { + .id = "INT33F4", + .driver_data = AXP288_ID, + }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match); + static const struct regmap_irq_chip axp20x_regmap_irq_chip = { .name = "axp20x_irq_chip", .status_base = AXP20X_IRQ1_STATE, .ack_base = AXP20X_IRQ1_STATE, .mask_base = AXP20X_IRQ1_EN, - .num_regs = 5, + .mask_invert = true, + .init_ack_masked = true, .irqs = axp20x_regmap_irqs, .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), + .num_regs = 5, + +}; + +static const struct regmap_irq_chip axp288_regmap_irq_chip = { + .name = "axp288_irq_chip", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, .mask_invert = true, .init_ack_masked = true, + .irqs = axp288_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp288_regmap_irqs), + .num_regs = 6, + }; static struct mfd_cell axp20x_cells[] = { @@ -150,36 +281,155 @@ static struct mfd_cell axp20x_cells[] = { }, }; +static struct resource axp288_adc_resources[] = { + { + .name = "GPADC", + .start = AXP288_IRQ_GPADC, + .end = AXP288_IRQ_GPADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource axp288_charger_resources[] = { + { + .start = AXP288_IRQ_OV, + .end = AXP288_IRQ_OV, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_DONE, + .end = AXP288_IRQ_DONE, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_CHARGING, + .end = AXP288_IRQ_CHARGING, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_SAFE_QUIT, + .end = AXP288_IRQ_SAFE_QUIT, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_SAFE_ENTER, + .end = AXP288_IRQ_SAFE_ENTER, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_QCBTU, + .end = AXP288_IRQ_QCBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_CBTU, + .end = AXP288_IRQ_CBTU, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_QCBTO, + .end = AXP288_IRQ_QCBTO, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_CBTO, + .end = AXP288_IRQ_CBTO, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell axp288_cells[] = { + { + .name = "axp288_adc", + .num_resources = ARRAY_SIZE(axp288_adc_resources), + .resources = axp288_adc_resources, + }, + { + .name = "axp288_charger", + .num_resources = ARRAY_SIZE(axp288_charger_resources), + .resources = axp288_charger_resources, + }, + { + .name = "axp288_battery", + .num_resources = ARRAY_SIZE(axp288_battery_resources), + .resources = axp288_battery_resources, + }, +}; + static struct axp20x_dev *axp20x_pm_power_off; static void axp20x_power_off(void) { + if (axp20x_pm_power_off->variant == AXP288_ID) + return; + regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, AXP20X_OFF); } +static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) +{ + const struct acpi_device_id *acpi_id; + const struct of_device_id *of_id; + + if (dev->of_node) { + of_id = of_match_device(axp20x_of_match, dev); + if (!of_id) { + dev_err(dev, "Unable to match OF ID\n"); + return -ENODEV; + } + axp20x->variant = (long) of_id->data; + } else { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id || !acpi_id->driver_data) { + dev_err(dev, "Unable to match ACPI ID and data\n"); + return -ENODEV; + } + axp20x->variant = (long) acpi_id->driver_data; + } + + switch (axp20x->variant) { + case AXP202_ID: + case AXP209_ID: + axp20x->nr_cells = ARRAY_SIZE(axp20x_cells); + axp20x->cells = axp20x_cells; + axp20x->regmap_cfg = &axp20x_regmap_config; + axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; + break; + case AXP288_ID: + axp20x->cells = axp288_cells; + axp20x->nr_cells = ARRAY_SIZE(axp288_cells); + axp20x->regmap_cfg = &axp288_regmap_config; + axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; + break; + default: + dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant); + return -EINVAL; + } + dev_info(dev, "AXP20x variant %s found\n", + axp20x_model_names[axp20x->variant]); + + return 0; +} + static int axp20x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct axp20x_dev *axp20x; - const struct of_device_id *of_id; int ret; axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); if (!axp20x) return -ENOMEM; - of_id = of_match_device(axp20x_of_match, &i2c->dev); - if (!of_id) { - dev_err(&i2c->dev, "Unable to setup AXP20X data\n"); - return -ENODEV; - } - axp20x->variant = (long) of_id->data; + ret = axp20x_match_device(axp20x, &i2c->dev); + if (ret) + return ret; axp20x->i2c_client = i2c; axp20x->dev = &i2c->dev; dev_set_drvdata(axp20x->dev, axp20x); - axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config); + axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); if (IS_ERR(axp20x->regmap)) { ret = PTR_ERR(axp20x->regmap); dev_err(&i2c->dev, "regmap init failed: %d\n", ret); @@ -188,15 +438,15 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, IRQF_ONESHOT | IRQF_SHARED, -1, - &axp20x_regmap_irq_chip, + axp20x->regmap_irq_chip, &axp20x->regmap_irqc); if (ret) { dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); return ret; } - ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells, - ARRAY_SIZE(axp20x_cells), NULL, 0, NULL); + ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells, + axp20x->nr_cells, NULL, 0, NULL); if (ret) { dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); @@ -234,6 +484,7 @@ static struct i2c_driver axp20x_i2c_driver = { .name = "axp20x", .owner = THIS_MODULE, .of_match_table = of_match_ptr(axp20x_of_match), + .acpi_match_table = ACPI_PTR(axp20x_acpi_match), }, .probe = axp20x_i2c_probe, .remove = axp20x_i2c_remove, diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index 93db8bb8c8f0..f38bc98a3c57 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -118,7 +118,7 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) da9063->irq_base = pdata->irq_base; } else { da9063->flags = 0; - da9063->irq_base = 0; + da9063->irq_base = -1; } da9063->chip_irq = irq; @@ -168,6 +168,8 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) return ret; } + da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq); + ret = mfd_add_devices(da9063->dev, -1, da9063_devs, ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, NULL); diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 193cf168ba84..a8204730f01c 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -3150,23 +3150,28 @@ static int db8500_prcmu_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu"); if (!res) { dev_err(&pdev->dev, "no prcmu memory region provided\n"); - return -ENOENT; + return -EINVAL; } prcmu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!prcmu_base) { dev_err(&pdev->dev, "failed to ioremap prcmu register memory\n"); - return -ENOENT; + return -ENOMEM; } init_prcm_registers(); dbx500_fw_version_init(pdev, pdata->version_offset); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); if (!res) { dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); - return -ENOENT; + return -EINVAL; } tcdm_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!tcdm_base) { + dev_err(&pdev->dev, + "failed to ioremap prcmu-tcdm register memory\n"); + return -ENOMEM; + } /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); @@ -3174,15 +3179,14 @@ static int db8500_prcmu_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "no prcmu irq provided\n"); - return -ENOENT; + return irq; } err = request_threaded_irq(irq, prcmu_irq_handler, prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); if (err < 0) { pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n"); - err = -EBUSY; - goto no_irq_return; + return err; } db8500_irq_init(np); @@ -3206,7 +3210,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) if (err) { mfd_remove_devices(&pdev->dev); pr_err("prcmu: Failed to add subdevices\n"); - goto no_irq_return; + return err; } } @@ -3214,12 +3218,10 @@ static int db8500_prcmu_probe(struct platform_device *pdev) if (err) { mfd_remove_devices(&pdev->dev); pr_err("prcmu: Failed to add ab8500 subdevice\n"); - goto no_irq_return; + return err; } pr_info("DB8500 PRCMU initialized\n"); - -no_irq_return: return err; } static const struct of_device_id db8500_prcmu_match[] = { diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c new file mode 100644 index 000000000000..6d49685d4ee4 --- /dev/null +++ b/drivers/mfd/dln2.c @@ -0,0 +1,781 @@ +/* + * Driver for the Diolan DLN-2 USB adapter + * + * Copyright (c) 2014 Intel Corporation + * + * Derived from: + * i2c-diolan-u2c.c + * Copyright (c) 2010-2011 Ericsson AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/dln2.h> +#include <linux/rculist.h> + +struct dln2_header { + __le16 size; + __le16 id; + __le16 echo; + __le16 handle; +}; + +struct dln2_response { + struct dln2_header hdr; + __le16 result; +}; + +#define DLN2_GENERIC_MODULE_ID 0x00 +#define DLN2_GENERIC_CMD(cmd) DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID) +#define CMD_GET_DEVICE_VER DLN2_GENERIC_CMD(0x30) +#define CMD_GET_DEVICE_SN DLN2_GENERIC_CMD(0x31) + +#define DLN2_HW_ID 0x200 +#define DLN2_USB_TIMEOUT 200 /* in ms */ +#define DLN2_MAX_RX_SLOTS 16 +#define DLN2_MAX_URBS 16 +#define DLN2_RX_BUF_SIZE 512 + +enum dln2_handle { + DLN2_HANDLE_EVENT = 0, /* don't change, hardware defined */ + DLN2_HANDLE_CTRL, + DLN2_HANDLE_GPIO, + DLN2_HANDLE_I2C, + DLN2_HANDLE_SPI, + DLN2_HANDLES +}; + +/* + * Receive context used between the receive demultiplexer and the transfer + * routine. While sending a request the transfer routine will look for a free + * receive context and use it to wait for a response and to receive the URB and + * thus the response data. + */ +struct dln2_rx_context { + /* completion used to wait for a response */ + struct completion done; + + /* if non-NULL the URB contains the response */ + struct urb *urb; + + /* if true then this context is used to wait for a response */ + bool in_use; +}; + +/* + * Receive contexts for a particular DLN2 module (i2c, gpio, etc.). We use the + * handle header field to identify the module in dln2_dev.mod_rx_slots and then + * the echo header field to index the slots field and find the receive context + * for a particular request. + */ +struct dln2_mod_rx_slots { + /* RX slots bitmap */ + DECLARE_BITMAP(bmap, DLN2_MAX_RX_SLOTS); + + /* used to wait for a free RX slot */ + wait_queue_head_t wq; + + /* used to wait for an RX operation to complete */ + struct dln2_rx_context slots[DLN2_MAX_RX_SLOTS]; + + /* avoid races between alloc/free_rx_slot and dln2_rx_transfer */ + spinlock_t lock; +}; + +struct dln2_dev { + struct usb_device *usb_dev; + struct usb_interface *interface; + u8 ep_in; + u8 ep_out; + + struct urb *rx_urb[DLN2_MAX_URBS]; + void *rx_buf[DLN2_MAX_URBS]; + + struct dln2_mod_rx_slots mod_rx_slots[DLN2_HANDLES]; + + struct list_head event_cb_list; + spinlock_t event_cb_lock; + + bool disconnect; + int active_transfers; + wait_queue_head_t disconnect_wq; + spinlock_t disconnect_lock; +}; + +struct dln2_event_cb_entry { + struct list_head list; + u16 id; + struct platform_device *pdev; + dln2_event_cb_t callback; +}; + +int dln2_register_event_cb(struct platform_device *pdev, u16 id, + dln2_event_cb_t event_cb) +{ + struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); + struct dln2_event_cb_entry *i, *entry; + unsigned long flags; + int ret = 0; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->id = id; + entry->callback = event_cb; + entry->pdev = pdev; + + spin_lock_irqsave(&dln2->event_cb_lock, flags); + + list_for_each_entry(i, &dln2->event_cb_list, list) { + if (i->id == id) { + ret = -EBUSY; + break; + } + } + + if (!ret) + list_add_rcu(&entry->list, &dln2->event_cb_list); + + spin_unlock_irqrestore(&dln2->event_cb_lock, flags); + + if (ret) + kfree(entry); + + return ret; +} +EXPORT_SYMBOL(dln2_register_event_cb); + +void dln2_unregister_event_cb(struct platform_device *pdev, u16 id) +{ + struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); + struct dln2_event_cb_entry *i; + unsigned long flags; + bool found = false; + + spin_lock_irqsave(&dln2->event_cb_lock, flags); + + list_for_each_entry(i, &dln2->event_cb_list, list) { + if (i->id == id) { + list_del_rcu(&i->list); + found = true; + break; + } + } + + spin_unlock_irqrestore(&dln2->event_cb_lock, flags); + + if (found) { + synchronize_rcu(); + kfree(i); + } +} +EXPORT_SYMBOL(dln2_unregister_event_cb); + +/* + * Returns true if a valid transfer slot is found. In this case the URB must not + * be resubmitted immediately in dln2_rx as we need the data when dln2_transfer + * is woke up. It will be resubmitted there. + */ +static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb, + u16 handle, u16 rx_slot) +{ + struct device *dev = &dln2->interface->dev; + struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; + struct dln2_rx_context *rxc; + bool valid_slot = false; + + if (rx_slot >= DLN2_MAX_RX_SLOTS) + goto out; + + rxc = &rxs->slots[rx_slot]; + + /* + * No need to disable interrupts as this lock is not taken in interrupt + * context elsewhere in this driver. This function (or its callers) are + * also not exported to other modules. + */ + spin_lock(&rxs->lock); + if (rxc->in_use && !rxc->urb) { + rxc->urb = urb; + complete(&rxc->done); + valid_slot = true; + } + spin_unlock(&rxs->lock); + +out: + if (!valid_slot) + dev_warn(dev, "bad/late response %d/%d\n", handle, rx_slot); + + return valid_slot; +} + +static void dln2_run_event_callbacks(struct dln2_dev *dln2, u16 id, u16 echo, + void *data, int len) +{ + struct dln2_event_cb_entry *i; + + rcu_read_lock(); + + list_for_each_entry_rcu(i, &dln2->event_cb_list, list) { + if (i->id == id) { + i->callback(i->pdev, echo, data, len); + break; + } + } + + rcu_read_unlock(); +} + +static void dln2_rx(struct urb *urb) +{ + struct dln2_dev *dln2 = urb->context; + struct dln2_header *hdr = urb->transfer_buffer; + struct device *dev = &dln2->interface->dev; + u16 id, echo, handle, size; + u8 *data; + int len; + int err; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EPIPE: + /* this urb is terminated, clean up */ + dev_dbg(dev, "urb shutting down with status %d\n", urb->status); + return; + default: + dev_dbg(dev, "nonzero urb status received %d\n", urb->status); + goto out; + } + + if (urb->actual_length < sizeof(struct dln2_header)) { + dev_err(dev, "short response: %d\n", urb->actual_length); + goto out; + } + + handle = le16_to_cpu(hdr->handle); + id = le16_to_cpu(hdr->id); + echo = le16_to_cpu(hdr->echo); + size = le16_to_cpu(hdr->size); + + if (size != urb->actual_length) { + dev_err(dev, "size mismatch: handle %x cmd %x echo %x size %d actual %d\n", + handle, id, echo, size, urb->actual_length); + goto out; + } + + if (handle >= DLN2_HANDLES) { + dev_warn(dev, "invalid handle %d\n", handle); + goto out; + } + + data = urb->transfer_buffer + sizeof(struct dln2_header); + len = urb->actual_length - sizeof(struct dln2_header); + + if (handle == DLN2_HANDLE_EVENT) { + dln2_run_event_callbacks(dln2, id, echo, data, len); + } else { + /* URB will be re-submitted in _dln2_transfer (free_rx_slot) */ + if (dln2_transfer_complete(dln2, urb, handle, echo)) + return; + } + +out: + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) + dev_err(dev, "failed to resubmit RX URB: %d\n", err); +} + +static void *dln2_prep_buf(u16 handle, u16 cmd, u16 echo, const void *obuf, + int *obuf_len, gfp_t gfp) +{ + int len; + void *buf; + struct dln2_header *hdr; + + len = *obuf_len + sizeof(*hdr); + buf = kmalloc(len, gfp); + if (!buf) + return NULL; + + hdr = (struct dln2_header *)buf; + hdr->id = cpu_to_le16(cmd); + hdr->size = cpu_to_le16(len); + hdr->echo = cpu_to_le16(echo); + hdr->handle = cpu_to_le16(handle); + + memcpy(buf + sizeof(*hdr), obuf, *obuf_len); + + *obuf_len = len; + + return buf; +} + +static int dln2_send_wait(struct dln2_dev *dln2, u16 handle, u16 cmd, u16 echo, + const void *obuf, int obuf_len) +{ + int ret = 0; + int len = obuf_len; + void *buf; + int actual; + + buf = dln2_prep_buf(handle, cmd, echo, obuf, &len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_bulk_msg(dln2->usb_dev, + usb_sndbulkpipe(dln2->usb_dev, dln2->ep_out), + buf, len, &actual, DLN2_USB_TIMEOUT); + + kfree(buf); + + return ret; +} + +static bool find_free_slot(struct dln2_dev *dln2, u16 handle, int *slot) +{ + struct dln2_mod_rx_slots *rxs; + unsigned long flags; + + if (dln2->disconnect) { + *slot = -ENODEV; + return true; + } + + rxs = &dln2->mod_rx_slots[handle]; + + spin_lock_irqsave(&rxs->lock, flags); + + *slot = find_first_zero_bit(rxs->bmap, DLN2_MAX_RX_SLOTS); + + if (*slot < DLN2_MAX_RX_SLOTS) { + struct dln2_rx_context *rxc = &rxs->slots[*slot]; + + set_bit(*slot, rxs->bmap); + rxc->in_use = true; + } + + spin_unlock_irqrestore(&rxs->lock, flags); + + return *slot < DLN2_MAX_RX_SLOTS; +} + +static int alloc_rx_slot(struct dln2_dev *dln2, u16 handle) +{ + int ret; + int slot; + + /* + * No need to timeout here, the wait is bounded by the timeout in + * _dln2_transfer. + */ + ret = wait_event_interruptible(dln2->mod_rx_slots[handle].wq, + find_free_slot(dln2, handle, &slot)); + if (ret < 0) + return ret; + + return slot; +} + +static void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot) +{ + struct dln2_mod_rx_slots *rxs; + struct urb *urb = NULL; + unsigned long flags; + struct dln2_rx_context *rxc; + + rxs = &dln2->mod_rx_slots[handle]; + + spin_lock_irqsave(&rxs->lock, flags); + + clear_bit(slot, rxs->bmap); + + rxc = &rxs->slots[slot]; + rxc->in_use = false; + urb = rxc->urb; + rxc->urb = NULL; + reinit_completion(&rxc->done); + + spin_unlock_irqrestore(&rxs->lock, flags); + + if (urb) { + int err; + struct device *dev = &dln2->interface->dev; + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) + dev_err(dev, "failed to resubmit RX URB: %d\n", err); + } + + wake_up_interruptible(&rxs->wq); +} + +static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, + const void *obuf, unsigned obuf_len, + void *ibuf, unsigned *ibuf_len) +{ + int ret = 0; + int rx_slot; + struct dln2_response *rsp; + struct dln2_rx_context *rxc; + struct device *dev = &dln2->interface->dev; + const unsigned long timeout = DLN2_USB_TIMEOUT * HZ / 1000; + struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; + int size; + + spin_lock(&dln2->disconnect_lock); + if (!dln2->disconnect) + dln2->active_transfers++; + else + ret = -ENODEV; + spin_unlock(&dln2->disconnect_lock); + + if (ret) + return ret; + + rx_slot = alloc_rx_slot(dln2, handle); + if (rx_slot < 0) { + ret = rx_slot; + goto out_decr; + } + + ret = dln2_send_wait(dln2, handle, cmd, rx_slot, obuf, obuf_len); + if (ret < 0) { + dev_err(dev, "USB write failed: %d\n", ret); + goto out_free_rx_slot; + } + + rxc = &rxs->slots[rx_slot]; + + ret = wait_for_completion_interruptible_timeout(&rxc->done, timeout); + if (ret <= 0) { + if (!ret) + ret = -ETIMEDOUT; + goto out_free_rx_slot; + } else { + ret = 0; + } + + if (dln2->disconnect) { + ret = -ENODEV; + goto out_free_rx_slot; + } + + /* if we got here we know that the response header has been checked */ + rsp = rxc->urb->transfer_buffer; + size = le16_to_cpu(rsp->hdr.size); + + if (size < sizeof(*rsp)) { + ret = -EPROTO; + goto out_free_rx_slot; + } + + if (le16_to_cpu(rsp->result) > 0x80) { + dev_dbg(dev, "%d received response with error %d\n", + handle, le16_to_cpu(rsp->result)); + ret = -EREMOTEIO; + goto out_free_rx_slot; + } + + if (!ibuf) + goto out_free_rx_slot; + + if (*ibuf_len > size - sizeof(*rsp)) + *ibuf_len = size - sizeof(*rsp); + + memcpy(ibuf, rsp + 1, *ibuf_len); + +out_free_rx_slot: + free_rx_slot(dln2, handle, rx_slot); +out_decr: + spin_lock(&dln2->disconnect_lock); + dln2->active_transfers--; + spin_unlock(&dln2->disconnect_lock); + if (dln2->disconnect) + wake_up(&dln2->disconnect_wq); + + return ret; +} + +int dln2_transfer(struct platform_device *pdev, u16 cmd, + const void *obuf, unsigned obuf_len, + void *ibuf, unsigned *ibuf_len) +{ + struct dln2_platform_data *dln2_pdata; + struct dln2_dev *dln2; + u16 handle; + + dln2 = dev_get_drvdata(pdev->dev.parent); + dln2_pdata = dev_get_platdata(&pdev->dev); + handle = dln2_pdata->handle; + + return _dln2_transfer(dln2, handle, cmd, obuf, obuf_len, ibuf, + ibuf_len); +} +EXPORT_SYMBOL(dln2_transfer); + +static int dln2_check_hw(struct dln2_dev *dln2) +{ + int ret; + __le32 hw_type; + int len = sizeof(hw_type); + + ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_VER, + NULL, 0, &hw_type, &len); + if (ret < 0) + return ret; + if (len < sizeof(hw_type)) + return -EREMOTEIO; + + if (le32_to_cpu(hw_type) != DLN2_HW_ID) { + dev_err(&dln2->interface->dev, "Device ID 0x%x not supported\n", + le32_to_cpu(hw_type)); + return -ENODEV; + } + + return 0; +} + +static int dln2_print_serialno(struct dln2_dev *dln2) +{ + int ret; + __le32 serial_no; + int len = sizeof(serial_no); + struct device *dev = &dln2->interface->dev; + + ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_SN, NULL, 0, + &serial_no, &len); + if (ret < 0) + return ret; + if (len < sizeof(serial_no)) + return -EREMOTEIO; + + dev_info(dev, "Diolan DLN2 serial %u\n", le32_to_cpu(serial_no)); + + return 0; +} + +static int dln2_hw_init(struct dln2_dev *dln2) +{ + int ret; + + ret = dln2_check_hw(dln2); + if (ret < 0) + return ret; + + return dln2_print_serialno(dln2); +} + +static void dln2_free_rx_urbs(struct dln2_dev *dln2) +{ + int i; + + for (i = 0; i < DLN2_MAX_URBS; i++) { + usb_kill_urb(dln2->rx_urb[i]); + usb_free_urb(dln2->rx_urb[i]); + kfree(dln2->rx_buf[i]); + } +} + +static void dln2_free(struct dln2_dev *dln2) +{ + dln2_free_rx_urbs(dln2); + usb_put_dev(dln2->usb_dev); + kfree(dln2); +} + +static int dln2_setup_rx_urbs(struct dln2_dev *dln2, + struct usb_host_interface *hostif) +{ + int i; + int ret; + const int rx_max_size = DLN2_RX_BUF_SIZE; + struct device *dev = &dln2->interface->dev; + + for (i = 0; i < DLN2_MAX_URBS; i++) { + dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); + if (!dln2->rx_buf[i]) + return -ENOMEM; + + dln2->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dln2->rx_urb[i]) + return -ENOMEM; + + usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev, + usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in), + dln2->rx_buf[i], rx_max_size, dln2_rx, dln2); + + ret = usb_submit_urb(dln2->rx_urb[i], GFP_KERNEL); + if (ret < 0) { + dev_err(dev, "failed to submit RX URB: %d\n", ret); + return ret; + } + } + + return 0; +} + +static struct dln2_platform_data dln2_pdata_gpio = { + .handle = DLN2_HANDLE_GPIO, +}; + +/* Only one I2C port seems to be supported on current hardware */ +static struct dln2_platform_data dln2_pdata_i2c = { + .handle = DLN2_HANDLE_I2C, + .port = 0, +}; + +/* Only one SPI port supported */ +static struct dln2_platform_data dln2_pdata_spi = { + .handle = DLN2_HANDLE_SPI, + .port = 0, +}; + +static const struct mfd_cell dln2_devs[] = { + { + .name = "dln2-gpio", + .platform_data = &dln2_pdata_gpio, + .pdata_size = sizeof(struct dln2_platform_data), + }, + { + .name = "dln2-i2c", + .platform_data = &dln2_pdata_i2c, + .pdata_size = sizeof(struct dln2_platform_data), + }, + { + .name = "dln2-spi", + .platform_data = &dln2_pdata_spi, + .pdata_size = sizeof(struct dln2_platform_data), + }, +}; + +static void dln2_disconnect(struct usb_interface *interface) +{ + struct dln2_dev *dln2 = usb_get_intfdata(interface); + int i, j; + + /* don't allow starting new transfers */ + spin_lock(&dln2->disconnect_lock); + dln2->disconnect = true; + spin_unlock(&dln2->disconnect_lock); + + /* cancel in progress transfers */ + for (i = 0; i < DLN2_HANDLES; i++) { + struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[i]; + unsigned long flags; + + spin_lock_irqsave(&rxs->lock, flags); + + /* cancel all response waiters */ + for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) { + struct dln2_rx_context *rxc = &rxs->slots[j]; + + if (rxc->in_use) + complete(&rxc->done); + } + + spin_unlock_irqrestore(&rxs->lock, flags); + } + + /* wait for transfers to end */ + wait_event(dln2->disconnect_wq, !dln2->active_transfers); + + mfd_remove_devices(&interface->dev); + + dln2_free(dln2); +} + +static int dln2_probe(struct usb_interface *interface, + const struct usb_device_id *usb_id) +{ + struct usb_host_interface *hostif = interface->cur_altsetting; + struct device *dev = &interface->dev; + struct dln2_dev *dln2; + int ret; + int i, j; + + if (hostif->desc.bInterfaceNumber != 0 || + hostif->desc.bNumEndpoints < 2) + return -ENODEV; + + dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress; + dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress; + dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dln2->interface = interface; + usb_set_intfdata(interface, dln2); + init_waitqueue_head(&dln2->disconnect_wq); + + for (i = 0; i < DLN2_HANDLES; i++) { + init_waitqueue_head(&dln2->mod_rx_slots[i].wq); + spin_lock_init(&dln2->mod_rx_slots[i].lock); + for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) + init_completion(&dln2->mod_rx_slots[i].slots[j].done); + } + + spin_lock_init(&dln2->event_cb_lock); + spin_lock_init(&dln2->disconnect_lock); + INIT_LIST_HEAD(&dln2->event_cb_list); + + ret = dln2_setup_rx_urbs(dln2, hostif); + if (ret) + goto out_cleanup; + + ret = dln2_hw_init(dln2); + if (ret < 0) { + dev_err(dev, "failed to initialize hardware\n"); + goto out_cleanup; + } + + ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs)); + if (ret != 0) { + dev_err(dev, "failed to add mfd devices to core\n"); + goto out_cleanup; + } + + return 0; + +out_cleanup: + dln2_free(dln2); + + return ret; +} + +static const struct usb_device_id dln2_table[] = { + { USB_DEVICE(0xa257, 0x2013) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dln2_table); + +static struct usb_driver dln2_driver = { + .name = "dln2", + .probe = dln2_probe, + .disconnect = dln2_disconnect, + .id_table = dln2_table, +}; + +module_usb_driver(dln2_driver); + +MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); +MODULE_DESCRIPTION("Core driver for the Diolan DLN2 interface adapter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index c980da479a35..5c38df35a84d 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -193,11 +193,7 @@ static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; } - ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); - if (ret) - mfd_remove_devices(&dev->dev); - - return ret; + return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); } static void lpc_sch_remove(struct pci_dev *dev) diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index de96b7fb1f6d..3bf8def82f1e 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -1,7 +1,7 @@ /* * max14577.c - mfd core driver for the Maxim 14577/77836 * - * Copyright (C) 2014 Samsung Electrnoics + * Copyright (C) 2014 Samsung Electronics * Chanwoo Choi <cw00.choi@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com> * diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index 711773e8e64b..a159593e27a0 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -43,9 +43,15 @@ static const struct mfd_cell max77693_devs[] = { { .name = "max77693-pmic", }, - { .name = "max77693-charger", }, + { + .name = "max77693-charger", + .of_compatible = "maxim,max77693-charger", + }, { .name = "max77693-muic", }, - { .name = "max77693-haptic", }, + { + .name = "max77693-haptic", + .of_compatible = "maxim,max77693-haptic", + }, { .name = "max77693-flash", .of_compatible = "maxim,max77693-flash", @@ -147,6 +153,12 @@ static const struct regmap_irq_chip max77693_muic_irq_chip = { .num_irqs = ARRAY_SIZE(max77693_muic_irqs), }; +static const struct regmap_config max77693_regmap_haptic_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77693_HAPTIC_REG_END, +}; + static int max77693_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -196,6 +208,15 @@ static int max77693_i2c_probe(struct i2c_client *i2c, } i2c_set_clientdata(max77693->haptic, max77693); + max77693->regmap_haptic = devm_regmap_init_i2c(max77693->haptic, + &max77693_regmap_haptic_config); + if (IS_ERR(max77693->regmap_haptic)) { + ret = PTR_ERR(max77693->regmap_haptic); + dev_err(max77693->dev, + "failed to initialize haptic register map: %d\n", ret); + goto err_regmap; + } + /* * Initialize register map for MUIC device because use regmap-muic * instance of MUIC device when irq of max77693 is initialized @@ -207,7 +228,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(max77693->regmap_muic); dev_err(max77693->dev, "failed to allocate register map: %d\n", ret); - goto err_regmap_muic; + goto err_regmap; } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, @@ -217,7 +238,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, &max77693->irq_data_led); if (ret) { dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); - goto err_regmap_muic; + goto err_regmap; } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, @@ -280,7 +301,7 @@ err_irq_charger: regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys); err_irq_topsys: regmap_del_irq_chip(max77693->irq, max77693->irq_data_led); -err_regmap_muic: +err_regmap: i2c_unregister_device(max77693->haptic); err_i2c_haptic: i2c_unregister_device(max77693->muic); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f3338fe9d069..2a87f69be53d 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -125,9 +125,15 @@ static int mfd_add_device(struct device *parent, int id, struct platform_device *pdev; struct device_node *np = NULL; int ret = -ENOMEM; + int platform_id; int r; - pdev = platform_device_alloc(cell->name, id + cell->id); + if (id < 0) + platform_id = id; + else + platform_id = id + cell->id; + + pdev = platform_device_alloc(cell->name, platform_id); if (!pdev) goto fail_alloc; diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index 9c8eec80ceed..32407404d838 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -130,6 +130,12 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) static int rts5227_optimize_phy(struct rtsx_pcr *pcr) { + int err; + + err = rtsx_gops_pm_reset(pcr); + if (err < 0) + return err; + /* Optimize RX sensitivity */ return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); } diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 573de7bfcced..cf425cc959d5 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -130,6 +130,10 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) { int err; + err = rtsx_gops_pm_reset(pcr); + if (err < 0) + return err; + err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED | PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN | diff --git a/drivers/mfd/rtsx_gops.c b/drivers/mfd/rtsx_gops.c new file mode 100644 index 000000000000..b1a98c678593 --- /dev/null +++ b/drivers/mfd/rtsx_gops.c @@ -0,0 +1,37 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: + * Micky Ching <micky_ching@realsil.com.cn> + */ + +#include <linux/mfd/rtsx_pci.h> +#include "rtsx_pcr.h" + +int rtsx_gops_pm_reset(struct rtsx_pcr *pcr) +{ + int err; + + /* init aspm */ + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0x00); + err = rtsx_pci_update_cfg_byte(pcr, LCTLR, ~LCTLR_ASPM_CTL_MASK, 0x00); + if (err < 0) + return err; + + /* reset PM_CTRL3 before send buffer cmd */ + return rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); +} diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 07e4c2ebf05a..fe2bbb67defc 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -72,4 +72,7 @@ do { \ pcr->ms_pull_ctl_disable_tbl = __device##_ms_pull_ctl_disable_tbl; \ } while (0) +/* generic operations */ +int rtsx_gops_pm_reset(struct rtsx_pcr *pcr); + #endif diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index 9cf98d142d9a..dbdd0faeb6ce 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -647,8 +647,8 @@ static int rtsx_usb_probe(struct usb_interface *intf, /* initialize USB SG transfer timer */ setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr); - ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells, - ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL); + ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells, + ARRAY_SIZE(rtsx_usb_cells)); if (ret) goto out_init_fail; diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index dba7e2b6f8e9..0a7bc43db4e4 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -27,11 +27,11 @@ #include <linux/mfd/samsung/irq.h> #include <linux/mfd/samsung/s2mpa01.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mpu02.h> #include <linux/mfd/samsung/s5m8763.h> #include <linux/mfd/samsung/s5m8767.h> -#include <linux/regulator/machine.h> #include <linux/regmap.h> static const struct mfd_cell s5m8751_devs[] = { @@ -74,6 +74,15 @@ static const struct mfd_cell s2mps11_devs[] = { } }; +static const struct mfd_cell s2mps13_devs[] = { + { .name = "s2mps13-pmic", }, + { .name = "s2mps13-rtc", }, + { + .name = "s2mps13-clk", + .of_compatible = "samsung,s2mps13-clk", + }, +}; + static const struct mfd_cell s2mps14_devs[] = { { .name = "s2mps14-pmic", @@ -108,6 +117,9 @@ static const struct of_device_id sec_dt_match[] = { .compatible = "samsung,s2mps11-pmic", .data = (void *)S2MPS11X, }, { + .compatible = "samsung,s2mps13-pmic", + .data = (void *)S2MPS13X, + }, { .compatible = "samsung,s2mps14-pmic", .data = (void *)S2MPS14X, }, { @@ -194,6 +206,15 @@ static const struct regmap_config s2mps11_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static const struct regmap_config s2mps13_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPS13_REG_LDODSCH5, + .volatile_reg = s2mps11_volatile, + .cache_type = REGCACHE_FLAT, +}; + static const struct regmap_config s2mps14_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -325,6 +346,9 @@ static int sec_pmic_probe(struct i2c_client *i2c, case S2MPS11X: regmap = &s2mps11_regmap_config; break; + case S2MPS13X: + regmap = &s2mps13_regmap_config; + break; case S2MPS14X: regmap = &s2mps14_regmap_config; break; @@ -378,6 +402,10 @@ static int sec_pmic_probe(struct i2c_client *i2c, sec_devs = s2mps11_devs; num_sec_devs = ARRAY_SIZE(s2mps11_devs); break; + case S2MPS13X: + sec_devs = s2mps13_devs; + num_sec_devs = ARRAY_SIZE(s2mps13_devs); + break; case S2MPS14X: sec_devs = s2mps14_devs; num_sec_devs = ARRAY_SIZE(s2mps14_devs); @@ -432,15 +460,6 @@ static int sec_pmic_suspend(struct device *dev) */ disable_irq(sec_pmic->irq); - switch (sec_pmic->device_type) { - case S2MPS14X: - case S2MPU02: - regulator_suspend_prepare(PM_SUSPEND_MEM); - break; - default: - break; - } - return 0; } diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index f9a57869e3ec..ba86a918c2da 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -389,14 +389,22 @@ static const struct regmap_irq_chip s2mps11_irq_chip = { .ack_base = S2MPS11_REG_INT1, }; +#define S2MPS1X_IRQ_CHIP_COMMON_DATA \ + .irqs = s2mps14_irqs, \ + .num_irqs = ARRAY_SIZE(s2mps14_irqs), \ + .num_regs = 3, \ + .status_base = S2MPS14_REG_INT1, \ + .mask_base = S2MPS14_REG_INT1M, \ + .ack_base = S2MPS14_REG_INT1 \ + +static const struct regmap_irq_chip s2mps13_irq_chip = { + .name = "s2mps13", + S2MPS1X_IRQ_CHIP_COMMON_DATA, +}; + static const struct regmap_irq_chip s2mps14_irq_chip = { .name = "s2mps14", - .irqs = s2mps14_irqs, - .num_irqs = ARRAY_SIZE(s2mps14_irqs), - .num_regs = 3, - .status_base = S2MPS14_REG_INT1, - .mask_base = S2MPS14_REG_INT1M, - .ack_base = S2MPS14_REG_INT1, + S2MPS1X_IRQ_CHIP_COMMON_DATA, }; static const struct regmap_irq_chip s2mpu02_irq_chip = { @@ -452,6 +460,9 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) case S2MPS11X: sec_irq_chip = &s2mps11_irq_chip; break; + case S2MPS13X: + sec_irq_chip = &s2mps13_irq_chip; + break; case S2MPS14X: sec_irq_chip = &s2mps14_irq_chip; break; diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index ca15878ce5c0..72373b113885 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/list.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -22,31 +23,94 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> +#include <linux/slab.h> static struct platform_driver syscon_driver; +static DEFINE_SPINLOCK(syscon_list_slock); +static LIST_HEAD(syscon_list); + struct syscon { + struct device_node *np; struct regmap *regmap; + struct list_head list; +}; + +static struct regmap_config syscon_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, }; -static int syscon_match_node(struct device *dev, void *data) +static struct syscon *of_syscon_register(struct device_node *np) { - struct device_node *dn = data; + struct syscon *syscon; + struct regmap *regmap; + void __iomem *base; + int ret; + struct regmap_config syscon_config = syscon_regmap_config; + + if (!of_device_is_compatible(np, "syscon")) + return ERR_PTR(-EINVAL); + + syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); + if (!syscon) + return ERR_PTR(-ENOMEM); + + base = of_iomap(np, 0); + if (!base) { + ret = -ENOMEM; + goto err_map; + } + + /* Parse the device's DT node for an endianness specification */ + if (of_property_read_bool(np, "big-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; + + regmap = regmap_init_mmio(NULL, base, &syscon_config); + if (IS_ERR(regmap)) { + pr_err("regmap init failed\n"); + ret = PTR_ERR(regmap); + goto err_regmap; + } + + syscon->regmap = regmap; + syscon->np = np; + + spin_lock(&syscon_list_slock); + list_add_tail(&syscon->list, &syscon_list); + spin_unlock(&syscon_list_slock); - return (dev->of_node == dn) ? 1 : 0; + return syscon; + +err_regmap: + iounmap(base); +err_map: + kfree(syscon); + return ERR_PTR(ret); } struct regmap *syscon_node_to_regmap(struct device_node *np) { - struct syscon *syscon; - struct device *dev; + struct syscon *entry, *syscon = NULL; - dev = driver_find_device(&syscon_driver.driver, NULL, np, - syscon_match_node); - if (!dev) - return ERR_PTR(-EPROBE_DEFER); + spin_lock(&syscon_list_slock); - syscon = dev_get_drvdata(dev); + list_for_each_entry(entry, &syscon_list, list) + if (entry->np == np) { + syscon = entry; + break; + } + + spin_unlock(&syscon_list_slock); + + if (!syscon) + syscon = of_syscon_register(np); + + if (IS_ERR(syscon)) + return ERR_CAST(syscon); return syscon->regmap; } @@ -110,17 +174,6 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); -static const struct of_device_id of_syscon_match[] = { - { .compatible = "syscon", }, - { }, -}; - -static struct regmap_config syscon_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -167,7 +220,6 @@ static struct platform_driver syscon_driver = { .driver = { .name = "syscon", .owner = THIS_MODULE, - .of_match_table = of_syscon_match, }, .probe = syscon_probe, .id_table = syscon_ids, diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 9e04a7485981..439d905bb219 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -87,7 +87,7 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc) unsigned long flags; u8 dev_ctl; - clk_enable(t7l66xb->clk32k); + clk_prepare_enable(t7l66xb->clk32k); spin_lock_irqsave(&t7l66xb->lock, flags); @@ -118,7 +118,7 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc) spin_unlock_irqrestore(&t7l66xb->lock, flags); - clk_disable(t7l66xb->clk32k); + clk_disable_unprepare(t7l66xb->clk32k); return 0; } @@ -285,7 +285,7 @@ static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) if (pdata && pdata->suspend) pdata->suspend(dev); - clk_disable(t7l66xb->clk48m); + clk_disable_unprepare(t7l66xb->clk48m); return 0; } @@ -295,7 +295,7 @@ static int t7l66xb_resume(struct platform_device *dev) struct t7l66xb *t7l66xb = platform_get_drvdata(dev); struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev); - clk_enable(t7l66xb->clk48m); + clk_prepare_enable(t7l66xb->clk48m); if (pdata && pdata->resume) pdata->resume(dev); @@ -369,7 +369,7 @@ static int t7l66xb_probe(struct platform_device *dev) goto err_ioremap; } - clk_enable(t7l66xb->clk48m); + clk_prepare_enable(t7l66xb->clk48m); if (pdata && pdata->enable) pdata->enable(dev); @@ -414,9 +414,9 @@ static int t7l66xb_remove(struct platform_device *dev) int ret; ret = pdata->disable(dev); - clk_disable(t7l66xb->clk48m); + clk_disable_unprepare(t7l66xb->clk48m); clk_put(t7l66xb->clk48m); - clk_disable(t7l66xb->clk32k); + clk_disable_unprepare(t7l66xb->clk32k); clk_put(t7l66xb->clk32k); t7l66xb_detach_irq(dev); iounmap(t7l66xb->scr); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 0072e668c208..aacb3720065c 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -241,10 +241,8 @@ static struct irq_domain_ops tc3589x_irq_ops = { static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np) { - int base = tc3589x->irq_base; - tc3589x->domain = irq_domain_add_simple( - np, TC3589x_NR_INTERNAL_IRQS, base, + np, TC3589x_NR_INTERNAL_IRQS, 0, &tc3589x_irq_ops, tc3589x); if (!tc3589x->domain) { @@ -298,7 +296,7 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) if (blocks & TC3589x_BLOCK_GPIO) { ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, ARRAY_SIZE(tc3589x_dev_gpio), NULL, - tc3589x->irq_base, tc3589x->domain); + 0, tc3589x->domain); if (ret) { dev_err(tc3589x->dev, "failed to add gpio child\n"); return ret; @@ -309,7 +307,7 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) if (blocks & TC3589x_BLOCK_KEYPAD) { ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, ARRAY_SIZE(tc3589x_dev_keypad), NULL, - tc3589x->irq_base, tc3589x->domain); + 0, tc3589x->domain); if (ret) { dev_err(tc3589x->dev, "failed to keypad child\n"); return ret; @@ -404,7 +402,6 @@ static int tc3589x_probe(struct i2c_client *i2c, tc3589x->dev = &i2c->dev; tc3589x->i2c = i2c; tc3589x->pdata = pdata; - tc3589x->irq_base = pdata->irq_base; switch (version) { case TC3589X_TC35893: diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index e71f88000ae5..85fab3729102 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -52,7 +52,7 @@ static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) if (pdata && pdata->suspend) pdata->suspend(dev); - clk_disable(tc6387xb->clk32k); + clk_disable_unprepare(tc6387xb->clk32k); return 0; } @@ -62,7 +62,7 @@ static int tc6387xb_resume(struct platform_device *dev) struct tc6387xb *tc6387xb = platform_get_drvdata(dev); struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev); - clk_enable(tc6387xb->clk32k); + clk_prepare_enable(tc6387xb->clk32k); if (pdata && pdata->resume) pdata->resume(dev); @@ -100,7 +100,7 @@ static int tc6387xb_mmc_enable(struct platform_device *mmc) struct platform_device *dev = to_platform_device(mmc->dev.parent); struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - clk_enable(tc6387xb->clk32k); + clk_prepare_enable(tc6387xb->clk32k); tmio_core_mmc_enable(tc6387xb->scr + 0x200, 0, tc6387xb_mmc_resources[0].start & 0xfffe); @@ -113,7 +113,7 @@ static int tc6387xb_mmc_disable(struct platform_device *mmc) struct platform_device *dev = to_platform_device(mmc->dev.parent); struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - clk_disable(tc6387xb->clk32k); + clk_disable_unprepare(tc6387xb->clk32k); return 0; } @@ -214,7 +214,7 @@ static int tc6387xb_remove(struct platform_device *dev) mfd_remove_devices(&dev->dev); iounmap(tc6387xb->scr); release_resource(&tc6387xb->rscr); - clk_disable(tc6387xb->clk32k); + clk_disable_unprepare(tc6387xb->clk32k); clk_put(tc6387xb->clk32k); kfree(tc6387xb); diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 4fac16bcd732..d35f11fbeab7 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -263,6 +263,17 @@ static int tc6393xb_ohci_disable(struct platform_device *dev) return 0; } +static int tc6393xb_ohci_suspend(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev_get_platdata(dev->dev.parent); + + /* We can't properly store/restore OHCI state, so fail here */ + if (tcpd->resume_restore) + return -EBUSY; + + return tc6393xb_ohci_disable(dev); +} + static int tc6393xb_fb_enable(struct platform_device *dev) { struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent); @@ -403,7 +414,7 @@ static struct mfd_cell tc6393xb_cells[] = { .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources), .resources = tc6393xb_ohci_resources, .enable = tc6393xb_ohci_enable, - .suspend = tc6393xb_ohci_disable, + .suspend = tc6393xb_ohci_suspend, .resume = tc6393xb_ohci_enable, .disable = tc6393xb_ohci_disable, }, @@ -654,7 +665,7 @@ static int tc6393xb_probe(struct platform_device *dev) goto err_ioremap; } - ret = clk_enable(tc6393xb->clk); + ret = clk_prepare_enable(tc6393xb->clk); if (ret) goto err_clk_enable; @@ -717,7 +728,7 @@ err_gpio_add: gpiochip_remove(&tc6393xb->gpio); tcpd->disable(dev); err_enable: - clk_disable(tc6393xb->clk); + clk_disable_unprepare(tc6393xb->clk); err_clk_enable: iounmap(tc6393xb->scr); err_ioremap: @@ -748,7 +759,7 @@ static int tc6393xb_remove(struct platform_device *dev) gpiochip_remove(&tc6393xb->gpio); ret = tcpd->disable(dev); - clk_disable(tc6393xb->clk); + clk_disable_unprepare(tc6393xb->clk); iounmap(tc6393xb->scr); release_resource(&tc6393xb->rscr); clk_put(tc6393xb->clk); @@ -776,7 +787,7 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); } ret = tcpd->suspend(dev); - clk_disable(tc6393xb->clk); + clk_disable_unprepare(tc6393xb->clk); return ret; } @@ -788,7 +799,7 @@ static int tc6393xb_resume(struct platform_device *dev) int ret; int i; - clk_enable(tc6393xb->clk); + clk_prepare_enable(tc6393xb->clk); ret = tcpd->resume(dev); if (ret) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 1c3e6e2efe41..14b62e11aff4 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -76,58 +76,58 @@ static struct mfd_cell tps65090s[] = { static const struct regmap_irq tps65090_irqs[] = { /* INT1 IRQs*/ [TPS65090_IRQ_VAC_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, }, [TPS65090_IRQ_VSYS_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, }, [TPS65090_IRQ_BAT_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, }, [TPS65090_IRQ_CHARGING_STATUS_CHANGE] = { - .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, + .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, }, [TPS65090_IRQ_CHARGING_COMPLETE] = { - .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, + .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, }, [TPS65090_IRQ_OVERLOAD_DCDC1] = { - .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, }, [TPS65090_IRQ_OVERLOAD_DCDC2] = { - .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, }, /* INT2 IRQs*/ [TPS65090_IRQ_OVERLOAD_DCDC3] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, }, [TPS65090_IRQ_OVERLOAD_FET1] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, }, [TPS65090_IRQ_OVERLOAD_FET2] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, }, [TPS65090_IRQ_OVERLOAD_FET3] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, }, [TPS65090_IRQ_OVERLOAD_FET4] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, }, [TPS65090_IRQ_OVERLOAD_FET5] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, }, [TPS65090_IRQ_OVERLOAD_FET6] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, }, [TPS65090_IRQ_OVERLOAD_FET7] = { - .reg_offset = 1, - .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, }, }; @@ -176,7 +176,7 @@ MODULE_DEVICE_TABLE(of, tps65090_of_match); #endif static int tps65090_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct tps65090_platform_data *pdata = dev_get_platdata(&client->dev); int irq_base = 0; @@ -210,11 +210,11 @@ static int tps65090_i2c_probe(struct i2c_client *client, if (client->irq) { ret = regmap_add_irq_chip(tps65090->rmap, client->irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, - &tps65090_irq_chip, &tps65090->irq_data); - if (ret) { - dev_err(&client->dev, - "IRQ init failed with err: %d\n", ret); + IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, + &tps65090_irq_chip, &tps65090->irq_data); + if (ret) { + dev_err(&client->dev, + "IRQ init failed with err: %d\n", ret); return ret; } } else { @@ -223,8 +223,8 @@ static int tps65090_i2c_probe(struct i2c_client *client, } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, - ARRAY_SIZE(tps65090s), NULL, - 0, regmap_irq_get_domain(tps65090->irq_data)); + ARRAY_SIZE(tps65090s), NULL, + 0, regmap_irq_get_domain(tps65090->irq_data)); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index a8ee52c95f2f..80a919a8ca97 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -33,9 +33,11 @@ static const struct mfd_cell tps65217s[] = { { .name = "tps65217-pmic", + .of_compatible = "ti,tps65217-pmic", }, { .name = "tps65217-bl", + .of_compatible = "ti,tps65217-bl", }, }; diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 50f9091bcd38..7d63e324e6a8 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -831,6 +831,9 @@ static struct twl4030_power_data osc_off_idle = { static struct of_device_id twl4030_power_of_match[] = { { + .compatible = "ti,twl4030-power", + }, + { .compatible = "ti,twl4030-power-reset", .data = &omap3_reset, }, diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index 3c2b8f9e3c84..e6b3c70aeb22 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -93,9 +93,8 @@ static int vprbrd_probe(struct usb_interface *interface, version >> 8, version & 0xff, vb->usb_dev->bus->busnum, vb->usb_dev->devnum); - ret = mfd_add_devices(&interface->dev, PLATFORM_DEVID_AUTO, - vprbrd_devs, ARRAY_SIZE(vprbrd_devs), NULL, 0, - NULL); + ret = mfd_add_hotplug_devices(&interface->dev, vprbrd_devs, + ARRAY_SIZE(vprbrd_devs)); if (ret != 0) { dev_err(&interface->dev, "Failed to add mfd devices to core."); goto error; diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index d6f35bbf795b..b326a82017ee 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -336,8 +336,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ - { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */ - { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */ @@ -1112,6 +1110,8 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: @@ -1949,6 +1949,8 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: case ARIZONA_DSP1_SCRATCH_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_HP_DACVAL: case ARIZONA_MIC_DETECT_3: diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 4642b5b816a0..12cad94b4035 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -895,8 +895,16 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */ { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */ { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */ + { 0x0000054B, 0x0002 }, /* R1355 - AIF2 Frame Ctrl 5 */ + { 0x0000054C, 0x0003 }, /* R1356 - AIF2 Frame Ctrl 6 */ + { 0x0000054D, 0x0004 }, /* R1357 - AIF2 Frame Ctrl 7 */ + { 0x0000054E, 0x0005 }, /* R1358 - AIF2 Frame Ctrl 8 */ { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */ { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */ + { 0x00000553, 0x0002 }, /* R1363 - AIF2 Frame Ctrl 13 */ + { 0x00000554, 0x0003 }, /* R1364 - AIF2 Frame Ctrl 14 */ + { 0x00000555, 0x0004 }, /* R1365 - AIF2 Frame Ctrl 15 */ + { 0x00000556, 0x0005 }, /* R1366 - AIF2 Frame Ctrl 16 */ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */ @@ -1790,6 +1798,8 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: @@ -1934,8 +1944,16 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_AIF2_FRAME_CTRL_2: case ARIZONA_AIF2_FRAME_CTRL_3: case ARIZONA_AIF2_FRAME_CTRL_4: + case ARIZONA_AIF2_FRAME_CTRL_5: + case ARIZONA_AIF2_FRAME_CTRL_6: + case ARIZONA_AIF2_FRAME_CTRL_7: + case ARIZONA_AIF2_FRAME_CTRL_8: case ARIZONA_AIF2_FRAME_CTRL_11: case ARIZONA_AIF2_FRAME_CTRL_12: + case ARIZONA_AIF2_FRAME_CTRL_13: + case ARIZONA_AIF2_FRAME_CTRL_14: + case ARIZONA_AIF2_FRAME_CTRL_15: + case ARIZONA_AIF2_FRAME_CTRL_16: case ARIZONA_AIF2_TX_ENABLES: case ARIZONA_AIF2_RX_ENABLES: case ARIZONA_AIF3_BCLK_CTRL: @@ -2825,6 +2843,8 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS: case ARIZONA_MIC_DETECT_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_INPUT_ENABLES_STATUS: case ARIZONA_OUTPUT_STATUS_1: diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 4ab527f5c53b..f5124a8acad8 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -308,7 +308,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, goto err; } - mode = id2 & WM8350_CONF_STS_MASK >> 10; + mode = (id2 & WM8350_CONF_STS_MASK) >> 10; cust_id = id2 & WM8350_CUST_ID_MASK; chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12; dev_info(wm8350->dev, diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index 510da3b52324..c0c25d75aacc 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -670,6 +670,7 @@ static const struct reg_default wm8997_reg_default[] = { { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */ + { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */ { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */ { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */ { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */ @@ -886,6 +887,8 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: @@ -1328,6 +1331,7 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg) case ARIZONA_INTERRUPT_STATUS_4: case ARIZONA_INTERRUPT_STATUS_5: case ARIZONA_INTERRUPT_STATUS_1_MASK: + case ARIZONA_INTERRUPT_STATUS_2_MASK: case ARIZONA_INTERRUPT_STATUS_3_MASK: case ARIZONA_INTERRUPT_STATUS_4_MASK: case ARIZONA_INTERRUPT_STATUS_5_MASK: @@ -1477,6 +1481,8 @@ static bool wm8997_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_SAMPLE_RATE_3_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: case ARIZONA_MIC_DETECT_3: + case ARIZONA_HP_CTRL_1L: + case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_INPUT_ENABLES_STATUS: case ARIZONA_OUTPUT_STATUS_1: diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index bbeb4516facf..006242c8bca0 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -75,7 +75,7 @@ config ATMEL_TCB_CLKSRC config ATMEL_TCB_CLKSRC_BLOCK int depends on ATMEL_TCB_CLKSRC - prompt "TC Block" if ARCH_AT91RM9200 || ARCH_AT91SAM9260 || CPU_AT32AP700X + prompt "TC Block" if CPU_AT32AP700X default 0 range 0 1 help diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c index 0ff4b02177be..0cf2c9d676be 100644 --- a/drivers/misc/eeprom/eeprom_93cx6.c +++ b/drivers/misc/eeprom/eeprom_93cx6.c @@ -170,7 +170,7 @@ static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom, } /** - * eeprom_93cx6_read - Read multiple words from eeprom + * eeprom_93cx6_read - Read a word from eeprom * @eeprom: Pointer to eeprom structure * @word: Word index from where we should start reading * @data: target pointer where the information will have to be stored @@ -235,6 +235,66 @@ void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word, EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread); /** + * eeprom_93cx6_readb - Read a byte from eeprom + * @eeprom: Pointer to eeprom structure + * @word: Byte index from where we should start reading + * @data: target pointer where the information will have to be stored + * + * This function will read a byte of the eeprom data + * into the given data pointer. + */ +void eeprom_93cx6_readb(struct eeprom_93cx6 *eeprom, const u8 byte, + u8 *data) +{ + u16 command; + u16 tmp; + + /* + * Initialize the eeprom register + */ + eeprom_93cx6_startup(eeprom); + + /* + * Select the read opcode and the byte to be read. + */ + command = (PCI_EEPROM_READ_OPCODE << (eeprom->width + 1)) | byte; + eeprom_93cx6_write_bits(eeprom, command, + PCI_EEPROM_WIDTH_OPCODE + eeprom->width + 1); + + /* + * Read the requested 8 bits. + */ + eeprom_93cx6_read_bits(eeprom, &tmp, 8); + *data = tmp & 0xff; + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_readb); + +/** + * eeprom_93cx6_multireadb - Read multiple bytes from eeprom + * @eeprom: Pointer to eeprom structure + * @byte: Index from where we should start reading + * @data: target pointer where the information will have to be stored + * @words: Number of bytes that should be read. + * + * This function will read all requested bytes from the eeprom, + * this is done by calling eeprom_93cx6_readb() multiple times. + */ +void eeprom_93cx6_multireadb(struct eeprom_93cx6 *eeprom, const u8 byte, + u8 *data, const u16 bytes) +{ + unsigned int i; + + for (i = 0; i < bytes; i++) + eeprom_93cx6_readb(eeprom, byte + i, &data[i]); +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_multireadb); + +/** * eeprom_93cx6_wren - set the write enable state * @eeprom: Pointer to eeprom structure * @enable: true to enable writes, otherwise disable writes diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 2cf2bbc0b927..180a5442fd4b 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -187,6 +187,7 @@ void enclosure_unregister(struct enclosure_device *edev) EXPORT_SYMBOL_GPL(enclosure_unregister); #define ENCLOSURE_NAME_SIZE 64 +#define COMPONENT_NAME_SIZE 64 static void enclosure_link_name(struct enclosure_component *cdev, char *name) { @@ -246,6 +247,29 @@ static void enclosure_component_release(struct device *dev) put_device(dev->parent); } +static struct enclosure_component * +enclosure_component_find_by_name(struct enclosure_device *edev, + const char *name) +{ + int i; + const char *cname; + struct enclosure_component *ecomp; + + if (!edev || !name || !name[0]) + return NULL; + + for (i = 0; i < edev->components; i++) { + ecomp = &edev->component[i]; + cname = dev_name(&ecomp->cdev); + if (ecomp->number != -1 && + cname && cname[0] && + !strcmp(cname, name)) + return ecomp; + } + + return NULL; +} + static const struct attribute_group *enclosure_component_groups[]; /** @@ -269,7 +293,8 @@ enclosure_component_register(struct enclosure_device *edev, { struct enclosure_component *ecomp; struct device *cdev; - int err; + int err, i; + char newname[COMPONENT_NAME_SIZE]; if (number >= edev->components) return ERR_PTR(-EINVAL); @@ -283,9 +308,20 @@ enclosure_component_register(struct enclosure_device *edev, ecomp->number = number; cdev = &ecomp->cdev; cdev->parent = get_device(&edev->edev); - if (name && name[0]) - dev_set_name(cdev, "%s", name); - else + + if (name && name[0]) { + /* Some hardware (e.g. enclosure in RX300 S6) has components + * with non unique names. Registering duplicates in sysfs + * will lead to warnings during bootup. So make the names + * unique by appending consecutive numbers -1, -2, ... */ + i = 1; + snprintf(newname, COMPONENT_NAME_SIZE, + "%s", name); + while (enclosure_component_find_by_name(edev, newname)) + snprintf(newname, COMPONENT_NAME_SIZE, + "%s-%i", name, i++); + dev_set_name(cdev, "%s", newname); + } else dev_set_name(cdev, "%u", number); cdev->release = enclosure_component_release; diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c index b3a812384a6f..c344483fa7d6 100644 --- a/drivers/misc/vexpress-syscfg.c +++ b/drivers/misc/vexpress-syscfg.c @@ -145,7 +145,7 @@ static struct regmap_config vexpress_syscfg_regmap_config = { static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, void *context) { - struct platform_device *pdev = to_platform_device(dev); + int err; struct vexpress_syscfg *syscfg = context; struct vexpress_syscfg_func *func; struct property *prop; @@ -155,32 +155,18 @@ static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, u32 site, position, dcc; int i; - if (dev->of_node) { - int err = vexpress_config_get_topo(dev->of_node, &site, + err = vexpress_config_get_topo(dev->of_node, &site, &position, &dcc); + if (err) + return ERR_PTR(err); - if (err) - return ERR_PTR(err); - - prop = of_find_property(dev->of_node, - "arm,vexpress-sysreg,func", NULL); - if (!prop) - return ERR_PTR(-EINVAL); - - num = prop->length / sizeof(u32) / 2; - val = prop->value; - } else { - if (pdev->num_resources != 1 || - pdev->resource[0].flags != IORESOURCE_BUS) - return ERR_PTR(-EFAULT); - - site = pdev->resource[0].start; - if (site == VEXPRESS_SITE_MASTER) - site = vexpress_config_get_master(); - position = 0; - dcc = 0; - num = 1; - } + prop = of_find_property(dev->of_node, + "arm,vexpress-sysreg,func", NULL); + if (!prop) + return ERR_PTR(-EINVAL); + + num = prop->length / sizeof(u32) / 2; + val = prop->value; /* * "arm,vexpress-energy" function used to be described @@ -207,13 +193,8 @@ static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, for (i = 0; i < num; i++) { u32 function, device; - if (dev->of_node) { - function = be32_to_cpup(val++); - device = be32_to_cpup(val++); - } else { - function = pdev->resource[0].end; - device = pdev->id; - } + function = be32_to_cpup(val++); + device = be32_to_cpup(val++); dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", func, site, position, dcc, @@ -265,17 +246,6 @@ static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = { }; -/* Non-DT hack, to be gone... */ -static struct device *vexpress_syscfg_bridge; - -int vexpress_syscfg_device_register(struct platform_device *pdev) -{ - pdev->dev.parent = vexpress_syscfg_bridge; - - return platform_device_register(pdev); -} - - static int vexpress_syscfg_probe(struct platform_device *pdev) { struct vexpress_syscfg *syscfg; @@ -303,10 +273,6 @@ static int vexpress_syscfg_probe(struct platform_device *pdev) if (IS_ERR(bridge)) return PTR_ERR(bridge); - /* Non-DT case */ - if (!pdev->dev.of_node) - vexpress_syscfg_bridge = bridge; - return 0; } diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1fa4c80ff886..4409d79ed650 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -78,13 +78,16 @@ static int perdev_minors = CONFIG_MMC_BLOCK_MINORS; /* * We've only got one major, so number of mmcblk devices is - * limited to 256 / number of minors per device. + * limited to (1 << 20) / number of minors per device. It is also + * currently limited by the size of the static bitmaps below. */ static int max_devices; -/* 256 minors, so at most 256 separate devices */ -static DECLARE_BITMAP(dev_use, 256); -static DECLARE_BITMAP(name_use, 256); +#define MAX_DEVICES 256 + +/* TODO: Replace these with struct ida */ +static DECLARE_BITMAP(dev_use, MAX_DEVICES); +static DECLARE_BITMAP(name_use, MAX_DEVICES); /* * There is one mmc_blk_data per slot. @@ -112,7 +115,7 @@ struct mmc_blk_data { /* * Only set in main mmc_blk_data associated - * with mmc_card with mmc_set_drvdata, and keeps + * with mmc_card with dev_set_drvdata, and keeps * track of the current selected device partition. */ unsigned int part_curr; @@ -260,7 +263,7 @@ static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr, int ret; struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); - ret = snprintf(buf, PAGE_SIZE, "%d", + ret = snprintf(buf, PAGE_SIZE, "%d\n", get_disk_ro(dev_to_disk(dev)) ^ md->read_only); mmc_blk_put(md); @@ -642,7 +645,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, struct mmc_blk_data *md) { int ret; - struct mmc_blk_data *main_md = mmc_get_drvdata(card); + struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev); if (main_md->part_curr == md->part_type) return 0; @@ -1004,7 +1007,8 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host, err = mmc_hw_reset(host); /* Ensure we switch back to the correct partition */ if (err != -EOPNOTSUPP) { - struct mmc_blk_data *main_md = mmc_get_drvdata(host->card); + struct mmc_blk_data *main_md = + dev_get_drvdata(&host->card->dev); int part_err; main_md->part_curr = main_md->part_type; @@ -1308,19 +1312,11 @@ static int mmc_blk_packed_err_check(struct mmc_card *card, } if (status & R1_EXCEPTION_EVENT) { - ext_csd = kzalloc(512, GFP_KERNEL); - if (!ext_csd) { - pr_err("%s: unable to allocate buffer for ext_csd\n", - req->rq_disk->disk_name); - return -ENOMEM; - } - - err = mmc_send_ext_csd(card, ext_csd); + err = mmc_get_ext_csd(card, &ext_csd); if (err) { pr_err("%s: error %d sending ext_csd\n", req->rq_disk->disk_name, err); - check = MMC_BLK_ABORT; - goto free; + return MMC_BLK_ABORT; } if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] & @@ -1338,7 +1334,6 @@ static int mmc_blk_packed_err_check(struct mmc_card *card, req->rq_disk->disk_name, packed->nr_entries, packed->blocks, packed->idx_failure); } -free: kfree(ext_csd); } @@ -2093,7 +2088,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, /* * !subname implies we are creating main mmc_blk_data that will be - * associated with mmc_card with mmc_set_drvdata. Due to device + * associated with mmc_card with dev_set_drvdata. Due to device * partitions, devidx will not coincide with a per-physical card * index anymore so we keep track of a name index. */ @@ -2425,8 +2420,9 @@ static const struct mmc_fixup blk_fixups[] = END_FIXUP }; -static int mmc_blk_probe(struct mmc_card *card) +static int mmc_blk_probe(struct device *dev) { + struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_blk_data *md, *part_md; char cap_str[10]; @@ -2451,7 +2447,7 @@ static int mmc_blk_probe(struct mmc_card *card) if (mmc_blk_alloc_parts(card, md)) goto out; - mmc_set_drvdata(card, md); + dev_set_drvdata(dev, md); if (mmc_add_disk(md)) goto out; @@ -2481,9 +2477,10 @@ static int mmc_blk_probe(struct mmc_card *card) return 0; } -static void mmc_blk_remove(struct mmc_card *card) +static int mmc_blk_remove(struct device *dev) { - struct mmc_blk_data *md = mmc_get_drvdata(card); + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_blk_data *md = dev_get_drvdata(dev); mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); @@ -2494,13 +2491,15 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_disable(&card->dev); pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); - mmc_set_drvdata(card, NULL); + dev_set_drvdata(dev, NULL); + + return 0; } -static int _mmc_blk_suspend(struct mmc_card *card) +static int _mmc_blk_suspend(struct device *dev) { struct mmc_blk_data *part_md; - struct mmc_blk_data *md = mmc_get_drvdata(card); + struct mmc_blk_data *md = dev_get_drvdata(dev); if (md) { mmc_queue_suspend(&md->queue); @@ -2511,21 +2510,21 @@ static int _mmc_blk_suspend(struct mmc_card *card) return 0; } -static void mmc_blk_shutdown(struct mmc_card *card) +static void mmc_blk_shutdown(struct device *dev) { - _mmc_blk_suspend(card); + _mmc_blk_suspend(dev); } -#ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card) +#ifdef CONFIG_PM_SLEEP +static int mmc_blk_suspend(struct device *dev) { - return _mmc_blk_suspend(card); + return _mmc_blk_suspend(dev); } -static int mmc_blk_resume(struct mmc_card *card) +static int mmc_blk_resume(struct device *dev) { struct mmc_blk_data *part_md; - struct mmc_blk_data *md = mmc_get_drvdata(card); + struct mmc_blk_data *md = dev_get_drvdata(dev); if (md) { /* @@ -2540,19 +2539,15 @@ static int mmc_blk_resume(struct mmc_card *card) } return 0; } -#else -#define mmc_blk_suspend NULL -#define mmc_blk_resume NULL #endif -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmcblk", - }, +static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume); + +static struct device_driver mmc_driver = { + .name = "mmcblk", + .pm = &mmc_blk_pm_ops, .probe = mmc_blk_probe, .remove = mmc_blk_remove, - .suspend = mmc_blk_suspend, - .resume = mmc_blk_resume, .shutdown = mmc_blk_shutdown, }; @@ -2563,7 +2558,7 @@ static int __init mmc_blk_init(void) if (perdev_minors != CONFIG_MMC_BLOCK_MINORS) pr_info("mmcblk: using %d minors per device\n", perdev_minors); - max_devices = 256 / perdev_minors; + max_devices = min(MAX_DEVICES, (1 << MINORBITS) / perdev_minors); res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); if (res) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 0c0fc52d42c5..0a7430f94d29 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -14,6 +14,7 @@ #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/slab.h> +#include <linux/device.h> #include <linux/scatterlist.h> #include <linux/swap.h> /* For nr_free_buffer_pages() */ @@ -32,6 +33,8 @@ #define BUFFER_ORDER 2 #define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER) +#define TEST_ALIGN_END 8 + /* * Limit the test area size to the maximum MMC HC erase group size. Note that * the maximum SD allocation unit size is just 4MiB. @@ -1174,7 +1177,7 @@ static int mmc_test_align_write(struct mmc_test_card *test) int ret, i; struct scatterlist sg; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, 512); ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); if (ret) @@ -1189,7 +1192,7 @@ static int mmc_test_align_read(struct mmc_test_card *test) int ret, i; struct scatterlist sg; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, 512); ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); if (ret) @@ -1216,7 +1219,7 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test) if (size < 1024) return RESULT_UNSUP_HOST; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, size); ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); if (ret) @@ -1243,7 +1246,7 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test) if (size < 1024) return RESULT_UNSUP_HOST; - for (i = 1;i < 4;i++) { + for (i = 1; i < TEST_ALIGN_END; i++) { sg_init_one(&sg, test->buffer + i, size); ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); if (ret) @@ -2997,8 +3000,9 @@ err: return ret; } -static int mmc_test_probe(struct mmc_card *card) +static int mmc_test_probe(struct device *dev) { + struct mmc_card *card = mmc_dev_to_card(dev); int ret; if (!mmc_card_mmc(card) && !mmc_card_sd(card)) @@ -3013,20 +3017,22 @@ static int mmc_test_probe(struct mmc_card *card) return 0; } -static void mmc_test_remove(struct mmc_card *card) +static int mmc_test_remove(struct device *dev) { + struct mmc_card *card = mmc_dev_to_card(dev); + mmc_test_free_result(card); mmc_test_free_dbgfs_file(card); + + return 0; } -static void mmc_test_shutdown(struct mmc_card *card) +static void mmc_test_shutdown(struct device *dev) { } -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmc_test", - }, +static struct device_driver mmc_driver = { + .name = "mmc_test", .probe = mmc_test_probe, .remove = mmc_test_remove, .shutdown = mmc_test_shutdown, diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index cfa6110632c3..236d194c2883 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -232,13 +232,15 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, if (!mqrq_cur->bounce_buf) { pr_warn("%s: unable to allocate bounce cur buffer\n", mmc_card_name(card)); - } - mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); - if (!mqrq_prev->bounce_buf) { - pr_warn("%s: unable to allocate bounce prev buffer\n", - mmc_card_name(card)); - kfree(mqrq_cur->bounce_buf); - mqrq_cur->bounce_buf = NULL; + } else { + mqrq_prev->bounce_buf = + kmalloc(bouncesz, GFP_KERNEL); + if (!mqrq_prev->bounce_buf) { + pr_warn("%s: unable to allocate bounce prev buffer\n", + mmc_card_name(card)); + kfree(mqrq_cur->bounce_buf); + mqrq_cur->bounce_buf = NULL; + } } } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 8a1f1240e058..5ca562ccfcf3 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -25,8 +25,6 @@ #include "sdio_cis.h" #include "bus.h" -#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) - static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -106,33 +104,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) return retval; } -static int mmc_bus_probe(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = mmc_dev_to_card(dev); - - return drv->probe(card); -} - -static int mmc_bus_remove(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = mmc_dev_to_card(dev); - - drv->remove(card); - - return 0; -} - static void mmc_bus_shutdown(struct device *dev) { - struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; - if (dev->driver && drv->shutdown) - drv->shutdown(card); + if (dev->driver && dev->driver->shutdown) + dev->driver->shutdown(dev); if (host->bus_ops->shutdown) { ret = host->bus_ops->shutdown(host); @@ -145,16 +124,13 @@ static void mmc_bus_shutdown(struct device *dev) #ifdef CONFIG_PM_SLEEP static int mmc_bus_suspend(struct device *dev) { - struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; - if (dev->driver && drv->suspend) { - ret = drv->suspend(card); - if (ret) - return ret; - } + ret = pm_generic_suspend(dev); + if (ret) + return ret; ret = host->bus_ops->suspend(host); return ret; @@ -162,7 +138,6 @@ static int mmc_bus_suspend(struct device *dev) static int mmc_bus_resume(struct device *dev) { - struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; @@ -172,9 +147,7 @@ static int mmc_bus_resume(struct device *dev) pr_warn("%s: error %d during resume (card was removed?)\n", mmc_hostname(host), ret); - if (dev->driver && drv->resume) - ret = drv->resume(card); - + ret = pm_generic_resume(dev); return ret; } #endif @@ -207,8 +180,6 @@ static struct bus_type mmc_bus_type = { .dev_groups = mmc_dev_groups, .match = mmc_bus_match, .uevent = mmc_bus_uevent, - .probe = mmc_bus_probe, - .remove = mmc_bus_remove, .shutdown = mmc_bus_shutdown, .pm = &mmc_bus_pm_ops, }; @@ -227,24 +198,22 @@ void mmc_unregister_bus(void) * mmc_register_driver - register a media driver * @drv: MMC media driver */ -int mmc_register_driver(struct mmc_driver *drv) +int mmc_register_driver(struct device_driver *drv) { - drv->drv.bus = &mmc_bus_type; - return driver_register(&drv->drv); + drv->bus = &mmc_bus_type; + return driver_register(drv); } - EXPORT_SYMBOL(mmc_register_driver); /** * mmc_unregister_driver - unregister a media driver * @drv: MMC media driver */ -void mmc_unregister_driver(struct mmc_driver *drv) +void mmc_unregister_driver(struct device_driver *drv) { - drv->drv.bus = &mmc_bus_type; - driver_unregister(&drv->drv); + drv->bus = &mmc_bus_type; + driver_unregister(drv); } - EXPORT_SYMBOL(mmc_unregister_driver); static void mmc_release_card(struct device *dev) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f26a5f1d926d..9584bffa8b22 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -149,6 +149,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) led_trigger_event(host->led, LED_OFF); + if (mrq->sbc) { + pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n", + mmc_hostname(host), mrq->sbc->opcode, + mrq->sbc->error, + mrq->sbc->resp[0], mrq->sbc->resp[1], + mrq->sbc->resp[2], mrq->sbc->resp[3]); + } + pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", mmc_hostname(host), cmd->opcode, err, cmd->resp[0], cmd->resp[1], @@ -214,6 +222,10 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mrq->cmd->error = 0; mrq->cmd->mrq = mrq; + if (mrq->sbc) { + mrq->sbc->error = 0; + mrq->sbc->mrq = mrq; + } if (mrq->data) { BUG_ON(mrq->data->blksz > host->max_blk_size); BUG_ON(mrq->data->blocks > host->max_blk_count); @@ -538,8 +550,18 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (host->card && mmc_card_mmc(host->card) && ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) || (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) && - (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) + (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) { + + /* Cancel the prepared request */ + if (areq) + mmc_post_req(host, areq->mrq, -EINVAL); + mmc_start_bkops(host->card, true); + + /* prepare the request again */ + if (areq) + mmc_pre_req(host, areq->mrq, !host->areq); + } } if (!err && areq) @@ -709,27 +731,16 @@ int mmc_read_bkops_status(struct mmc_card *card) int err; u8 *ext_csd; - /* - * In future work, we should consider storing the entire ext_csd. - */ - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - pr_err("%s: could not allocate buffer to receive the ext_csd.\n", - mmc_hostname(card->host)); - return -ENOMEM; - } - mmc_claim_host(card->host); - err = mmc_send_ext_csd(card, ext_csd); + err = mmc_get_ext_csd(card, &ext_csd); mmc_release_host(card->host); if (err) - goto out; + return err; card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS]; -out: kfree(ext_csd); - return err; + return 0; } EXPORT_SYMBOL(mmc_read_bkops_status); @@ -1088,6 +1099,22 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) mmc_host_clk_release(host); } +/* + * Set initial state after a power cycle or a hw_reset. + */ +void mmc_set_initial_state(struct mmc_host *host) +{ + if (mmc_host_is_spi(host)) + host->ios.chip_select = MMC_CS_HIGH; + else + host->ios.chip_select = MMC_CS_DONTCARE; + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + + mmc_set_ios(host); +} + /** * mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number * @vdd: voltage (mV) @@ -1420,18 +1447,20 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) pr_warn("%s: cannot verify signal voltage switch\n", mmc_hostname(host)); + mmc_host_clk_hold(host); + cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(host, &cmd, 0); if (err) - return err; + goto err_command; - if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) - return -EIO; - - mmc_host_clk_hold(host); + if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) { + err = -EIO; + goto err_command; + } /* * The card should drive cmd and dat[0:3] low immediately * after the response of cmd11, but wait 1 ms to be sure @@ -1480,6 +1509,7 @@ power_cycle: mmc_power_cycle(host, ocr); } +err_command: mmc_host_clk_release(host); return err; @@ -1526,15 +1556,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) mmc_host_clk_hold(host); host->ios.vdd = fls(ocr) - 1; - if (mmc_host_is_spi(host)) - host->ios.chip_select = MMC_CS_HIGH; - else - host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ios.power_mode = MMC_POWER_UP; - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0) @@ -1574,14 +1598,9 @@ void mmc_power_off(struct mmc_host *host) host->ios.clock = 0; host->ios.vdd = 0; - if (!mmc_host_is_spi(host)) { - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - host->ios.chip_select = MMC_CS_DONTCARE; - } host->ios.power_mode = MMC_POWER_OFF; - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); /* * Some configurations, such as the 802.11 SDIO card in the OLPC @@ -2259,30 +2278,16 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check) /* If the reset has happened, then a status command will fail */ if (check) { - struct mmc_command cmd = {0}; - int err; + u32 status; - cmd.opcode = MMC_SEND_STATUS; - if (!mmc_host_is_spi(card->host)) - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 0); - if (!err) { + if (!mmc_send_status(card, &status)) { mmc_host_clk_release(host); return -ENOSYS; } } - if (mmc_host_is_spi(host)) { - host->ios.chip_select = MMC_CS_HIGH; - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; - } else { - host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - } - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); mmc_host_clk_release(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 443a584660f0..d76597c65e3a 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -49,6 +49,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); void mmc_power_up(struct mmc_host *host, u32 ocr); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host, u32 ocr); +void mmc_set_initial_state(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 91eb16223246..e9142108a6c6 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -291,14 +291,8 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) if (!buf) return -ENOMEM; - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - err = -ENOMEM; - goto out_free; - } - mmc_get_card(card); - err = mmc_send_ext_csd(card, ext_csd); + err = mmc_get_ext_csd(card, &ext_csd); mmc_put_card(card); if (err) goto out_free; @@ -314,7 +308,6 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) out_free: kfree(buf); - kfree(ext_csd); return err; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a301a78a2bd1..02ad79229f65 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -177,65 +177,6 @@ static int mmc_decode_csd(struct mmc_card *card) return 0; } -/* - * Read extended CSD. - */ -static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) -{ - int err; - u8 *ext_csd; - - BUG_ON(!card); - BUG_ON(!new_ext_csd); - - *new_ext_csd = NULL; - - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) - return 0; - - /* - * As the ext_csd is so large and mostly unused, we don't store the - * raw block in mmc_card. - */ - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - pr_err("%s: could not allocate a buffer to " - "receive the ext_csd.\n", mmc_hostname(card->host)); - return -ENOMEM; - } - - err = mmc_send_ext_csd(card, ext_csd); - if (err) { - kfree(ext_csd); - *new_ext_csd = NULL; - - /* If the host or the card can't do the switch, - * fail more gracefully. */ - if ((err != -EINVAL) - && (err != -ENOSYS) - && (err != -EFAULT)) - return err; - - /* - * High capacity cards should have this "magic" size - * stored in their CSD. - */ - if (card->csd.capacity == (4096 * 512)) { - pr_err("%s: unable to read EXT_CSD " - "on a possible high capacity card. " - "Card will be ignored.\n", - mmc_hostname(card->host)); - } else { - pr_warn("%s: unable to read EXT_CSD, performance might suffer\n", - mmc_hostname(card->host)); - err = 0; - } - } else - *new_ext_csd = ext_csd; - - return err; -} - static void mmc_select_card_type(struct mmc_card *card) { struct mmc_host *host = card->host; @@ -391,16 +332,11 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) /* * Decode extended CSD. */ -static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) +static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) { int err = 0, idx; unsigned int part_size; - BUG_ON(!card); - - if (!ext_csd) - return 0; - /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE]; if (card->csd.structure == 3) { @@ -628,16 +564,56 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.data_sector_size = 512; } + /* eMMC v5 or later */ + if (card->ext_csd.rev >= 7) { + memcpy(card->ext_csd.fwrev, &ext_csd[EXT_CSD_FIRMWARE_VERSION], + MMC_FIRMWARE_LEN); + card->ext_csd.ffu_capable = + (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) && + !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1); + } out: return err; } -static inline void mmc_free_ext_csd(u8 *ext_csd) +static int mmc_read_ext_csd(struct mmc_card *card) { + u8 *ext_csd; + int err; + + if (!mmc_can_ext_csd(card)) + return 0; + + err = mmc_get_ext_csd(card, &ext_csd); + if (err) { + /* If the host or the card can't do the switch, + * fail more gracefully. */ + if ((err != -EINVAL) + && (err != -ENOSYS) + && (err != -EFAULT)) + return err; + + /* + * High capacity cards should have this "magic" size + * stored in their CSD. + */ + if (card->csd.capacity == (4096 * 512)) { + pr_err("%s: unable to read EXT_CSD on a possible high capacity card. Card will be ignored.\n", + mmc_hostname(card->host)); + } else { + pr_warn("%s: unable to read EXT_CSD, performance might suffer\n", + mmc_hostname(card->host)); + err = 0; + } + + return err; + } + + err = mmc_decode_ext_csd(card, ext_csd); kfree(ext_csd); + return err; } - static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) { u8 *bw_ext_csd; @@ -647,11 +623,8 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) return 0; err = mmc_get_ext_csd(card, &bw_ext_csd); - - if (err || bw_ext_csd == NULL) { - err = -EINVAL; - goto out; - } + if (err) + return err; /* only compare read only fields */ err = !((card->ext_csd.raw_partition_support == @@ -710,8 +683,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) if (err) err = -EINVAL; -out: - mmc_free_ext_csd(bw_ext_csd); + kfree(bw_ext_csd); return err; } @@ -722,7 +694,7 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); -MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); +MMC_DEV_ATTR(ffu_capable, "%d\n", card->ext_csd.ffu_capable); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name); @@ -735,6 +707,22 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); +static ssize_t mmc_fwrev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + + if (card->ext_csd.rev < 7) { + return sprintf(buf, "0x%x\n", card->cid.fwrev); + } else { + return sprintf(buf, "0x%*phN\n", MMC_FIRMWARE_LEN, + card->ext_csd.fwrev); + } +} + +static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL); + static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, @@ -742,6 +730,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_erase_size.attr, &dev_attr_preferred_erase_size.attr, &dev_attr_fwrev.attr, + &dev_attr_ffu_capable.attr, &dev_attr_hwrev.attr, &dev_attr_manfid.attr, &dev_attr_name.attr, @@ -774,14 +763,6 @@ static int __mmc_select_powerclass(struct mmc_card *card, unsigned int pwrclass_val = 0; int err = 0; - /* Power class selection is supported for versions >= 4.0 */ - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) - return 0; - - /* Power class values are defined only for 4/8 bit bus */ - if (bus_width == EXT_CSD_BUS_WIDTH_1) - return 0; - switch (1 << host->ios.vdd) { case MMC_VDD_165_195: if (host->ios.clock <= MMC_HIGH_26_MAX_DTR) @@ -844,7 +825,7 @@ static int mmc_select_powerclass(struct mmc_card *card) int err, ddr; /* Power class selection is supported for versions >= 4.0 */ - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) + if (!mmc_can_ext_csd(card)) return 0; bus_width = host->ios.bus_width; @@ -905,7 +886,7 @@ static int mmc_select_bus_width(struct mmc_card *card) unsigned idx, bus_width = 0; int err = 0; - if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && + if (!mmc_can_ext_csd(card) && !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) return 0; @@ -998,7 +979,7 @@ static int mmc_select_hs_ddr(struct mmc_card *card) ext_csd_bits, card->ext_csd.generic_cmd6_time); if (err) { - pr_warn("%s: switch to bus width %d ddr failed\n", + pr_err("%s: switch to bus width %d ddr failed\n", mmc_hostname(host), 1 << bus_width); return err; } @@ -1069,7 +1050,7 @@ static int mmc_select_hs400(struct mmc_card *card) card->ext_csd.generic_cmd6_time, true, true, true); if (err) { - pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n", + pr_err("%s: switch to high-speed from hs200 failed, err:%d\n", mmc_hostname(host), err); return err; } @@ -1079,7 +1060,7 @@ static int mmc_select_hs400(struct mmc_card *card) EXT_CSD_DDR_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time); if (err) { - pr_warn("%s: switch to bus width for hs400 failed, err:%d\n", + pr_err("%s: switch to bus width for hs400 failed, err:%d\n", mmc_hostname(host), err); return err; } @@ -1089,7 +1070,7 @@ static int mmc_select_hs400(struct mmc_card *card) card->ext_csd.generic_cmd6_time, true, true, true); if (err) { - pr_warn("%s: switch to hs400 failed, err:%d\n", + pr_err("%s: switch to hs400 failed, err:%d\n", mmc_hostname(host), err); return err; } @@ -1146,8 +1127,7 @@ static int mmc_select_timing(struct mmc_card *card) { int err = 0; - if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && - card->ext_csd.hs_max_dtr == 0)) + if (!mmc_can_ext_csd(card)) goto bus_speed; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) @@ -1232,7 +1212,7 @@ static int mmc_hs200_tuning(struct mmc_card *card) mmc_host_clk_release(host); if (err) - pr_warn("%s: tuning execution failed\n", + pr_err("%s: tuning execution failed\n", mmc_hostname(host)); } @@ -1252,7 +1232,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, int err; u32 cid[4]; u32 rocr; - u8 *ext_csd = NULL; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1361,14 +1340,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } if (!oldcard) { - /* - * Fetch and process extended CSD. - */ - - err = mmc_get_ext_csd(card, &ext_csd); - if (err) - goto free_card; - err = mmc_read_ext_csd(card, ext_csd); + /* Read extended CSD. */ + err = mmc_read_ext_csd(card); if (err) goto free_card; @@ -1458,18 +1431,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (mmc_card_hs200(card)) { err = mmc_hs200_tuning(card); if (err) - goto err; + goto free_card; err = mmc_select_hs400(card); if (err) - goto err; + goto free_card; } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); if (!IS_ERR_VALUE(err)) { err = mmc_select_hs_ddr(card); if (err) - goto err; + goto free_card; } } @@ -1545,15 +1518,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!oldcard) host->card = card; - mmc_free_ext_csd(ext_csd); return 0; free_card: if (!oldcard) mmc_remove_card(card); err: - mmc_free_ext_csd(ext_csd); - return err; } diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 7911e0510a1d..3b044c5b029c 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -264,20 +264,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; - void *data_buf; - int is_on_stack; - - is_on_stack = object_is_on_stack(buf); - if (is_on_stack) { - /* - * dma onto stack is unsafe/nonportable, but callers to this - * routine normally provide temporary on-stack buffers ... - */ - data_buf = kmalloc(len, GFP_KERNEL); - if (!data_buf) - return -ENOMEM; - } else - data_buf = buf; mrq.cmd = &cmd; mrq.data = &data; @@ -298,7 +284,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, data.sg = &sg; data.sg_len = 1; - sg_init_one(&sg, data_buf, len); + sg_init_one(&sg, buf, len); if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) { /* @@ -312,11 +298,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, mmc_wait_for_req(host, &mrq); - if (is_on_stack) { - memcpy(buf, data_buf, len); - kfree(data_buf); - } - if (cmd.error) return cmd.error; if (data.error) @@ -334,7 +315,7 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd) return mmc_send_cxd_native(card->host, card->rca << 16, csd, MMC_SEND_CSD); - csd_tmp = kmalloc(16, GFP_KERNEL); + csd_tmp = kzalloc(16, GFP_KERNEL); if (!csd_tmp) return -ENOMEM; @@ -362,7 +343,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid) cid, MMC_SEND_CID); } - cid_tmp = kmalloc(16, GFP_KERNEL); + cid_tmp = kzalloc(16, GFP_KERNEL); if (!cid_tmp) return -ENOMEM; @@ -378,12 +359,35 @@ err: return ret; } -int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) +int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) { - return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, - ext_csd, 512); + int err; + u8 *ext_csd; + + if (!card || !new_ext_csd) + return -EINVAL; + + if (!mmc_can_ext_csd(card)) + return -EOPNOTSUPP; + + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + ext_csd = kzalloc(512, GFP_KERNEL); + if (!ext_csd) + return -ENOMEM; + + err = mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ext_csd, + 512); + if (err) + kfree(ext_csd); + else + *new_ext_csd = ext_csd; + + return err; } -EXPORT_SYMBOL_GPL(mmc_send_ext_csd); +EXPORT_SYMBOL_GPL(mmc_get_ext_csd); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) { @@ -543,6 +547,75 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, } EXPORT_SYMBOL_GPL(mmc_switch); +int mmc_send_tuning(struct mmc_host *host) +{ + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + struct mmc_ios *ios = &host->ios; + const u8 *tuning_block_pattern; + int size, err = 0; + u8 *data_buf; + u32 opcode; + + if (ios->bus_width == MMC_BUS_WIDTH_8) { + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); + opcode = MMC_SEND_TUNING_BLOCK_HS200; + } else if (ios->bus_width == MMC_BUS_WIDTH_4) { + tuning_block_pattern = tuning_blk_pattern_4bit; + size = sizeof(tuning_blk_pattern_4bit); + opcode = MMC_SEND_TUNING_BLOCK; + } else + return -EINVAL; + + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = opcode; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = size; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + /* + * According to the tuning specs, Tuning process + * is normally shorter 40 executions of CMD19, + * and timeout value should be shorter than 150 ms + */ + data.timeout_ns = 150 * NSEC_PER_MSEC; + + data.sg = &sg; + data.sg_len = 1; + sg_init_one(&sg, data_buf, size); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error) { + err = cmd.error; + goto out; + } + + if (data.error) { + err = data.error; + goto out; + } + + if (memcmp(data_buf, tuning_block_pattern, size)) + err = -EIO; + +out: + kfree(data_buf); + return err; +} +EXPORT_SYMBOL_GPL(mmc_send_tuning); + static int mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) @@ -675,3 +748,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) return 0; } + +int mmc_can_ext_csd(struct mmc_card *card) +{ + return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3); +} diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 390dac665b2a..6f4b00ed93de 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -20,13 +20,13 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_set_relative_addr(struct mmc_card *card); int mmc_send_csd(struct mmc_card *card, u32 *csd); -int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); +int mmc_can_ext_csd(struct mmc_card *card); #endif diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 2439e717655b..fd0750b5a634 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -980,8 +980,12 @@ static int mmc_sdio_resume(struct mmc_host *host) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { sdio_reset(host); mmc_go_idle(host); - err = mmc_sdio_init_card(host, host->card->ocr, host->card, - mmc_card_keep_power(host)); + mmc_send_if_cond(host, host->card->ocr); + err = mmc_send_io_op_cond(host, 0, NULL); + if (!err) + err = mmc_sdio_init_card(host, host->card->ocr, + host->card, + mmc_card_keep_power(host)); } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); @@ -1035,7 +1039,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host) sdio_reset(host); mmc_go_idle(host); - mmc_send_if_cond(host, host->ocr_avail); + mmc_send_if_cond(host, host->card->ocr); ret = mmc_send_io_op_cond(host, 0, NULL); if (ret) diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 6da97b170563..60885316afba 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -26,6 +26,8 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) + /* show configuration fields */ #define sdio_config_attr(field, format_string) \ static ssize_t \ @@ -196,8 +198,6 @@ static int sdio_bus_remove(struct device *dev) return ret; } -#ifdef CONFIG_PM - static const struct dev_pm_ops sdio_bus_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume) SET_RUNTIME_PM_OPS( @@ -207,14 +207,6 @@ static const struct dev_pm_ops sdio_bus_pm_ops = { ) }; -#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops) - -#else /* !CONFIG_PM */ - -#define SDIO_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM */ - static struct bus_type sdio_bus_type = { .name = "sdio", .dev_groups = sdio_dev_groups, @@ -222,7 +214,7 @@ static struct bus_type sdio_bus_type = { .uevent = sdio_bus_uevent, .probe = sdio_bus_probe, .remove = sdio_bus_remove, - .pm = SDIO_PM_OPS_PTR, + .pm = &sdio_bus_pm_ops, }; int sdio_register_bus(void) @@ -295,7 +287,7 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card) static void sdio_acpi_set_handle(struct sdio_func *func) { struct mmc_host *host = func->card->host; - u64 addr = (host->slotno << 16) | func->num; + u64 addr = ((u64)host->slotno << 16) | func->num; acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr); } diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 13860656104b..2d6fbdd11803 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -580,7 +580,7 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" depends on HAS_DMA - depends on ARC || ARM || MIPS || COMPILE_TEST + depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both @@ -748,3 +748,8 @@ config MMC_SUNXI help This selects support for the SD/MMC Host Controller on Allwinner sunxi SoCs. + +config MMC_TOSHIBA_PCI + tristate "Toshiba Type A SD/MMC Card Interface Driver" + depends on PCI + help diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index b09ecfb88269..f7b0a77cf419 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o +obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 77250d4b1979..62aba9af19f4 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -30,13 +30,16 @@ #include <linux/stat.h> #include <linux/types.h> #include <linux/platform_data/atmel.h> +#include <linux/platform_data/mmc-atmel-mci.h> #include <linux/mmc/host.h> #include <linux/mmc/sdio.h> -#include <mach/atmel-mci.h> #include <linux/atmel-mci.h> #include <linux/atmel_pdc.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/pinctrl/consumer.h> #include <asm/cacheflush.h> #include <asm/io.h> @@ -44,6 +47,8 @@ #include "atmel-mci-regs.h" +#define AUTOSUSPEND_DELAY 50 + #define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE) #define ATMCI_DMA_THRESHOLD 16 @@ -386,20 +391,19 @@ static int atmci_regs_show(struct seq_file *s, void *v) if (!buf) return -ENOMEM; + pm_runtime_get_sync(&host->pdev->dev); + /* * Grab a more or less consistent snapshot. Note that we're * not disabling interrupts, so IMR and SR may not be * consistent. */ - ret = clk_prepare_enable(host->mck); - if (ret) - goto out; - spin_lock_bh(&host->lock); memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE); spin_unlock_bh(&host->lock); - clk_disable_unprepare(host->mck); + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); seq_printf(s, "MR:\t0x%08x%s%s ", buf[ATMCI_MR / 4], @@ -449,7 +453,6 @@ static int atmci_regs_show(struct seq_file *s, void *v) val & ATMCI_CFG_LSYNC ? " LSYNC" : ""); } -out: kfree(buf); return ret; @@ -560,6 +563,9 @@ atmci_of_init(struct platform_device *pdev) pdata->slot[slot_id].detect_is_active_high = of_property_read_bool(cnp, "cd-inverted"); + pdata->slot[slot_id].non_removable = + of_property_read_bool(cnp, "non-removable"); + pdata->slot[slot_id].wp_pin = of_get_named_gpio(cnp, "wp-gpios", 0); } @@ -1252,6 +1258,8 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(slot->mrq); dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode); + pm_runtime_get_sync(&host->pdev->dev); + /* * We may "know" the card is gone even though there's still an * electrical connection. If so, we really need to communicate @@ -1281,7 +1289,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct atmel_mci_slot *slot = mmc_priv(mmc); struct atmel_mci *host = slot->host; unsigned int i; - bool unprepare_clk; + + pm_runtime_get_sync(&host->pdev->dev); slot->sdc_reg &= ~ATMCI_SDCBUS_MASK; switch (ios->bus_width) { @@ -1297,13 +1306,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned int clock_min = ~0U; u32 clkdiv; - clk_prepare(host->mck); - unprepare_clk = true; - spin_lock_bh(&host->lock); if (!host->mode_reg) { - clk_enable(host->mck); - unprepare_clk = false; atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN); if (host->caps.has_cfg_reg) @@ -1371,8 +1375,6 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } else { bool any_slot_active = false; - unprepare_clk = false; - spin_lock_bh(&host->lock); slot->clock = 0; for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { @@ -1385,17 +1387,12 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); if (host->mode_reg) { atmci_readl(host, ATMCI_MR); - clk_disable(host->mck); - unprepare_clk = true; } host->mode_reg = 0; } spin_unlock_bh(&host->lock); } - if (unprepare_clk) - clk_unprepare(host->mck); - switch (ios->power_mode) { case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) @@ -1421,6 +1418,9 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) */ break; } + + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); } static int atmci_get_ro(struct mmc_host *mmc) @@ -1512,6 +1512,9 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); + + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&host->pdev->dev); } static void atmci_command_complete(struct atmel_mci *host, @@ -2137,7 +2140,7 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init atmci_init_slot(struct atmel_mci *host, +static int atmci_init_slot(struct atmel_mci *host, struct mci_slot_pdata *slot_data, unsigned int id, u32 sdc_reg, u32 sdio_irq) { @@ -2206,8 +2209,12 @@ static int __init atmci_init_slot(struct atmel_mci *host, } } - if (!gpio_is_valid(slot->detect_pin)) - mmc->caps |= MMC_CAP_NEEDS_POLL; + if (!gpio_is_valid(slot->detect_pin)) { + if (slot_data->non_removable) + mmc->caps |= MMC_CAP_NONREMOVABLE; + else + mmc->caps |= MMC_CAP_NEEDS_POLL; + } if (gpio_is_valid(slot->wp_pin)) { if (devm_gpio_request(&host->pdev->dev, slot->wp_pin, @@ -2265,55 +2272,25 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot, mmc_free_host(slot->mmc); } -static bool atmci_filter(struct dma_chan *chan, void *pdata) +static int atmci_configure_dma(struct atmel_mci *host) { - struct mci_platform_data *sl_pdata = pdata; - struct mci_dma_data *sl; - - if (!sl_pdata) - return false; + host->dma.chan = dma_request_slave_channel_reason(&host->pdev->dev, + "rxtx"); + if (IS_ERR(host->dma.chan)) + return PTR_ERR(host->dma.chan); + + dev_info(&host->pdev->dev, "using %s for DMA transfers\n", + dma_chan_name(host->dma.chan)); + + host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; + host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.src_maxburst = 1; + host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; + host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.dst_maxburst = 1; + host->dma_conf.device_fc = false; - sl = sl_pdata->dma_slave; - if (sl && find_slave_dev(sl) == chan->device->dev) { - chan->private = slave_data_ptr(sl); - return true; - } else { - return false; - } -} - -static bool atmci_configure_dma(struct atmel_mci *host) -{ - struct mci_platform_data *pdata; - dma_cap_mask_t mask; - - if (host == NULL) - return false; - - pdata = host->pdev->dev.platform_data; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma.chan = dma_request_slave_channel_compat(mask, atmci_filter, pdata, - &host->pdev->dev, "rxtx"); - if (!host->dma.chan) { - dev_warn(&host->pdev->dev, "no DMA channel available\n"); - return false; - } else { - dev_info(&host->pdev->dev, - "using %s for DMA transfers\n", - dma_chan_name(host->dma.chan)); - - host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; - host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_conf.src_maxburst = 1; - host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; - host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_conf.dst_maxburst = 1; - host->dma_conf.device_fc = false; - return true; - } + return 0; } /* @@ -2321,7 +2298,7 @@ static bool atmci_configure_dma(struct atmel_mci *host) * HSMCI provides DMA support and a new config register but no more supports * PDC. */ -static void __init atmci_get_cap(struct atmel_mci *host) +static void atmci_get_cap(struct atmel_mci *host) { unsigned int version; @@ -2370,7 +2347,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) } } -static int __init atmci_probe(struct platform_device *pdev) +static int atmci_probe(struct platform_device *pdev) { struct mci_platform_data *pdata; struct atmel_mci *host; @@ -2417,19 +2394,23 @@ static int __init atmci_probe(struct platform_device *pdev) atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); host->bus_hz = clk_get_rate(host->mck); - clk_disable_unprepare(host->mck); host->mapbase = regs->start; tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host); ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); - if (ret) + if (ret) { + clk_disable_unprepare(host->mck); return ret; + } /* Get MCI capabilities and set operations according to it */ atmci_get_cap(host); - if (atmci_configure_dma(host)) { + ret = atmci_configure_dma(host); + if (ret == -EPROBE_DEFER) + goto err_dma_probe_defer; + if (ret == 0) { host->prepare_data = &atmci_prepare_data_dma; host->submit_data = &atmci_submit_data_dma; host->stop_transfer = &atmci_stop_transfer_dma; @@ -2449,6 +2430,12 @@ static int __init atmci_probe(struct platform_device *pdev) setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + /* We need at least one slot to succeed */ nr_slots = 0; ret = -ENODEV; @@ -2491,6 +2478,9 @@ static int __init atmci_probe(struct platform_device *pdev) "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", host->mapbase, irq, nr_slots); + pm_runtime_mark_last_busy(&host->pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; err_dma_alloc: @@ -2499,18 +2489,26 @@ err_dma_alloc: atmci_cleanup_slot(host->slot[i], i); } err_init_slot: + clk_disable_unprepare(host->mck); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + del_timer_sync(&host->timer); - if (host->dma.chan) + if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); +err_dma_probe_defer: free_irq(irq, host); return ret; } -static int __exit atmci_remove(struct platform_device *pdev) +static int atmci_remove(struct platform_device *pdev) { struct atmel_mci *host = platform_get_drvdata(pdev); unsigned int i; + pm_runtime_get_sync(&pdev->dev); + if (host->buffer) dma_free_coherent(&pdev->dev, host->buf_size, host->buffer, host->buf_phys_addr); @@ -2520,41 +2518,62 @@ static int __exit atmci_remove(struct platform_device *pdev) atmci_cleanup_slot(host->slot[i], i); } - clk_prepare_enable(host->mck); atmci_writel(host, ATMCI_IDR, ~0UL); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); atmci_readl(host, ATMCI_SR); - clk_disable_unprepare(host->mck); del_timer_sync(&host->timer); - if (host->dma.chan) + if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); free_irq(platform_get_irq(pdev, 0), host); + clk_disable_unprepare(host->mck); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; } -static struct platform_driver atmci_driver = { - .remove = __exit_p(atmci_remove), - .driver = { - .name = "atmel_mci", - .of_match_table = of_match_ptr(atmci_dt_ids), - }, -}; - -static int __init atmci_init(void) +#ifdef CONFIG_PM +static int atmci_runtime_suspend(struct device *dev) { - return platform_driver_probe(&atmci_driver, atmci_probe); + struct atmel_mci *host = dev_get_drvdata(dev); + + clk_disable_unprepare(host->mck); + + pinctrl_pm_select_sleep_state(dev); + + return 0; } -static void __exit atmci_exit(void) +static int atmci_runtime_resume(struct device *dev) { - platform_driver_unregister(&atmci_driver); + struct atmel_mci *host = dev_get_drvdata(dev); + + pinctrl_pm_select_default_state(dev); + + return clk_prepare_enable(host->mck); } +#endif -late_initcall(atmci_init); /* try to load after dma driver when built-in */ -module_exit(atmci_exit); +static const struct dev_pm_ops atmci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_PM_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL) +}; + +static struct platform_driver atmci_driver = { + .probe = atmci_probe, + .remove = atmci_remove, + .driver = { + .name = "atmel_mci", + .of_match_table = of_match_ptr(atmci_dt_ids), + .pm = &atmci_dev_pm_ops, + }, +}; +module_platform_driver(atmci_driver); MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 0fbc53ac7eae..509365cb22c6 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -25,6 +25,7 @@ #define NUM_PINS(x) (x + 2) #define SDMMC_CLKSEL 0x09C +#define SDMMC_CLKSEL64 0x0A8 #define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) #define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) #define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) @@ -65,6 +66,8 @@ enum dw_mci_exynos_type { DW_MCI_TYPE_EXYNOS5250, DW_MCI_TYPE_EXYNOS5420, DW_MCI_TYPE_EXYNOS5420_SMU, + DW_MCI_TYPE_EXYNOS7, + DW_MCI_TYPE_EXYNOS7_SMU, }; /* Exynos implementation specific driver private data */ @@ -95,6 +98,12 @@ static struct dw_mci_exynos_compatible { }, { .compatible = "samsung,exynos5420-dw-mshc-smu", .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, + }, { + .compatible = "samsung,exynos7-dw-mshc", + .ctrl_type = DW_MCI_TYPE_EXYNOS7, + }, { + .compatible = "samsung,exynos7-dw-mshc-smu", + .ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU, }, }; @@ -102,7 +111,8 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; - if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) { + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { mci_writel(host, MPSBEGIN0, 0); mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | @@ -153,11 +163,22 @@ static int dw_mci_exynos_resume(struct device *dev) static int dw_mci_exynos_resume_noirq(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_exynos_priv_data *priv = host->priv; u32 clksel; - clksel = mci_readl(host, CLKSEL); - if (clksel & SDMMC_CLKSEL_WAKEUP_INT) - mci_writel(host, CLKSEL, clksel); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); + + if (clksel & SDMMC_CLKSEL_WAKEUP_INT) { + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); + } return 0; } @@ -169,6 +190,7 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) { + struct dw_mci_exynos_priv_data *priv = host->priv; /* * Exynos4412 and Exynos5250 extends the use of CMD register with the * use of bit 29 (which is reserved on standard MSHC controllers) for @@ -176,8 +198,14 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) - *cmdr |= SDMMC_CMD_USE_HOLD_REG; + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { + if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64))) + *cmdr |= SDMMC_CMD_USE_HOLD_REG; + } else { + if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) + *cmdr |= SDMMC_CMD_USE_HOLD_REG; + } } static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) @@ -188,12 +216,20 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) u8 div = priv->ciu_div + 1; if (ios->timing == MMC_TIMING_MMC_DDR52) { - mci_writel(host, CLKSEL, priv->ddr_timing); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, priv->ddr_timing); + else + mci_writel(host, CLKSEL, priv->ddr_timing); /* Should be double rate for DDR mode */ if (ios->bus_width == MMC_BUS_WIDTH_8) wanted <<= 1; } else { - mci_writel(host, CLKSEL, priv->sdr_timing); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, priv->sdr_timing); + else + mci_writel(host, CLKSEL, priv->sdr_timing); } /* Don't care if wanted clock is zero */ @@ -265,26 +301,51 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) { - return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); + struct dw_mci_exynos_priv_data *priv = host->priv; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64)); + else + return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); } static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) { u32 clksel; - clksel = mci_readl(host, CLKSEL); + struct dw_mci_exynos_priv_data *priv = host->priv; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); - mci_writel(host, CLKSEL, clksel); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); } static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) { + struct dw_mci_exynos_priv_data *priv = host->priv; u32 clksel; u8 sample; - clksel = mci_readl(host, CLKSEL); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); sample = (clksel + 1) & 0x7; clksel = (clksel & ~0x7) | sample; - mci_writel(host, CLKSEL, clksel); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); return sample; } @@ -411,6 +472,10 @@ static const struct of_device_id dw_mci_exynos_match[] = { .data = &exynos_drv_data, }, { .compatible = "samsung,exynos5420-dw-mshc-smu", .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos7-dw-mshc", + .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos7-dw-mshc-smu", + .data = &exynos_drv_data, }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 8b6572162ed9..ec6dbcdec693 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -35,6 +35,10 @@ static const struct dw_mci_drv_data socfpga_drv_data = { .prepare_command = dw_mci_pltfm_prepare_command, }; +static const struct dw_mci_drv_data pistachio_drv_data = { + .prepare_command = dw_mci_pltfm_prepare_command, +}; + int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data) { @@ -90,6 +94,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data }, + { .compatible = "img,pistachio-dw-mshc", + .data = &pistachio_drv_data }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index f0c2cb1a210d..5650ac488cf3 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -37,6 +37,9 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) unsigned int cclkin; u32 bus_hz; + if (ios->clock == 0) + return; + /* * cclkin: source clock of mmc controller * bus_hz: card interface clock generated by CLKGEN @@ -65,14 +68,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rockchip_init(struct dw_mci *host) +{ + /* It is slot 8 on Rockchip SoCs */ + host->sdio_id0 = 8; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command = dw_mci_rockchip_prepare_command, + .init = dw_mci_rockchip_init, }; static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command = dw_mci_rockchip_prepare_command, .set_ios = dw_mci_rk3288_set_ios, .setup_clock = dw_mci_rk3288_setup_clock, + .init = dw_mci_rockchip_init, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc68d5b2..67c04518ec4c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -34,7 +34,6 @@ #include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> -#include <linux/workqueue.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/mmc/slot-gpio.h> @@ -62,6 +61,24 @@ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ SDMMC_IDMAC_INT_TI) +struct idmac_desc_64addr { + u32 des0; /* Control Descriptor */ + + u32 des1; /* Reserved */ + + u32 des2; /*Buffer sizes */ +#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \ + ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff)) + + u32 des3; /* Reserved */ + + u32 des4; /* Lower 32-bits of Buffer Address Pointer 1*/ + u32 des5; /* Upper 32-bits of Buffer Address Pointer 1*/ + + u32 des6; /* Lower 32-bits of Next Descriptor Address */ + u32 des7; /* Upper 32-bits of Next Descriptor Address */ +}; + struct idmac_desc { u32 des0; /* Control Descriptor */ #define IDMAC_DES0_DIC BIT(1) @@ -83,6 +100,7 @@ struct idmac_desc { #endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -414,30 +432,66 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { int i; - struct idmac_desc *desc = host->sg_cpu; + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *desc = host->sg_cpu; - for (i = 0; i < sg_len; i++, desc++) { - unsigned int length = sg_dma_len(&data->sg[i]); - u32 mem_addr = sg_dma_address(&data->sg[i]); + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u64 mem_addr = sg_dma_address(&data->sg[i]); - /* Set the OWN bit and disable interrupts for this descriptor */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + /* + * Set the OWN bit and disable interrupts for this + * descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + } - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, length); + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; - /* Physical address to DMA to/from */ - desc->des2 = mem_addr; - } + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * + sizeof(struct idmac_desc_64addr); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; - /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= IDMAC_DES0_FD; + } else { + struct idmac_desc *desc = host->sg_cpu; + + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); + + /* + * Set the OWN bit and disable interrupts for this + * descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, length); - /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc->des0 |= IDMAC_DES0_LD; + /* Physical address to DMA to/from */ + desc->des2 = mem_addr; + } + + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; + } wmb(); } @@ -448,6 +502,10 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) dw_mci_translate_sglist(host, host->data, sg_len); + /* Make sure to reset DMA in case we did PIO before this */ + dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET); + dw_mci_idmac_reset(host); + /* Select IDMAC interface */ temp = mci_readl(host, CTRL); temp |= SDMMC_CTRL_USE_IDMAC; @@ -466,29 +524,71 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) static int dw_mci_idmac_init(struct dw_mci *host) { - struct idmac_desc *p; int i; - /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr); + + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; + i++, p++) { + p->des6 = (host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) & 0xffffffff; + + p->des7 = (u64)(host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) >> 32; + /* Initialize reserved and buffer size fields to "0" */ + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des6 = host->sg_dma & 0xffffffff; + p->des7 = (u64)host->sg_dma >> 32; + p->des0 = IDMAC_DES0_ER; + + } else { + struct idmac_desc *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); - /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) - p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1)); + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) + p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * + (i + 1)); - /* Set the last descriptor as the end-of-ring descriptor */ - p->des3 = host->sg_dma; - p->des0 = IDMAC_DES0_ER; + /* Set the last descriptor as the end-of-ring descriptor */ + p->des3 = host->sg_dma; + p->des0 = IDMAC_DES0_ER; + } dw_mci_idmac_reset(host); - /* Mask out interrupts - get Tx & Rx complete only */ - mci_writel(host, IDSTS, IDMAC_INT_CLR); - mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI | - SDMMC_IDMAC_INT_TI); + if (host->dma_64bit_address == 1) { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS64, IDMAC_INT_CLR); + mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff); + mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32); + + } else { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); + mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDR, host->sg_dma); + } - /* Set the descriptor base address */ - mci_writel(host, DBADDR, host->sg_dma); return 0; } @@ -626,6 +726,13 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) WARN_ON(!(data->flags & MMC_DATA_READ)); + /* + * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is + * in the FIFO region, so we really shouldn't access it). + */ + if (host->verid < DW_MMC_240A) + return; + if (host->timing != MMC_TIMING_MMC_HS200 && host->timing != MMC_TIMING_UHS_SDR104) goto disable; @@ -819,7 +926,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id))) + if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1075,7 +1182,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); if (ret) { - dev_err(&mmc->class_dev, + dev_dbg(&mmc->class_dev, "Regulator set error %d: %d - %d\n", ret, min_uv, max_uv); return ret; @@ -1180,10 +1287,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot->id))); + (int_mask | SDMMC_INT_SDIO(slot->sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask & ~SDMMC_INT_SDIO(slot->id))); + (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id))); } } @@ -1954,6 +2061,23 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) tasklet_schedule(&host->tasklet); } +static void dw_mci_handle_cd(struct dw_mci *host) +{ + int i; + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot[i]; + + if (!slot) + continue; + + if (slot->mmc->ops->card_event) + slot->mmc->ops->card_event(slot->mmc); + mmc_detect_change(slot->mmc, + msecs_to_jiffies(host->pdata->detect_delay_ms)); + } +} + static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; @@ -2029,14 +2153,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_CD) { mci_writel(host, RINTSTS, SDMMC_INT_CD); - queue_work(host->card_workqueue, &host->card_work); + dw_mci_handle_cd(host); } /* Handle SDIO Interrupts */ for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; - if (pending & SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot->sdio_id)); mmc_signal_sdio_irq(slot->mmc); } } @@ -2045,99 +2170,28 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) #ifdef CONFIG_MMC_DW_IDMAC /* Handle DMA interrupts */ - pending = mci_readl(host, IDSTS); - if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { - mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); - mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete(host); + if (host->dma_64bit_address == 1) { + pending = mci_readl(host, IDSTS64); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI | + SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } else { + pending = mci_readl(host, IDSTS); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | + SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } } #endif return IRQ_HANDLED; } -static void dw_mci_work_routine_card(struct work_struct *work) -{ - struct dw_mci *host = container_of(work, struct dw_mci, card_work); - int i; - - for (i = 0; i < host->num_slots; i++) { - struct dw_mci_slot *slot = host->slot[i]; - struct mmc_host *mmc = slot->mmc; - struct mmc_request *mrq; - int present; - - present = dw_mci_get_cd(mmc); - while (present != slot->last_detect_state) { - dev_dbg(&slot->mmc->class_dev, "card %s\n", - present ? "inserted" : "removed"); - - spin_lock_bh(&host->lock); - - /* Card change detected */ - slot->last_detect_state = present; - - /* Clean up queue if present */ - mrq = slot->mrq; - if (mrq) { - if (mrq == host->mrq) { - host->data = NULL; - host->cmd = NULL; - - switch (host->state) { - case STATE_IDLE: - case STATE_WAITING_CMD11_DONE: - break; - case STATE_SENDING_CMD11: - case STATE_SENDING_CMD: - mrq->cmd->error = -ENOMEDIUM; - if (!mrq->data) - break; - /* fall through */ - case STATE_SENDING_DATA: - mrq->data->error = -ENOMEDIUM; - dw_mci_stop_dma(host); - break; - case STATE_DATA_BUSY: - case STATE_DATA_ERROR: - if (mrq->data->error == -EINPROGRESS) - mrq->data->error = -ENOMEDIUM; - /* fall through */ - case STATE_SENDING_STOP: - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - break; - } - - dw_mci_request_end(host, mrq); - } else { - list_del(&slot->queue_node); - mrq->cmd->error = -ENOMEDIUM; - if (mrq->data) - mrq->data->error = -ENOMEDIUM; - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - - spin_unlock(&host->lock); - mmc_request_done(slot->mmc, mrq); - spin_lock(&host->lock); - } - } - - /* Power down slot */ - if (present == 0) - dw_mci_reset(host); - - spin_unlock_bh(&host->lock); - - present = dw_mci_get_cd(mmc); - } - - mmc_detect_change(slot->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); - } -} - #ifdef CONFIG_OF /* given a slot id, find out the device node representing that slot */ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) @@ -2206,6 +2260,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot->id = id; + slot->sdio_id = host->sdio_id0 + id; slot->mmc = mmc; slot->host = host; host->slot[id] = slot; @@ -2289,9 +2344,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) dw_mci_init_debugfs(slot); #endif - /* Card initially undetected */ - slot->last_detect_state = 0; - return 0; err_host_allocated: @@ -2309,6 +2361,22 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { + int addr_config; + /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */ + addr_config = (mci_readl(host, HCON) >> 27) & 0x01; + + if (addr_config == 1) { + /* host supports IDMAC in 64-bit address mode */ + host->dma_64bit_address = 1; + dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); + if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) + dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64)); + } else { + /* host supports IDMAC in 32-bit address mode */ + host->dma_64bit_address = 0; + dev_info(host->dev, "IDMAC supports 32-bit address mode.\n"); + } + /* Alloc memory for sg translation */ host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); @@ -2672,17 +2740,10 @@ int dw_mci_probe(struct dw_mci *host) host->data_offset = DATA_240A_OFFSET; tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); - host->card_workqueue = alloc_workqueue("dw-mci-card", - WQ_MEM_RECLAIM, 1); - if (!host->card_workqueue) { - ret = -ENOMEM; - goto err_dmaunmap; - } - INIT_WORK(&host->card_work, dw_mci_work_routine_card); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); if (ret) - goto err_workqueue; + goto err_dmaunmap; if (host->pdata->num_slots) host->num_slots = host->pdata->num_slots; @@ -2718,7 +2779,7 @@ int dw_mci_probe(struct dw_mci *host) } else { dev_dbg(host->dev, "attempted to initialize %d slots, " "but failed on all\n", host->num_slots); - goto err_workqueue; + goto err_dmaunmap; } if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) @@ -2726,9 +2787,6 @@ int dw_mci_probe(struct dw_mci *host) return 0; -err_workqueue: - destroy_workqueue(host->card_workqueue); - err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); @@ -2762,8 +2820,6 @@ void dw_mci_remove(struct dw_mci *host) mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - destroy_workqueue(host->card_workqueue); - if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 01b99e8a9190..0d0f7a271d63 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -55,6 +55,17 @@ #define SDMMC_BUFADDR 0x098 #define SDMMC_CDTHRCTL 0x100 #define SDMMC_DATA(x) (x) +/* +* Registers to support idmac 64-bit address mode +*/ +#define SDMMC_DBADDRL 0x088 +#define SDMMC_DBADDRU 0x08c +#define SDMMC_IDSTS64 0x090 +#define SDMMC_IDINTEN64 0x094 +#define SDMMC_DSCADDRL 0x098 +#define SDMMC_DSCADDRU 0x09c +#define SDMMC_BUFADDRL 0x0A0 +#define SDMMC_BUFADDRU 0x0A4 /* * Data offset is difference according to Version @@ -214,7 +225,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. - * @last_detect_state: Most recently observed card detect state. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -234,7 +245,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 int id; - int last_detect_state; + int sdio_id; }; struct dw_mci_tuning_data { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 43af791e2e45..53bf7a4b5839 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -736,8 +736,15 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, chan = host->dma_tx_channel; dmaengine_terminate_all(chan); + if (host->dma_desc_current == next->dma_desc) + host->dma_desc_current = NULL; + + if (host->dma_current == next->dma_chan) + host->dma_current = NULL; + next->dma_desc = NULL; next->dma_chan = NULL; + data->host_cookie = 0; } } diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 9405ecdaf6cf..90c60fd4ff6e 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1360,7 +1360,7 @@ msmsdcc_probe(struct platform_device *pdev) if (ret) goto cmd_irq_free; - mmc_set_drvdata(pdev, mmc); + platform_set_drvdata(pdev, mmc); mmc_add_host(mmc); pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", @@ -1419,7 +1419,7 @@ ioremap_free: static int msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { - struct mmc_host *mmc = mmc_get_drvdata(dev); + struct mmc_host *mmc = platform_get_drvdata(dev); if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); @@ -1437,7 +1437,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) static int msmsdcc_resume(struct platform_device *dev) { - struct mmc_host *mmc = mmc_get_drvdata(dev); + struct mmc_host *mmc = platform_get_drvdata(dev); if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 6b4c5ad3b393..4f8618f4522d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -111,10 +111,15 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data) mvsd_write(MVSD_BLK_COUNT, data->blocks); mvsd_write(MVSD_BLK_SIZE, data->blksz); - if (nodma || (data->blksz | data->sg->offset) & 3) { + if (nodma || (data->blksz | data->sg->offset) & 3 || + ((!(data->flags & MMC_DATA_READ) && data->sg->offset & 0x3f))) { /* * We cannot do DMA on a buffer which offset or size * is not aligned on a 4-byte boundary. + * + * It also appears the host to card DMA can corrupt + * data when the buffer is not aligned on a 64 byte + * boundary. */ host->pio_size = data->blocks * data->blksz; host->pio_ptr = sg_virt(data->sg); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index ad111422ad55..5316d9b9e7b4 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -373,13 +373,9 @@ static void mxcmci_dma_callback(void *data) del_timer(&host->watchdog); stat = mxcmci_readl(host, MMC_REG_STATUS); - mxcmci_writel(host, stat & ~STATUS_DATA_TRANS_DONE, MMC_REG_STATUS); dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); - if (stat & STATUS_READ_OP_DONE) - mxcmci_writel(host, STATUS_READ_OP_DONE, MMC_REG_STATUS); - mxcmci_data_done(host, stat); } @@ -743,10 +739,8 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio; spin_unlock_irqrestore(&host->lock, flags); - if (mxcmci_use_dma(host) && - (stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE))) - mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE, - MMC_REG_STATUS); + if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE))) + mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS); if (sdio_irq) { mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS); @@ -756,8 +750,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) if (stat & STATUS_END_CMD_RESP) mxcmci_cmd_done(host, stat); - if (mxcmci_use_dma(host) && - (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) { + if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) { del_timer(&host->watchdog); mxcmci_data_done(host, stat); } @@ -1084,12 +1077,14 @@ static int mxcmci_probe(struct platform_device *pdev) dat3_card_detect = true; ret = mmc_regulator_get_supply(mmc); - if (ret) { - if (pdata && ret != -EPROBE_DEFER) - mmc->ocr_avail = pdata->ocr_avail ? : - MMC_VDD_32_33 | MMC_VDD_33_34; + if (ret == -EPROBE_DEFER) + goto out_free; + + if (!mmc->ocr_avail) { + if (pdata && pdata->ocr_avail) + mmc->ocr_avail = pdata->ocr_avail; else - goto out_free; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; } if (dat3_card_detect) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index cd74e5143c36..60c4ca97a727 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -581,10 +581,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) struct regulator *reg_vmmc; struct mxs_ssp *ssp; - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); - if (!iores || irq_err < 0) - return -EINVAL; + if (irq_err < 0) + return irq_err; mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev); if (!mmc) @@ -593,6 +592,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) host = mmc_priv(mmc); ssp = &host->ssp; ssp->dev = &pdev->dev; + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssp->base = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(ssp->base)) { ret = PTR_ERR(ssp->base); @@ -619,7 +619,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) ret = PTR_ERR(ssp->clk); goto out_mmc_free; } - clk_prepare_enable(ssp->clk); + ret = clk_prepare_enable(ssp->clk); + if (ret) + goto out_mmc_free; ret = mxs_mmc_reset(host); if (ret) { @@ -660,7 +662,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mmc); ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0, - DRIVER_NAME, host); + dev_name(&pdev->dev), host); if (ret) goto out_free_dma; @@ -702,7 +704,7 @@ static int mxs_mmc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mxs_mmc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); @@ -719,25 +721,19 @@ static int mxs_mmc_resume(struct device *dev) struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - clk_prepare_enable(ssp->clk); - return 0; + return clk_prepare_enable(ssp->clk); } - -static const struct dev_pm_ops mxs_mmc_pm_ops = { - .suspend = mxs_mmc_suspend, - .resume = mxs_mmc_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume); + static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, .remove = mxs_mmc_remove, .id_table = mxs_ssp_ids, .driver = { .name = DRIVER_NAME, -#ifdef CONFIG_PM .pm = &mxs_mmc_pm_ops, -#endif .of_match_table = mxs_mmc_dt_ids, }, }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index df27bb4fc098..7c71dcdcba8b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -42,7 +42,7 @@ #include <linux/regulator/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> -#include <linux/platform_data/mmc-omap.h> +#include <linux/platform_data/hsmmc-omap.h> /* OMAP HSMMC Host Controller Registers */ #define OMAP_HSMMC_SYSSTATUS 0x0014 @@ -155,7 +155,7 @@ * omap.c controller driver. Luckily this is not currently done on any known * omap_hsmmc.c device. */ -#define mmc_slot(host) (host->pdata->slots[host->slot_id]) +#define mmc_pdata(host) host->pdata /* * MMC Host controller read/write API's @@ -207,7 +207,6 @@ struct omap_hsmmc_host { int use_dma, dma_ch; struct dma_chan *tx_chan; struct dma_chan *rx_chan; - int slot_id; int response_busy; int context_loss; int protect_card; @@ -220,7 +219,26 @@ struct omap_hsmmc_host { #define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */ #define HSMMC_WAKE_IRQ_ENABLED (1 << 2) struct omap_hsmmc_next next_data; - struct omap_mmc_platform_data *pdata; + struct omap_hsmmc_platform_data *pdata; + + /* To handle board related suspend/resume functionality for MMC */ + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + + /* return MMC cover switch state, can be NULL if not supported. + * + * possible return values: + * 0 - closed + * 1 - open + */ + int (*get_cover_state)(struct device *dev); + + /* Card detection IRQs */ + int card_detect_irq; + + int (*card_detect)(struct device *dev); + int (*get_ro)(struct device *dev); + }; struct omap_mmc_of_data { @@ -230,50 +248,48 @@ struct omap_mmc_of_data { static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host); -static int omap_hsmmc_card_detect(struct device *dev, int slot) +static int omap_hsmmc_card_detect(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; + struct omap_hsmmc_platform_data *mmc = host->pdata; /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); + return !gpio_get_value_cansleep(mmc->switch_pin); } -static int omap_hsmmc_get_wp(struct device *dev, int slot) +static int omap_hsmmc_get_wp(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; + struct omap_hsmmc_platform_data *mmc = host->pdata; /* NOTE: assumes write protect signal is active-high */ - return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); + return gpio_get_value_cansleep(mmc->gpio_wp); } -static int omap_hsmmc_get_cover_state(struct device *dev, int slot) +static int omap_hsmmc_get_cover_state(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; + struct omap_hsmmc_platform_data *mmc = host->pdata; /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); + return !gpio_get_value_cansleep(mmc->switch_pin); } #ifdef CONFIG_PM -static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot) +static int omap_hsmmc_suspend_cdirq(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; - disable_irq(mmc->slots[0].card_detect_irq); + disable_irq(host->card_detect_irq); return 0; } -static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) +static int omap_hsmmc_resume_cdirq(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_mmc_platform_data *mmc = host->pdata; - enable_irq(mmc->slots[0].card_detect_irq); + enable_irq(host->card_detect_irq); return 0; } @@ -286,8 +302,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) #ifdef CONFIG_REGULATOR -static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, - int vdd) +static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) { struct omap_hsmmc_host *host = platform_get_drvdata(to_platform_device(dev)); @@ -300,8 +315,8 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, if (!host->vcc) return 0; - if (mmc_slot(host).before_set_reg) - mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); + if (mmc_pdata(host)->before_set_reg) + mmc_pdata(host)->before_set_reg(dev, power_on, vdd); if (host->pbias) { if (host->pbias_enabled == 1) { @@ -363,8 +378,8 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, } } - if (mmc_slot(host).after_set_reg) - mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); + if (mmc_pdata(host)->after_set_reg) + mmc_pdata(host)->after_set_reg(dev, power_on, vdd); error_set_power: return ret; @@ -383,18 +398,18 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } else { host->vcc = reg; ocr_value = mmc_regulator_get_ocrmask(reg); - if (!mmc_slot(host).ocr_mask) { - mmc_slot(host).ocr_mask = ocr_value; + if (!mmc_pdata(host)->ocr_mask) { + mmc_pdata(host)->ocr_mask = ocr_value; } else { - if (!(mmc_slot(host).ocr_mask & ocr_value)) { + if (!(mmc_pdata(host)->ocr_mask & ocr_value)) { dev_err(host->dev, "ocrmask %x is not supported\n", - mmc_slot(host).ocr_mask); - mmc_slot(host).ocr_mask = 0; + mmc_pdata(host)->ocr_mask); + mmc_pdata(host)->ocr_mask = 0; return -EINVAL; } } } - mmc_slot(host).set_power = omap_hsmmc_set_power; + mmc_pdata(host)->set_power = omap_hsmmc_set_power; /* Allow an aux regulator */ reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); @@ -404,7 +419,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) host->pbias = IS_ERR(reg) ? NULL : reg; /* For eMMC do not power off when not in sleep state */ - if (mmc_slot(host).no_regulator_off_init) + if (mmc_pdata(host)->no_regulator_off_init) return 0; /* * To disable boot_on regulator, enable regulator @@ -412,10 +427,10 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) */ if ((host->vcc && regulator_is_enabled(host->vcc) > 0) || (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { - int vdd = ffs(mmc_slot(host).ocr_mask) - 1; + int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1; - mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); - mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + mmc_pdata(host)->set_power(host->dev, 1, vdd); + mmc_pdata(host)->set_power(host->dev, 0, 0); } return 0; @@ -423,7 +438,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) { - mmc_slot(host).set_power = NULL; + mmc_pdata(host)->set_power = NULL; } static inline int omap_hsmmc_have_reg(void) @@ -449,55 +464,59 @@ static inline int omap_hsmmc_have_reg(void) #endif -static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata) +static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host, + struct omap_hsmmc_platform_data *pdata) { int ret; - if (gpio_is_valid(pdata->slots[0].switch_pin)) { - if (pdata->slots[0].cover) - pdata->slots[0].get_cover_state = - omap_hsmmc_get_cover_state; + if (gpio_is_valid(pdata->switch_pin)) { + if (pdata->cover) + host->get_cover_state = + omap_hsmmc_get_cover_state; else - pdata->slots[0].card_detect = omap_hsmmc_card_detect; - pdata->slots[0].card_detect_irq = - gpio_to_irq(pdata->slots[0].switch_pin); - ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd"); + host->card_detect = omap_hsmmc_card_detect; + host->card_detect_irq = + gpio_to_irq(pdata->switch_pin); + ret = gpio_request(pdata->switch_pin, "mmc_cd"); if (ret) return ret; - ret = gpio_direction_input(pdata->slots[0].switch_pin); + ret = gpio_direction_input(pdata->switch_pin); if (ret) goto err_free_sp; - } else - pdata->slots[0].switch_pin = -EINVAL; + } else { + pdata->switch_pin = -EINVAL; + } - if (gpio_is_valid(pdata->slots[0].gpio_wp)) { - pdata->slots[0].get_ro = omap_hsmmc_get_wp; - ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp"); + if (gpio_is_valid(pdata->gpio_wp)) { + host->get_ro = omap_hsmmc_get_wp; + ret = gpio_request(pdata->gpio_wp, "mmc_wp"); if (ret) goto err_free_cd; - ret = gpio_direction_input(pdata->slots[0].gpio_wp); + ret = gpio_direction_input(pdata->gpio_wp); if (ret) goto err_free_wp; - } else - pdata->slots[0].gpio_wp = -EINVAL; + } else { + pdata->gpio_wp = -EINVAL; + } return 0; err_free_wp: - gpio_free(pdata->slots[0].gpio_wp); + gpio_free(pdata->gpio_wp); err_free_cd: - if (gpio_is_valid(pdata->slots[0].switch_pin)) + if (gpio_is_valid(pdata->switch_pin)) err_free_sp: - gpio_free(pdata->slots[0].switch_pin); + gpio_free(pdata->switch_pin); return ret; } -static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata) +static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host, + struct omap_hsmmc_platform_data *pdata) { - if (gpio_is_valid(pdata->slots[0].gpio_wp)) - gpio_free(pdata->slots[0].gpio_wp); - if (gpio_is_valid(pdata->slots[0].switch_pin)) - gpio_free(pdata->slots[0].switch_pin); + if (gpio_is_valid(pdata->gpio_wp)) + gpio_free(pdata->gpio_wp); + if (gpio_is_valid(pdata->switch_pin)) + gpio_free(pdata->switch_pin); } /* @@ -607,8 +626,9 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) * in capabilities register * - MMC/SD clock coming out of controller > 25MHz */ - if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && + if ((mmc_pdata(host)->features & HSMMC_HAS_HSPE_SUPPORT) && (ios->timing != MMC_TIMING_MMC_DDR52) && + (ios->timing != MMC_TIMING_UHS_DDR50) && ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { regval = OMAP_HSMMC_READ(host->base, HCTL); if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) @@ -628,7 +648,8 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host) u32 con; con = OMAP_HSMMC_READ(host->base, CON); - if (ios->timing == MMC_TIMING_MMC_DDR52) + if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_UHS_DDR50) con |= DDR; /* configure in DDR mode */ else con &= ~DDR; @@ -791,8 +812,8 @@ int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host) { int r = 1; - if (mmc_slot(host).get_cover_state) - r = mmc_slot(host).get_cover_state(host->dev, host->slot_id); + if (host->get_cover_state) + r = host->get_cover_state(host->dev); return r; } @@ -816,7 +837,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr, struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); struct omap_hsmmc_host *host = mmc_priv(mmc); - return sprintf(buf, "%s\n", mmc_slot(host).name); + return sprintf(buf, "%s\n", mmc_pdata(host)->name); } static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); @@ -1061,7 +1082,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, * OMAP4 ES2 and greater has an updated reset logic. * Monitor a 0->1 transition first */ - if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { + if (mmc_pdata(host)->features & HSMMC_HAS_UPDATED_RESET) { while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) && (i++ < limit)) udelay(1); @@ -1210,12 +1231,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) clk_disable_unprepare(host->dbclk); /* Turn the power off */ - ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + ret = mmc_pdata(host)->set_power(host->dev, 0, 0); /* Turn the power ON with given VDD 1.8 or 3.0v */ if (!ret) - ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, - vdd); + ret = mmc_pdata(host)->set_power(host->dev, 1, vdd); pm_runtime_get_sync(host->dev); if (host->dbclk) clk_prepare_enable(host->dbclk); @@ -1259,11 +1279,11 @@ err: /* Protect the card while the cover is open */ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) { - if (!mmc_slot(host).get_cover_state) + if (!host->get_cover_state) return; host->reqs_blocked = 0; - if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { + if (host->get_cover_state(host->dev)) { if (host->protect_card) { dev_info(host->dev, "%s: cover is closed, " "card is now accessible\n", @@ -1286,13 +1306,12 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) { struct omap_hsmmc_host *host = dev_id; - struct omap_mmc_slot_data *slot = &mmc_slot(host); int carddetect; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); - if (slot->card_detect) - carddetect = slot->card_detect(host->dev, host->slot_id); + if (host->card_detect) + carddetect = host->card_detect(host->dev); else { omap_hsmmc_protect_card(host); carddetect = -ENOSYS; @@ -1618,12 +1637,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode != host->power_mode) { switch (ios->power_mode) { case MMC_POWER_OFF: - mmc_slot(host).set_power(host->dev, host->slot_id, - 0, 0); + mmc_pdata(host)->set_power(host->dev, 0, 0); break; case MMC_POWER_UP: - mmc_slot(host).set_power(host->dev, host->slot_id, - 1, ios->vdd); + mmc_pdata(host)->set_power(host->dev, 1, ios->vdd); break; case MMC_POWER_ON: do_send_init_stream = 1; @@ -1668,26 +1685,26 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc) { struct omap_hsmmc_host *host = mmc_priv(mmc); - if (!mmc_slot(host).card_detect) + if (!host->card_detect) return -ENOSYS; - return mmc_slot(host).card_detect(host->dev, host->slot_id); + return host->card_detect(host->dev); } static int omap_hsmmc_get_ro(struct mmc_host *mmc) { struct omap_hsmmc_host *host = mmc_priv(mmc); - if (!mmc_slot(host).get_ro) + if (!host->get_ro) return -ENOSYS; - return mmc_slot(host).get_ro(host->dev, 0); + return host->get_ro(host->dev); } static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) { struct omap_hsmmc_host *host = mmc_priv(mmc); - if (mmc_slot(host).init_card) - mmc_slot(host).init_card(card); + if (mmc_pdata(host)->init_card) + mmc_pdata(host)->init_card(card); } static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1957,9 +1974,9 @@ static const struct of_device_id omap_mmc_of_match[] = { }; MODULE_DEVICE_TABLE(of, omap_mmc_of_match); -static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) +static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { - struct omap_mmc_platform_data *pdata; + struct omap_hsmmc_platform_data *pdata; struct device_node *np = dev->of_node; u32 bus_width, max_freq; int cd_gpio, wp_gpio; @@ -1976,40 +1993,38 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (of_find_property(np, "ti,dual-volt", NULL)) pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; - /* This driver only supports 1 slot */ - pdata->nr_slots = 1; - pdata->slots[0].switch_pin = cd_gpio; - pdata->slots[0].gpio_wp = wp_gpio; + pdata->switch_pin = cd_gpio; + pdata->gpio_wp = wp_gpio; if (of_find_property(np, "ti,non-removable", NULL)) { - pdata->slots[0].nonremovable = true; - pdata->slots[0].no_regulator_off_init = true; + pdata->nonremovable = true; + pdata->no_regulator_off_init = true; } of_property_read_u32(np, "bus-width", &bus_width); if (bus_width == 4) - pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA; + pdata->caps |= MMC_CAP_4_BIT_DATA; else if (bus_width == 8) - pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA; + pdata->caps |= MMC_CAP_8_BIT_DATA; if (of_find_property(np, "ti,needs-special-reset", NULL)) - pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET; + pdata->features |= HSMMC_HAS_UPDATED_RESET; if (!of_property_read_u32(np, "max-frequency", &max_freq)) pdata->max_freq = max_freq; if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) - pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; + pdata->features |= HSMMC_HAS_HSPE_SUPPORT; if (of_find_property(np, "keep-power-in-suspend", NULL)) - pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER; + pdata->pm_caps |= MMC_PM_KEEP_POWER; if (of_find_property(np, "enable-sdio-wakeup", NULL)) - pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; return pdata; } #else -static inline struct omap_mmc_platform_data +static inline struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { return ERR_PTR(-EINVAL); @@ -2018,7 +2033,7 @@ static inline struct omap_mmc_platform_data static int omap_hsmmc_probe(struct platform_device *pdev) { - struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; + struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_host *mmc; struct omap_hsmmc_host *host = NULL; struct resource *res; @@ -2048,11 +2063,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) return -ENXIO; } - if (pdata->nr_slots == 0) { - dev_err(&pdev->dev, "No Slots\n"); - return -ENXIO; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (res == NULL || irq < 0) @@ -2062,14 +2072,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - ret = omap_hsmmc_gpio_init(pdata); - if (ret) - goto err; - mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; - goto err_alloc; + goto err; } host = mmc_priv(mmc); @@ -2079,13 +2085,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->use_dma = 1; host->dma_ch = -1; host->irq = irq; - host->slot_id = 0; host->mapbase = res->start + pdata->reg_offset; host->base = base + pdata->reg_offset; host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; host->pbias_enabled = 0; + ret = omap_hsmmc_gpio_init(host, pdata); + if (ret) + goto err_gpio; + platform_set_drvdata(pdev, host); if (pdev->dev.of_node) @@ -2144,14 +2153,14 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - mmc->caps |= mmc_slot(host).caps; + mmc->caps |= mmc_pdata(host)->caps; if (mmc->caps & MMC_CAP_8_BIT_DATA) mmc->caps |= MMC_CAP_4_BIT_DATA; - if (mmc_slot(host).nonremovable) + if (mmc_pdata(host)->nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; - mmc->pm_caps = mmc_slot(host).pm_caps; + mmc->pm_caps = mmc_pdata(host)->pm_caps; omap_hsmmc_conf_bus_power(host); @@ -2204,27 +2213,19 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err_irq; } - if (pdata->init != NULL) { - if (pdata->init(&pdev->dev) != 0) { - dev_err(mmc_dev(host->mmc), - "Unable to configure MMC IRQs\n"); - goto err_irq; - } - } - - if (omap_hsmmc_have_reg() && !mmc_slot(host).set_power) { + if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) { ret = omap_hsmmc_reg_get(host); if (ret) - goto err_reg; + goto err_irq; host->use_reg = 1; } - mmc->ocr_avail = mmc_slot(host).ocr_mask; + mmc->ocr_avail = mmc_pdata(host)->ocr_mask; /* Request IRQ for card detect */ - if ((mmc_slot(host).card_detect_irq)) { + if (host->card_detect_irq) { ret = devm_request_threaded_irq(&pdev->dev, - mmc_slot(host).card_detect_irq, + host->card_detect_irq, NULL, omap_hsmmc_detect, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, mmc_hostname(mmc), host); @@ -2233,8 +2234,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev) "Unable to grab MMC CD IRQ\n"); goto err_irq_cd; } - pdata->suspend = omap_hsmmc_suspend_cdirq; - pdata->resume = omap_hsmmc_resume_cdirq; + host->suspend = omap_hsmmc_suspend_cdirq; + host->resume = omap_hsmmc_resume_cdirq; } omap_hsmmc_disable_irq(host); @@ -2255,12 +2256,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc_add_host(mmc); - if (mmc_slot(host).name != NULL) { + if (mmc_pdata(host)->name != NULL) { ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); if (ret < 0) goto err_slot_name; } - if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) { + if (host->card_detect_irq && host->get_cover_state) { ret = device_create_file(&mmc->class_dev, &dev_attr_cover_switch); if (ret < 0) @@ -2278,9 +2279,6 @@ err_slot_name: err_irq_cd: if (host->use_reg) omap_hsmmc_reg_put(host); -err_reg: - if (host->pdata->cleanup) - host->pdata->cleanup(&pdev->dev); err_irq: if (host->tx_chan) dma_release_channel(host->tx_chan); @@ -2291,9 +2289,9 @@ err_irq: if (host->dbclk) clk_disable_unprepare(host->dbclk); err1: + omap_hsmmc_gpio_free(host, pdata); +err_gpio: mmc_free_host(mmc); -err_alloc: - omap_hsmmc_gpio_free(pdata); err: return ret; } @@ -2306,8 +2304,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) mmc_remove_host(host->mmc); if (host->use_reg) omap_hsmmc_reg_put(host); - if (host->pdata->cleanup) - host->pdata->cleanup(&pdev->dev); if (host->tx_chan) dma_release_channel(host->tx_chan); @@ -2319,7 +2315,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) if (host->dbclk) clk_disable_unprepare(host->dbclk); - omap_hsmmc_gpio_free(host->pdata); + omap_hsmmc_gpio_free(host, host->pdata); mmc_free_host(host->mmc); return 0; @@ -2330,8 +2326,8 @@ static int omap_hsmmc_prepare(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - if (host->pdata->suspend) - return host->pdata->suspend(dev, host->slot_id); + if (host->suspend) + return host->suspend(dev); return 0; } @@ -2340,8 +2336,8 @@ static void omap_hsmmc_complete(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - if (host->pdata->resume) - host->pdata->resume(dev, host->slot_id); + if (host->resume) + host->resume(dev); } diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 9cccc0e89b04..daba49ac1242 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -76,6 +76,7 @@ struct sdhci_acpi_host { const struct sdhci_acpi_slot *slot; struct platform_device *pdev; bool use_runtime_pm; + bool dma_setup; }; static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) @@ -85,7 +86,29 @@ static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) static int sdhci_acpi_enable_dma(struct sdhci_host *host) { - return 0; + struct sdhci_acpi_host *c = sdhci_priv(host); + struct device *dev = &c->pdev->dev; + int err = -1; + + if (c->dma_setup) + return 0; + + if (host->flags & SDHCI_USE_64_BIT_DMA) { + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) { + host->flags &= ~SDHCI_USE_64_BIT_DMA; + } else { + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + dev_warn(dev, "Failed to set 64-bit DMA mask\n"); + } + } + + if (err) + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + + c->dma_setup = !err; + + return err; } static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) @@ -180,17 +203,21 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .chip = &sdhci_acpi_chip_int, .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR, + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | + MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, .probe_slot = sdhci_acpi_emmc_probe_slot, }; static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { - .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, - .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD, + .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD | + MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, .flags = SDHCI_ACPI_RUNTIME_PM, .pm_caps = MMC_PM_KEEP_POWER, .probe_slot = sdhci_acpi_sdio_probe_slot, @@ -199,8 +226,10 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL | SDHCI_ACPI_RUNTIME_PM, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_STOP_WITH_TC, + .caps = MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, .probe_slot = sdhci_acpi_sd_probe_slot, }; @@ -305,21 +334,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev) goto err_free; } - if (!dev->dma_mask) { - u64 dma_mask; - - if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) { - /* 64-bit DMA is not supported at present */ - dma_mask = DMA_BIT_MASK(32); - } else { - dma_mask = DMA_BIT_MASK(32); - } - - err = dma_coerce_mask_and_coherent(dev, dma_mask); - if (err) - goto err_free; - } - if (c->slot) { if (c->slot->probe_slot) { err = c->slot->probe_slot(pdev, hid, uid); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 587ee0edeb57..12711ab51aed 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -65,8 +65,6 @@ /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ #define ESDHC_TUNING_START_TAP 0x1 -#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64 - /* pinctrl state */ #define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" #define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz" @@ -692,8 +690,6 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) /* FIXME: delay a bit for card to be ready for next tuning due to errors */ mdelay(1); - /* This is balanced by the runtime put in sdhci_tasklet_finish */ - pm_runtime_get_sync(host->mmc->parent); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL; @@ -704,54 +700,6 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS)); } -static void esdhc_request_done(struct mmc_request *mrq) -{ - complete(&mrq->completion); -} - -static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, - struct scatterlist *sg) -{ - struct mmc_command cmd = {0}; - struct mmc_request mrq = {NULL}; - struct mmc_data data = {0}; - - cmd.opcode = opcode; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = sg; - data.sg_len = 1; - - mrq.cmd = &cmd; - mrq.cmd->mrq = &mrq; - mrq.data = &data; - mrq.data->mrq = &mrq; - mrq.cmd->data = mrq.data; - - mrq.done = esdhc_request_done; - init_completion(&(mrq.completion)); - - spin_lock_irq(&host->lock); - host->mrq = &mrq; - - sdhci_send_command(host, mrq.cmd); - - spin_unlock_irq(&host->lock); - - wait_for_completion(&mrq.completion); - - if (cmd.error) - return cmd.error; - if (data.error) - return data.error; - - return 0; -} - static void esdhc_post_tuning(struct sdhci_host *host) { u32 reg; @@ -763,21 +711,13 @@ static void esdhc_post_tuning(struct sdhci_host *host) static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) { - struct scatterlist sg; - char *tuning_pattern; int min, max, avg, ret; - tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL); - if (!tuning_pattern) - return -ENOMEM; - - sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN); - /* find the mininum delay first which can pass tuning */ min = ESDHC_TUNE_CTRL_MIN; while (min < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, min); - if (!esdhc_send_tuning_cmd(host, opcode, &sg)) + if (!mmc_send_tuning(host->mmc)) break; min += ESDHC_TUNE_CTRL_STEP; } @@ -786,7 +726,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) max = min + ESDHC_TUNE_CTRL_STEP; while (max < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, max); - if (esdhc_send_tuning_cmd(host, opcode, &sg)) { + if (mmc_send_tuning(host->mmc)) { max -= ESDHC_TUNE_CTRL_STEP; break; } @@ -796,11 +736,9 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) /* use average delay to get the best timing */ avg = (min + max) / 2; esdhc_prepare_tuning(host, avg); - ret = esdhc_send_tuning_cmd(host, opcode, &sg); + ret = mmc_send_tuning(host->mmc); esdhc_post_tuning(host); - kfree(tuning_pattern); - dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", ret ? "failed" : "passed", avg, ret); @@ -1031,11 +969,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl, PINCTRL_STATE_DEFAULT); - if (IS_ERR(imx_data->pins_default)) { - err = PTR_ERR(imx_data->pins_default); - dev_err(mmc_dev(host->mmc), "could not get default state\n"); - goto disable_clk; - } + if (IS_ERR(imx_data->pins_default)) + dev_warn(mmc_dev(host->mmc), "could not get default state\n"); host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; @@ -1123,7 +1058,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) } /* sdr50 and sdr104 needs work on 1.8v signal voltage */ - if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) { + if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) && + !IS_ERR(imx_data->pins_default)) { imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, ESDHC_PINCTRL_STATE_100MHZ); imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 30804385af6d..3d32ce896b09 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -339,9 +339,7 @@ static int msm_init_cm_dll(struct sdhci_host *host) static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) { int tuning_seq_cnt = 3; - u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; - const u8 *tuning_block_pattern = tuning_blk_pattern_4bit; - int size = sizeof(tuning_blk_pattern_4bit); + u8 phase, tuned_phases[16], tuned_phase_cnt = 0; int rc; struct mmc_host *mmc = host->mmc; struct mmc_ios ios = host->mmc->ios; @@ -355,53 +353,21 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) (ios.timing == MMC_TIMING_UHS_SDR104))) return 0; - if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && - (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { - tuning_block_pattern = tuning_blk_pattern_8bit; - size = sizeof(tuning_blk_pattern_8bit); - } - - data_buf = kmalloc(size, GFP_KERNEL); - if (!data_buf) - return -ENOMEM; - retry: /* First of all reset the tuning block */ rc = msm_init_cm_dll(host); if (rc) - goto out; + return rc; phase = 0; do { - struct mmc_command cmd = { 0 }; - struct mmc_data data = { 0 }; - struct mmc_request mrq = { - .cmd = &cmd, - .data = &data - }; - struct scatterlist sg; - /* Set the phase in delay line hw block */ rc = msm_config_cm_dll_phase(host, phase); if (rc) - goto out; + return rc; - cmd.opcode = opcode; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - data.blksz = size; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.timeout_ns = NSEC_PER_SEC; /* 1 second */ - - data.sg = &sg; - data.sg_len = 1; - sg_init_one(&sg, data_buf, size); - memset(data_buf, 0, size); - mmc_wait_for_req(mmc, &mrq); - - if (!cmd.error && !data.error && - !memcmp(data_buf, tuning_block_pattern, size)) { + rc = mmc_send_tuning(mmc); + if (!rc) { /* Tuning is successful at this tuning point */ tuned_phases[tuned_phase_cnt++] = phase; dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", @@ -413,7 +379,7 @@ retry: rc = msm_find_most_appropriate_phase(host, tuned_phases, tuned_phase_cnt); if (rc < 0) - goto out; + return rc; else phase = rc; @@ -423,7 +389,7 @@ retry: */ rc = msm_config_cm_dll_phase(host, phase); if (rc) - goto out; + return rc; dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", mmc_hostname(mmc), phase); } else { @@ -435,8 +401,6 @@ retry: rc = -EIO; } -out: - kfree(data_buf); return rc; } diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 981d66e5c023..bcb51e9dfdcd 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -165,7 +165,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0); if (IS_ERR(host)) { ret = PTR_ERR(host); - dev_err(&pdev->dev, "platform init failed (%u)\n", ret); goto clk_disable_all; } @@ -175,10 +174,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) pltfm_host->clk = clk_xin; ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "platform register failed (%u)\n", ret); + if (ret) goto err_pltfm_free; - } return 0; diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 5670e381b0cf..e2ec108dba0e 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -127,8 +127,6 @@ void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) return; scratch_32 &= ~((1 << 21) | (1 << 30)); - /* Set RTD3 function disabled */ - scratch_32 |= ((1 << 29) | (1 << 28)); pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); /* Set L1 Entrance Timer */ diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 61192973e7cb..95f73007fccf 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -269,7 +269,9 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host) static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR; + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | + MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->hw_reset = sdhci_pci_int_hw_reset; if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) @@ -279,12 +281,16 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE; + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE | + MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; return 0; } static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) { + slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; slot->cd_con_id = NULL; slot->cd_idx = 0; slot->cd_override_level = true; @@ -294,11 +300,13 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, }; static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, @@ -306,6 +314,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { }; static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, @@ -645,6 +654,25 @@ static const struct sdhci_pci_fixes sdhci_rtsx = { .probe_slot = rtsx_probe_slot, }; +static int amd_probe(struct sdhci_pci_chip *chip) +{ + struct pci_dev *smbus_dev; + + smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); + + if (smbus_dev && (smbus_dev->revision < 0x51)) { + chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; + chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; + } + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_amd = { + .probe = amd_probe, +}; + static const struct pci_device_id pci_ids[] = { { .vendor = PCI_VENDOR_ID_RICOH, @@ -1044,7 +1072,15 @@ static const struct pci_device_id pci_ids[] = { .subdevice = PCI_ANY_ID, .driver_data = (kernel_ulong_t)&sdhci_o2, }, - + { + .vendor = PCI_VENDOR_ID_AMD, + .device = PCI_ANY_ID, + .class = PCI_CLASS_SYSTEM_SDHCI << 8, + .class_mask = 0xFFFF00, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_amd, + }, { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, @@ -1064,7 +1100,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) { struct sdhci_pci_slot *slot; struct pci_dev *pdev; - int ret; + int ret = -1; slot = sdhci_priv(host); pdev = slot->chip->pdev; @@ -1076,7 +1112,17 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) "doesn't fully claim to support it.\n"); } - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (host->flags & SDHCI_USE_64_BIT_DMA) { + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) { + host->flags &= ~SDHCI_USE_64_BIT_DMA; + } else { + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (ret) + dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n"); + } + } + if (ret) + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) return ret; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index b4c23e983baf..f98008b5ea77 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -167,23 +167,17 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; - struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; int ret; struct clk *clk; - pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); - if (!pxa) - return -ENOMEM; - host = sdhci_pltfm_init(pdev, NULL, 0); - if (IS_ERR(host)) { - kfree(pxa); + if (IS_ERR(host)) return PTR_ERR(host); - } + pltfm_host = sdhci_priv(host); - pltfm_host->priv = pxa; + pltfm_host->priv = NULL; clk = clk_get(dev, "PXA-SDHCLK"); if (IS_ERR(clk)) { @@ -238,7 +232,6 @@ err_add_host: clk_put(clk); err_clk_get: sdhci_pltfm_free(pdev); - kfree(pxa); return ret; } @@ -246,14 +239,12 @@ static int sdhci_pxav2_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_pxa *pxa = pltfm_host->priv; sdhci_remove_host(host, 1); clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); sdhci_pltfm_free(pdev); - kfree(pxa); return 0; } diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 5036d7d39529..ad0badad0ebc 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -58,6 +58,12 @@ #define SDCE_MISC_INT (1<<2) #define SDCE_MISC_INT_EN (1<<1) +struct sdhci_pxa { + struct clk *clk_core; + struct clk *clk_io; + u8 power_mode; +}; + /* * These registers are relative to the second register region, for the * MBus bridge. @@ -211,6 +217,7 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) case MMC_TIMING_UHS_SDR104: ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180; break; + case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180; break; @@ -283,9 +290,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; - int ret; - struct clk *clk; pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) @@ -305,14 +310,20 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; - clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) { + pxa->clk_io = devm_clk_get(dev, "io"); + if (IS_ERR(pxa->clk_io)) + pxa->clk_io = devm_clk_get(dev, NULL); + if (IS_ERR(pxa->clk_io)) { dev_err(dev, "failed to get io clock\n"); - ret = PTR_ERR(clk); + ret = PTR_ERR(pxa->clk_io); goto err_clk_get; } - pltfm_host->clk = clk; - clk_prepare_enable(clk); + pltfm_host->clk = pxa->clk_io; + clk_prepare_enable(pxa->clk_io); + + pxa->clk_core = devm_clk_get(dev, "core"); + if (!IS_ERR(pxa->clk_core)) + clk_prepare_enable(pxa->clk_core); /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -385,7 +396,9 @@ err_add_host: pm_runtime_disable(&pdev->dev); err_of_parse: err_cd_req: - clk_disable_unprepare(clk); + clk_disable_unprepare(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_disable_unprepare(pxa->clk_core); err_clk_get: err_mbus_win: sdhci_pltfm_free(pdev); @@ -396,12 +409,15 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; pm_runtime_get_sync(&pdev->dev); sdhci_remove_host(host, 1); pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_disable_unprepare(pxa->clk_core); sdhci_pltfm_free(pdev); @@ -441,15 +457,16 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; unsigned long flags; - if (pltfm_host->clk) { - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = true; - spin_unlock_irqrestore(&host->lock, flags); + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = true; + spin_unlock_irqrestore(&host->lock, flags); - clk_disable_unprepare(pltfm_host->clk); - } + clk_disable_unprepare(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_disable_unprepare(pxa->clk_core); return 0; } @@ -458,15 +475,16 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; unsigned long flags; - if (pltfm_host->clk) { - clk_prepare_enable(pltfm_host->clk); + clk_prepare_enable(pxa->clk_io); + if (!IS_ERR(pxa->clk_core)) + clk_prepare_enable(pxa->clk_core); - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = false; - spin_unlock_irqrestore(&host->lock, flags); - } + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = false; + spin_unlock_irqrestore(&host->lock, flags); return 0; } diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 0ce6eb17deaf..4f7a63213b33 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -300,6 +300,7 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) struct device *dev = &ourhost->pdev->dev; unsigned long timeout; u16 clk = 0; + int ret; host->mmc->actual_clock = 0; @@ -311,7 +312,12 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_s3c_set_clock(host, clock); - clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + if (ret != 0) { + dev_err(dev, "%s: failed to set clock rate %uHz\n", + mmc_hostname(host->mmc), clock); + return; + } clk = SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ada1a3ea3a87..73de62a58d70 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -44,8 +44,6 @@ #define MAX_TUNING_LOOP 40 -#define ADMA_SIZE ((128 * 2 + 1) * 4) - static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -119,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host) pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", sdhci_readw(host, SDHCI_HOST_CONTROL2)); - if (host->flags & SDHCI_USE_ADMA) - pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", - readl(host->ioaddr + SDHCI_ADMA_ERROR), - readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + if (host->flags & SDHCI_USE_ADMA) { + if (host->flags & SDHCI_USE_64_BIT_DMA) + pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + else + pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + } pr_debug(DRIVER_NAME ": ===========================================\n"); } @@ -448,18 +453,26 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) local_irq_restore(*flags); } -static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd) +static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc, + dma_addr_t addr, int len, unsigned cmd) { - __le32 *dataddr = (__le32 __force *)(desc + 4); - __le16 *cmdlen = (__le16 __force *)desc; + struct sdhci_adma2_64_desc *dma_desc = desc; + + /* 32-bit and 64-bit descriptors have these members in same position */ + dma_desc->cmd = cpu_to_le16(cmd); + dma_desc->len = cpu_to_le16(len); + dma_desc->addr_lo = cpu_to_le32((u32)addr); - /* SDHCI specification says ADMA descriptors should be 4 byte - * aligned, so using 16 or 32bit operations should be safe. */ + if (host->flags & SDHCI_USE_64_BIT_DMA) + dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32); +} - cmdlen[0] = cpu_to_le16(cmd); - cmdlen[1] = cpu_to_le16(len); +static void sdhci_adma_mark_end(void *desc) +{ + struct sdhci_adma2_64_desc *dma_desc = desc; - dataddr[0] = cpu_to_le32(addr); + /* 32-bit and 64-bit descriptors have 'cmd' in same position */ + dma_desc->cmd |= cpu_to_le16(ADMA2_END); } static int sdhci_adma_table_pre(struct sdhci_host *host, @@ -467,8 +480,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, { int direction; - u8 *desc; - u8 *align; + void *desc; + void *align; dma_addr_t addr; dma_addr_t align_addr; int len, offset; @@ -489,17 +502,17 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, direction = DMA_TO_DEVICE; host->align_addr = dma_map_single(mmc_dev(host->mmc), - host->align_buffer, 128 * 4, direction); + host->align_buffer, host->align_buffer_sz, direction); if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto fail; - BUG_ON(host->align_addr & 0x3); + BUG_ON(host->align_addr & host->align_mask); host->sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction); if (host->sg_count == 0) goto unmap_align; - desc = host->adma_desc; + desc = host->adma_table; align = host->align_buffer; align_addr = host->align_addr; @@ -515,24 +528,27 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, * the (up to three) bytes that screw up the * alignment. */ - offset = (4 - (addr & 0x3)) & 0x3; + offset = (host->align_sz - (addr & host->align_mask)) & + host->align_mask; if (offset) { if (data->flags & MMC_DATA_WRITE) { buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); + WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > + (PAGE_SIZE - offset)); memcpy(align, buffer, offset); sdhci_kunmap_atomic(buffer, &flags); } /* tran, valid */ - sdhci_set_adma_desc(desc, align_addr, offset, 0x21); + sdhci_adma_write_desc(host, desc, align_addr, offset, + ADMA2_TRAN_VALID); BUG_ON(offset > 65536); - align += 4; - align_addr += 4; + align += host->align_sz; + align_addr += host->align_sz; - desc += 8; + desc += host->desc_sz; addr += offset; len -= offset; @@ -541,23 +557,23 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, BUG_ON(len > 65536); /* tran, valid */ - sdhci_set_adma_desc(desc, addr, len, 0x21); - desc += 8; + sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID); + desc += host->desc_sz; /* * If this triggers then we have a calculation bug * somewhere. :/ */ - WARN_ON((desc - host->adma_desc) > ADMA_SIZE); + WARN_ON((desc - host->adma_table) >= host->adma_table_sz); } if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { /* * Mark the last descriptor as the terminating descriptor */ - if (desc != host->adma_desc) { - desc -= 8; - desc[0] |= 0x2; /* end */ + if (desc != host->adma_table) { + desc -= host->desc_sz; + sdhci_adma_mark_end(desc); } } else { /* @@ -565,7 +581,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, */ /* nop, end, valid */ - sdhci_set_adma_desc(desc, 0, 0, 0x3); + sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID); } /* @@ -573,14 +589,14 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, */ if (data->flags & MMC_DATA_WRITE) { dma_sync_single_for_device(mmc_dev(host->mmc), - host->align_addr, 128 * 4, direction); + host->align_addr, host->align_buffer_sz, direction); } return 0; unmap_align: dma_unmap_single(mmc_dev(host->mmc), host->align_addr, - 128 * 4, direction); + host->align_buffer_sz, direction); fail: return -EINVAL; } @@ -592,7 +608,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host, struct scatterlist *sg; int i, size; - u8 *align; + void *align; char *buffer; unsigned long flags; bool has_unaligned; @@ -603,12 +619,12 @@ static void sdhci_adma_table_post(struct sdhci_host *host, direction = DMA_TO_DEVICE; dma_unmap_single(mmc_dev(host->mmc), host->align_addr, - 128 * 4, direction); + host->align_buffer_sz, direction); /* Do a quick scan of the SG list for any unaligned mappings */ has_unaligned = false; for_each_sg(data->sg, sg, host->sg_count, i) - if (sg_dma_address(sg) & 3) { + if (sg_dma_address(sg) & host->align_mask) { has_unaligned = true; break; } @@ -620,15 +636,17 @@ static void sdhci_adma_table_post(struct sdhci_host *host, align = host->align_buffer; for_each_sg(data->sg, sg, host->sg_count, i) { - if (sg_dma_address(sg) & 0x3) { - size = 4 - (sg_dma_address(sg) & 0x3); + if (sg_dma_address(sg) & host->align_mask) { + size = host->align_sz - + (sg_dma_address(sg) & host->align_mask); buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); + WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > + (PAGE_SIZE - size)); memcpy(buffer, align, size); sdhci_kunmap_atomic(buffer, &flags); - align += 4; + align += host->align_sz; } } } @@ -822,6 +840,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } else { sdhci_writel(host, host->adma_addr, SDHCI_ADMA_ADDRESS); + if (host->flags & SDHCI_USE_64_BIT_DMA) + sdhci_writel(host, + (u64)host->adma_addr >> 32, + SDHCI_ADMA_ADDRESS_HI); } } else { int sg_cnt; @@ -855,10 +877,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; if ((host->flags & SDHCI_REQ_USE_DMA) && - (host->flags & SDHCI_USE_ADMA)) - ctrl |= SDHCI_CTRL_ADMA32; - else + (host->flags & SDHCI_USE_ADMA)) { + if (host->flags & SDHCI_USE_64_BIT_DMA) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + } else { ctrl |= SDHCI_CTRL_SDMA; + } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } @@ -889,10 +915,15 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, struct mmc_data *data = cmd->data; if (data == NULL) { + if (host->quirks2 & + SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) { + sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); + } else { /* clear Auto CMD settings for no data CMDs */ - mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); - sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); + sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); + } return; } @@ -1117,6 +1148,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) case MMC_TIMING_UHS_DDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; + case MMC_TIMING_MMC_HS400: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400); + break; default: pr_warn("%s: Invalid UHS-I mode selected\n", mmc_hostname(host->mmc)); @@ -1444,6 +1478,8 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) else if ((timing == MMC_TIMING_UHS_DDR50) || (timing == MMC_TIMING_MMC_DDR52)) ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); @@ -1515,7 +1551,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) u16 clk, ctrl_2; /* In case of UHS-I modes, set High Speed Enable */ - if ((ios->timing == MMC_TIMING_MMC_HS200) || + if ((ios->timing == MMC_TIMING_MMC_HS400) || + (ios->timing == MMC_TIMING_MMC_HS200) || (ios->timing == MMC_TIMING_MMC_DDR52) || (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || @@ -1862,6 +1899,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * tuning function has to be executed. */ switch (host->timing) { + case MMC_TIMING_MMC_HS400: case MMC_TIMING_MMC_HS200: case MMC_TIMING_UHS_SDR104: break; @@ -2144,9 +2182,10 @@ static void sdhci_tasklet_finish(unsigned long param) */ if (!(host->flags & SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->error) || - (mrq->data && (mrq->data->error || - (mrq->data->stop && mrq->data->stop->error))) || - (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { + (mrq->sbc && mrq->sbc->error) || + (mrq->data && ((mrq->data->error && !mrq->data->stop) || + (mrq->data->stop && mrq->data->stop->error))) || + (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) @@ -2282,32 +2321,36 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) } #ifdef CONFIG_MMC_DEBUG -static void sdhci_show_adma_error(struct sdhci_host *host) +static void sdhci_adma_show_error(struct sdhci_host *host) { const char *name = mmc_hostname(host->mmc); - u8 *desc = host->adma_desc; - __le32 *dma; - __le16 *len; - u8 attr; + void *desc = host->adma_table; sdhci_dumpregs(host); while (true) { - dma = (__le32 *)(desc + 4); - len = (__le16 *)(desc + 2); - attr = *desc; - - DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", - name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr); + struct sdhci_adma2_64_desc *dma_desc = desc; + + if (host->flags & SDHCI_USE_64_BIT_DMA) + DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", + name, desc, le32_to_cpu(dma_desc->addr_hi), + le32_to_cpu(dma_desc->addr_lo), + le16_to_cpu(dma_desc->len), + le16_to_cpu(dma_desc->cmd)); + else + DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", + name, desc, le32_to_cpu(dma_desc->addr_lo), + le16_to_cpu(dma_desc->len), + le16_to_cpu(dma_desc->cmd)); - desc += 8; + desc += host->desc_sz; - if (attr & 2) + if (dma_desc->cmd & cpu_to_le16(ADMA2_END)) break; } } #else -static void sdhci_show_adma_error(struct sdhci_host *host) { } +static void sdhci_adma_show_error(struct sdhci_host *host) { } #endif static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) @@ -2370,7 +2413,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) host->data->error = -EILSEQ; else if (intmask & SDHCI_INT_ADMA_ERROR) { pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); - sdhci_show_adma_error(host); + sdhci_adma_show_error(host); host->data->error = -EIO; if (host->ops->adma_workaround) host->ops->adma_workaround(host, intmask); @@ -2849,6 +2892,16 @@ int sdhci_add_host(struct sdhci_host *host) host->flags &= ~SDHCI_USE_ADMA; } + /* + * It is assumed that a 64-bit capable device has set a 64-bit DMA mask + * and *must* do 64-bit DMA. A driver has the opportunity to change + * that during the first call to ->enable_dma(). Similarly + * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to + * implement. + */ + if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) + host->flags |= SDHCI_USE_64_BIT_DMA; + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) { if (host->ops->enable_dma(host)) { @@ -2860,33 +2913,56 @@ int sdhci_add_host(struct sdhci_host *host) } } + /* SDMA does not support 64-bit DMA */ + if (host->flags & SDHCI_USE_64_BIT_DMA) + host->flags &= ~SDHCI_USE_SDMA; + if (host->flags & SDHCI_USE_ADMA) { /* - * We need to allocate descriptors for all sg entries - * (128) and potentially one alignment transfer for - * each of those entries. + * The DMA descriptor table size is calculated as the maximum + * number of segments times 2, to allow for an alignment + * descriptor for each segment, plus 1 for a nop end descriptor, + * all multipled by the descriptor size. */ - host->adma_desc = dma_alloc_coherent(mmc_dev(mmc), - ADMA_SIZE, &host->adma_addr, - GFP_KERNEL); - host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); - if (!host->adma_desc || !host->align_buffer) { - dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, - host->adma_desc, host->adma_addr); + if (host->flags & SDHCI_USE_64_BIT_DMA) { + host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * + SDHCI_ADMA2_64_DESC_SZ; + host->align_buffer_sz = SDHCI_MAX_SEGS * + SDHCI_ADMA2_64_ALIGN; + host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; + host->align_sz = SDHCI_ADMA2_64_ALIGN; + host->align_mask = SDHCI_ADMA2_64_ALIGN - 1; + } else { + host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * + SDHCI_ADMA2_32_DESC_SZ; + host->align_buffer_sz = SDHCI_MAX_SEGS * + SDHCI_ADMA2_32_ALIGN; + host->desc_sz = SDHCI_ADMA2_32_DESC_SZ; + host->align_sz = SDHCI_ADMA2_32_ALIGN; + host->align_mask = SDHCI_ADMA2_32_ALIGN - 1; + } + host->adma_table = dma_alloc_coherent(mmc_dev(mmc), + host->adma_table_sz, + &host->adma_addr, + GFP_KERNEL); + host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL); + if (!host->adma_table || !host->align_buffer) { + dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, + host->adma_table, host->adma_addr); kfree(host->align_buffer); pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; - host->adma_desc = NULL; + host->adma_table = NULL; host->align_buffer = NULL; - } else if (host->adma_addr & 3) { + } else if (host->adma_addr & host->align_mask) { pr_warn("%s: unable to allocate aligned ADMA descriptor\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; - dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, - host->adma_desc, host->adma_addr); + dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, + host->adma_table, host->adma_addr); kfree(host->align_buffer); - host->adma_desc = NULL; + host->adma_table = NULL; host->align_buffer = NULL; } } @@ -3027,7 +3103,7 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); - mmc->supply.vqmmc = NULL; + mmc->supply.vqmmc = ERR_PTR(-EINVAL); } } @@ -3046,16 +3122,21 @@ int sdhci_add_host(struct sdhci_host *host) /* SD3.0: SDR104 is supported so (for eMMC) the caps2 * field can be promoted to support HS200. */ - if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) { + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) mmc->caps2 |= MMC_CAP2_HS200; - if (IS_ERR(mmc->supply.vqmmc) || - !regulator_is_supported_voltage - (mmc->supply.vqmmc, 1100000, 1300000)) - mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR; - } } else if (caps[1] & SDHCI_SUPPORT_SDR50) mmc->caps |= MMC_CAP_UHS_SDR50; + if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 && + (caps[1] & SDHCI_SUPPORT_HS400)) + mmc->caps2 |= MMC_CAP2_HS400; + + if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) && + (IS_ERR(mmc->supply.vqmmc) || + !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000, + 1300000))) + mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V; + if ((caps[1] & SDHCI_SUPPORT_DDR50) && !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50)) mmc->caps |= MMC_CAP_UHS_DDR50; @@ -3175,11 +3256,11 @@ int sdhci_add_host(struct sdhci_host *host) * can do scatter/gather or not. */ if (host->flags & SDHCI_USE_ADMA) - mmc->max_segs = 128; + mmc->max_segs = SDHCI_MAX_SEGS; else if (host->flags & SDHCI_USE_SDMA) mmc->max_segs = 1; else /* PIO */ - mmc->max_segs = 128; + mmc->max_segs = SDHCI_MAX_SEGS; /* * Maximum number of sectors in one transfer. Limited by DMA boundary @@ -3277,7 +3358,8 @@ int sdhci_add_host(struct sdhci_host *host) pr_info("%s: SDHCI controller on %s [%s] using %s\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), - (host->flags & SDHCI_USE_ADMA) ? "ADMA" : + (host->flags & SDHCI_USE_ADMA) ? + (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); sdhci_enable_card_detection(host); @@ -3339,18 +3421,15 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->finish_tasklet); - if (!IS_ERR(mmc->supply.vmmc)) - regulator_disable(mmc->supply.vmmc); - if (!IS_ERR(mmc->supply.vqmmc)) regulator_disable(mmc->supply.vqmmc); - if (host->adma_desc) - dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, - host->adma_desc, host->adma_addr); + if (host->adma_table) + dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, + host->adma_table, host->adma_addr); kfree(host->align_buffer); - host->adma_desc = NULL; + host->adma_table = NULL; host->align_buffer = NULL; } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 31896a779d4e..ddd31cda2370 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -161,7 +161,7 @@ #define SDHCI_CTRL_UHS_SDR50 0x0002 #define SDHCI_CTRL_UHS_SDR104 0x0003 #define SDHCI_CTRL_UHS_DDR50 0x0004 -#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */ +#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */ #define SDHCI_CTRL_VDD_180 0x0008 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_B 0x0000 @@ -204,6 +204,7 @@ #define SDHCI_RETUNING_MODE_SHIFT 14 #define SDHCI_CLOCK_MUL_MASK 0x00FF0000 #define SDHCI_CLOCK_MUL_SHIFT 16 +#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ #define SDHCI_CAPABILITIES_1 0x44 @@ -227,6 +228,7 @@ /* 55-57 reserved */ #define SDHCI_ADMA_ADDRESS 0x58 +#define SDHCI_ADMA_ADDRESS_HI 0x5C /* 60-FB reserved */ @@ -235,6 +237,7 @@ #define SDHCI_PRESET_FOR_SDR50 0x6A #define SDHCI_PRESET_FOR_SDR104 0x6C #define SDHCI_PRESET_FOR_DDR50 0x6E +#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ #define SDHCI_PRESET_DRV_MASK 0xC000 #define SDHCI_PRESET_DRV_SHIFT 14 #define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400 @@ -266,6 +269,46 @@ #define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) #define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) +/* ADMA2 32-bit DMA descriptor size */ +#define SDHCI_ADMA2_32_DESC_SZ 8 + +/* ADMA2 32-bit DMA alignment */ +#define SDHCI_ADMA2_32_ALIGN 4 + +/* ADMA2 32-bit descriptor */ +struct sdhci_adma2_32_desc { + __le16 cmd; + __le16 len; + __le32 addr; +} __packed __aligned(SDHCI_ADMA2_32_ALIGN); + +/* ADMA2 64-bit DMA descriptor size */ +#define SDHCI_ADMA2_64_DESC_SZ 12 + +/* ADMA2 64-bit DMA alignment */ +#define SDHCI_ADMA2_64_ALIGN 8 + +/* + * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte + * aligned. + */ +struct sdhci_adma2_64_desc { + __le16 cmd; + __le16 len; + __le32 addr_lo; + __le32 addr_hi; +} __packed __aligned(4); + +#define ADMA2_TRAN_VALID 0x21 +#define ADMA2_NOP_END_VALID 0x3 +#define ADMA2_END 0x2 + +/* + * Maximum segments assuming a 512KiB maximum requisition size and a minimum + * 4KiB page size. + */ +#define SDHCI_MAX_SEGS 128 + struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS u32 (*read_l)(struct sdhci_host *host, int reg); diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index d1663b3c4143..15cb8b7ffc34 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -21,7 +21,6 @@ #include <linux/err.h> #include <linux/clk.h> -#include <linux/clk-private.h> #include <linux/clk/sunxi.h> #include <linux/gpio.h> diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c new file mode 100644 index 000000000000..4666262edaca --- /dev/null +++ b/drivers/mmc/host/toshsd.c @@ -0,0 +1,717 @@ +/* + * Toshiba PCI Secure Digital Host Controller Interface driver + * + * Copyright (C) 2014 Ondrej Zary + * Copyright (C) 2007 Richard Betts, All Rights Reserved. + * + * Based on asic3_mmc.c, copyright (c) 2005 SDG Systems, LLC and, + * sdhci.c, copyright (C) 2005-2006 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/scatterlist.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/pm.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> + +#include "toshsd.h" + +#define DRIVER_NAME "toshsd" + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA, 0x0805) }, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(pci, pci_ids); + +static void toshsd_init(struct toshsd_host *host) +{ + /* enable clock */ + pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, + SD_PCICFG_CLKSTOP_ENABLE_ALL); + pci_write_config_byte(host->pdev, SD_PCICFG_CARDDETECT, 2); + + /* reset */ + iowrite16(0, host->ioaddr + SD_SOFTWARERESET); /* assert */ + mdelay(2); + iowrite16(1, host->ioaddr + SD_SOFTWARERESET); /* deassert */ + mdelay(2); + + /* Clear card registers */ + iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); + iowrite32(0, host->ioaddr + SD_CARDSTATUS); + iowrite32(0, host->ioaddr + SD_ERRORSTATUS0); + iowrite16(0, host->ioaddr + SD_STOPINTERNAL); + + /* SDIO clock? */ + iowrite16(0x100, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL); + + /* enable LED */ + pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE1, + SD_PCICFG_LED_ENABLE1_START); + pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE2, + SD_PCICFG_LED_ENABLE2_START); + + /* set interrupt masks */ + iowrite32(~(u32)(SD_CARD_RESP_END | SD_CARD_RW_END + | SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0 + | SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE + | SD_BUF_CMD_TIMEOUT), + host->ioaddr + SD_INTMASKCARD); + + iowrite16(0x1000, host->ioaddr + SD_TRANSACTIONCTRL); +} + +/* Set MMC clock / power. + * Note: This controller uses a simple divider scheme therefore it cannot run + * SD/MMC cards at full speed (24/20MHz). HCLK (=33MHz PCI clock?) is too high + * and the next slowest is 16MHz (div=2). + */ +static void __toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct toshsd_host *host = mmc_priv(mmc); + + if (ios->clock) { + u16 clk; + int div = 1; + + while (ios->clock < HCLK / div) + div *= 2; + + clk = div >> 2; + + if (div == 1) { /* disable the divider */ + pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, + SD_PCICFG_CLKMODE_DIV_DISABLE); + clk |= SD_CARDCLK_DIV_DISABLE; + } else + pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, 0); + + clk |= SD_CARDCLK_ENABLE_CLOCK; + iowrite16(clk, host->ioaddr + SD_CARDCLOCKCTRL); + + mdelay(10); + } else + iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, + SD_PCICFG_PWR1_OFF); + mdelay(1); + break; + case MMC_POWER_UP: + break; + case MMC_POWER_ON: + pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, + SD_PCICFG_PWR1_33V); + pci_write_config_byte(host->pdev, SD_PCICFG_POWER2, + SD_PCICFG_PWR2_AUTO); + mdelay(20); + break; + } + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14) + | SD_CARDOPT_C2_MODULE_ABSENT + | SD_CARDOPT_DATA_XFR_WIDTH_1, + host->ioaddr + SD_CARDOPTIONSETUP); + break; + case MMC_BUS_WIDTH_4: + iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14) + | SD_CARDOPT_C2_MODULE_ABSENT + | SD_CARDOPT_DATA_XFR_WIDTH_4, + host->ioaddr + SD_CARDOPTIONSETUP); + break; + } +} + +static void toshsd_set_led(struct toshsd_host *host, unsigned char state) +{ + iowrite16(state, host->ioaddr + SDIO_BASE + SDIO_LEDCTRL); +} + +static void toshsd_finish_request(struct toshsd_host *host) +{ + struct mmc_request *mrq = host->mrq; + + /* Write something to end the command */ + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + + toshsd_set_led(host, 0); + mmc_request_done(host->mmc, mrq); +} + +static irqreturn_t toshsd_thread_irq(int irq, void *dev_id) +{ + struct toshsd_host *host = dev_id; + struct mmc_data *data = host->data; + struct sg_mapping_iter *sg_miter = &host->sg_miter; + unsigned short *buf; + int count; + unsigned long flags; + + if (!data) { + dev_warn(&host->pdev->dev, "Spurious Data IRQ\n"); + if (host->cmd) { + host->cmd->error = -EIO; + toshsd_finish_request(host); + } + return IRQ_NONE; + } + spin_lock_irqsave(&host->lock, flags); + + if (!sg_miter_next(sg_miter)) + return IRQ_HANDLED; + buf = sg_miter->addr; + + /* Ensure we dont read more than one block. The chip will interrupt us + * When the next block is available. + */ + count = sg_miter->length; + if (count > data->blksz) + count = data->blksz; + + dev_dbg(&host->pdev->dev, "count: %08x, flags %08x\n", count, + data->flags); + + /* Transfer the data */ + if (data->flags & MMC_DATA_READ) + ioread32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2); + else + iowrite32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2); + + sg_miter->consumed = count; + sg_miter_stop(sg_miter); + + spin_unlock_irqrestore(&host->lock, flags); + + return IRQ_HANDLED; +} + +static void toshsd_cmd_irq(struct toshsd_host *host) +{ + struct mmc_command *cmd = host->cmd; + u8 *buf; + u16 data; + + if (!host->cmd) { + dev_warn(&host->pdev->dev, "Spurious CMD irq\n"); + return; + } + buf = (u8 *)cmd->resp; + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) { + /* R2 */ + buf[12] = 0xff; + data = ioread16(host->ioaddr + SD_RESPONSE0); + buf[13] = data & 0xff; + buf[14] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE1); + buf[15] = data & 0xff; + buf[8] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE2); + buf[9] = data & 0xff; + buf[10] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE3); + buf[11] = data & 0xff; + buf[4] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE4); + buf[5] = data & 0xff; + buf[6] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE5); + buf[7] = data & 0xff; + buf[0] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE6); + buf[1] = data & 0xff; + buf[2] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE7); + buf[3] = data & 0xff; + } else if (cmd->flags & MMC_RSP_PRESENT) { + /* R1, R1B, R3, R6, R7 */ + data = ioread16(host->ioaddr + SD_RESPONSE0); + buf[0] = data & 0xff; + buf[1] = data >> 8; + data = ioread16(host->ioaddr + SD_RESPONSE1); + buf[2] = data & 0xff; + buf[3] = data >> 8; + } + + dev_dbg(&host->pdev->dev, "Command IRQ complete %d %d %x\n", + cmd->opcode, cmd->error, cmd->flags); + + /* If there is data to handle we will + * finish the request in the mmc_data_end_irq handler.*/ + if (host->data) + return; + + toshsd_finish_request(host); +} + +static void toshsd_data_end_irq(struct toshsd_host *host) +{ + struct mmc_data *data = host->data; + + host->data = NULL; + + if (!data) { + dev_warn(&host->pdev->dev, "Spurious data end IRQ\n"); + return; + } + + if (data->error == 0) + data->bytes_xfered = data->blocks * data->blksz; + else + data->bytes_xfered = 0; + + dev_dbg(&host->pdev->dev, "Completed data request xfr=%d\n", + data->bytes_xfered); + + iowrite16(0, host->ioaddr + SD_STOPINTERNAL); + + toshsd_finish_request(host); +} + +static irqreturn_t toshsd_irq(int irq, void *dev_id) +{ + struct toshsd_host *host = dev_id; + u32 int_reg, int_mask, int_status, detail; + int error = 0, ret = IRQ_HANDLED; + + spin_lock(&host->lock); + int_status = ioread32(host->ioaddr + SD_CARDSTATUS); + int_mask = ioread32(host->ioaddr + SD_INTMASKCARD); + int_reg = int_status & ~int_mask & ~IRQ_DONT_CARE_BITS; + + dev_dbg(&host->pdev->dev, "IRQ status:%x mask:%x\n", + int_status, int_mask); + + /* nothing to do: it's not our IRQ */ + if (!int_reg) { + ret = IRQ_NONE; + goto irq_end; + } + + if (int_reg & SD_BUF_CMD_TIMEOUT) { + error = -ETIMEDOUT; + dev_dbg(&host->pdev->dev, "Timeout\n"); + } else if (int_reg & SD_BUF_CRC_ERR) { + error = -EILSEQ; + dev_err(&host->pdev->dev, "BadCRC\n"); + } else if (int_reg & (SD_BUF_ILLEGAL_ACCESS + | SD_BUF_CMD_INDEX_ERR + | SD_BUF_STOP_BIT_END_ERR + | SD_BUF_OVERFLOW + | SD_BUF_UNDERFLOW + | SD_BUF_DATA_TIMEOUT)) { + dev_err(&host->pdev->dev, "Buffer status error: { %s%s%s%s%s%s}\n", + int_reg & SD_BUF_ILLEGAL_ACCESS ? "ILLEGAL_ACC " : "", + int_reg & SD_BUF_CMD_INDEX_ERR ? "CMD_INDEX " : "", + int_reg & SD_BUF_STOP_BIT_END_ERR ? "STOPBIT_END " : "", + int_reg & SD_BUF_OVERFLOW ? "OVERFLOW " : "", + int_reg & SD_BUF_UNDERFLOW ? "UNDERFLOW " : "", + int_reg & SD_BUF_DATA_TIMEOUT ? "DATA_TIMEOUT " : ""); + + detail = ioread32(host->ioaddr + SD_ERRORSTATUS0); + dev_err(&host->pdev->dev, "detail error status { %s%s%s%s%s%s%s%s%s%s%s%s%s}\n", + detail & SD_ERR0_RESP_CMD_ERR ? "RESP_CMD " : "", + detail & SD_ERR0_RESP_NON_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "", + detail & SD_ERR0_RESP_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "", + detail & SD_ERR0_READ_DATA_END_BIT_ERR ? "READ_DATA_END_BIT " : "", + detail & SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR ? "WRITE_CMD_END_BIT " : "", + detail & SD_ERR0_RESP_NON_CMD12_CRC_ERR ? "RESP_CRC " : "", + detail & SD_ERR0_RESP_CMD12_CRC_ERR ? "RESP_CRC " : "", + detail & SD_ERR0_READ_DATA_CRC_ERR ? "READ_DATA_CRC " : "", + detail & SD_ERR0_WRITE_CMD_CRC_ERR ? "WRITE_CMD_CRC " : "", + detail & SD_ERR1_NO_CMD_RESP ? "NO_CMD_RESP " : "", + detail & SD_ERR1_TIMEOUT_READ_DATA ? "READ_DATA_TIMEOUT " : "", + detail & SD_ERR1_TIMEOUT_CRS_STATUS ? "CRS_STATUS_TIMEOUT " : "", + detail & SD_ERR1_TIMEOUT_CRC_BUSY ? "CRC_BUSY_TIMEOUT " : ""); + error = -EIO; + } + + if (error) { + if (host->cmd) + host->cmd->error = error; + + if (error == -ETIMEDOUT) { + iowrite32(int_status & + ~(SD_BUF_CMD_TIMEOUT | SD_CARD_RESP_END), + host->ioaddr + SD_CARDSTATUS); + } else { + toshsd_init(host); + __toshsd_set_ios(host->mmc, &host->mmc->ios); + goto irq_end; + } + } + + /* Card insert/remove. The mmc controlling code is stateless. */ + if (int_reg & (SD_CARD_CARD_INSERTED_0 | SD_CARD_CARD_REMOVED_0)) { + iowrite32(int_status & + ~(SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0), + host->ioaddr + SD_CARDSTATUS); + + if (int_reg & SD_CARD_CARD_INSERTED_0) + toshsd_init(host); + + mmc_detect_change(host->mmc, 1); + } + + /* Data transfer */ + if (int_reg & (SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE)) { + iowrite32(int_status & + ~(SD_BUF_WRITE_ENABLE | SD_BUF_READ_ENABLE), + host->ioaddr + SD_CARDSTATUS); + + ret = IRQ_WAKE_THREAD; + goto irq_end; + } + + /* Command completion */ + if (int_reg & SD_CARD_RESP_END) { + iowrite32(int_status & ~(SD_CARD_RESP_END), + host->ioaddr + SD_CARDSTATUS); + toshsd_cmd_irq(host); + } + + /* Data transfer completion */ + if (int_reg & SD_CARD_RW_END) { + iowrite32(int_status & ~(SD_CARD_RW_END), + host->ioaddr + SD_CARDSTATUS); + toshsd_data_end_irq(host); + } +irq_end: + spin_unlock(&host->lock); + return ret; +} + +static void toshsd_start_cmd(struct toshsd_host *host, struct mmc_command *cmd) +{ + struct mmc_data *data = host->data; + int c = cmd->opcode; + + dev_dbg(&host->pdev->dev, "Command opcode: %d\n", cmd->opcode); + + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + iowrite16(SD_STOPINT_ISSUE_CMD12, + host->ioaddr + SD_STOPINTERNAL); + + cmd->resp[0] = cmd->opcode; + cmd->resp[1] = 0; + cmd->resp[2] = 0; + cmd->resp[3] = 0; + + toshsd_finish_request(host); + return; + } + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + c |= SD_CMD_RESP_TYPE_NONE; + break; + + case MMC_RSP_R1: + c |= SD_CMD_RESP_TYPE_EXT_R1; + break; + case MMC_RSP_R1B: + c |= SD_CMD_RESP_TYPE_EXT_R1B; + break; + case MMC_RSP_R2: + c |= SD_CMD_RESP_TYPE_EXT_R2; + break; + case MMC_RSP_R3: + c |= SD_CMD_RESP_TYPE_EXT_R3; + break; + + default: + dev_err(&host->pdev->dev, "Unknown response type %d\n", + mmc_resp_type(cmd)); + break; + } + + host->cmd = cmd; + + if (cmd->opcode == MMC_APP_CMD) + c |= SD_CMD_TYPE_ACMD; + + if (cmd->opcode == MMC_GO_IDLE_STATE) + c |= (3 << 8); /* removed from ipaq-asic3.h for some reason */ + + if (data) { + c |= SD_CMD_DATA_PRESENT; + + if (data->blocks > 1) { + iowrite16(SD_STOPINT_AUTO_ISSUE_CMD12, + host->ioaddr + SD_STOPINTERNAL); + c |= SD_CMD_MULTI_BLOCK; + } + + if (data->flags & MMC_DATA_READ) + c |= SD_CMD_TRANSFER_READ; + + /* MMC_DATA_WRITE does not require a bit to be set */ + } + + /* Send the command */ + iowrite32(cmd->arg, host->ioaddr + SD_ARG0); + iowrite16(c, host->ioaddr + SD_CMD); +} + +static void toshsd_start_data(struct toshsd_host *host, struct mmc_data *data) +{ + unsigned int flags = SG_MITER_ATOMIC; + + dev_dbg(&host->pdev->dev, "setup data transfer: blocksize %08x nr_blocks %d, offset: %08x\n", + data->blksz, data->blocks, data->sg->offset); + + host->data = data; + + if (data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); + + /* Set transfer length and blocksize */ + iowrite16(data->blocks, host->ioaddr + SD_BLOCKCOUNT); + iowrite16(data->blksz, host->ioaddr + SD_CARDXFERDATALEN); +} + +/* Process requests from the MMC layer */ +static void toshsd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct toshsd_host *host = mmc_priv(mmc); + unsigned long flags; + + /* abort if card not present */ + if (!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + } + + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); + + host->mrq = mrq; + + if (mrq->data) + toshsd_start_data(host, mrq->data); + + toshsd_set_led(host, 1); + + toshsd_start_cmd(host, mrq->cmd); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static void toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct toshsd_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + __toshsd_set_ios(mmc, ios); + spin_unlock_irqrestore(&host->lock, flags); +} + +static int toshsd_get_ro(struct mmc_host *mmc) +{ + struct toshsd_host *host = mmc_priv(mmc); + + /* active low */ + return !(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_WRITE_PROTECT); +} + +static int toshsd_get_cd(struct mmc_host *mmc) +{ + struct toshsd_host *host = mmc_priv(mmc); + + return !!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0); +} + +static struct mmc_host_ops toshsd_ops = { + .request = toshsd_request, + .set_ios = toshsd_set_ios, + .get_ro = toshsd_get_ro, + .get_cd = toshsd_get_cd, +}; + + +static void toshsd_powerdown(struct toshsd_host *host) +{ + /* mask all interrupts */ + iowrite32(0xffffffff, host->ioaddr + SD_INTMASKCARD); + /* disable card clock */ + iowrite16(0x000, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL); + iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); + /* power down card */ + pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, SD_PCICFG_PWR1_OFF); + /* disable clock */ + pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0); +} + +#ifdef CONFIG_PM_SLEEP +static int toshsd_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct toshsd_host *host = pci_get_drvdata(pdev); + + toshsd_powerdown(host); + + pci_save_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int toshsd_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct toshsd_host *host = pci_get_drvdata(pdev); + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + + toshsd_init(host); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + struct toshsd_host *host; + struct mmc_host *mmc; + resource_size_t base; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto err; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + + host->pdev = pdev; + pci_set_drvdata(pdev, host); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret) + goto free; + + host->ioaddr = pci_iomap(pdev, 0, 0); + if (!host->ioaddr) { + ret = -ENOMEM; + goto release; + } + + /* Set MMC host parameters */ + mmc->ops = &toshsd_ops; + mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->ocr_avail = MMC_VDD_32_33; + + mmc->f_min = HCLK / 512; + mmc->f_max = HCLK; + + spin_lock_init(&host->lock); + + toshsd_init(host); + + ret = request_threaded_irq(pdev->irq, toshsd_irq, toshsd_thread_irq, + IRQF_SHARED, DRIVER_NAME, host); + if (ret) + goto unmap; + + mmc_add_host(mmc); + + base = pci_resource_start(pdev, 0); + dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq); + + pm_suspend_ignore_children(&pdev->dev, 1); + + return 0; + +unmap: + pci_iounmap(pdev, host->ioaddr); +release: + pci_release_regions(pdev); +free: + mmc_free_host(mmc); + pci_set_drvdata(pdev, NULL); +err: + pci_disable_device(pdev); + return ret; +} + +static void toshsd_remove(struct pci_dev *pdev) +{ + struct toshsd_host *host = pci_get_drvdata(pdev); + + mmc_remove_host(host->mmc); + toshsd_powerdown(host); + free_irq(pdev->irq, host); + pci_iounmap(pdev, host->ioaddr); + pci_release_regions(pdev); + mmc_free_host(host->mmc); + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); +} + +static const struct dev_pm_ops toshsd_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume) +}; + +static struct pci_driver toshsd_driver = { + .name = DRIVER_NAME, + .id_table = pci_ids, + .probe = toshsd_probe, + .remove = toshsd_remove, + .driver.pm = &toshsd_pm_ops, +}; + +static int __init toshsd_drv_init(void) +{ + return pci_register_driver(&toshsd_driver); +} + +static void __exit toshsd_drv_exit(void) +{ + pci_unregister_driver(&toshsd_driver); +} + +module_init(toshsd_drv_init); +module_exit(toshsd_drv_exit); + +MODULE_AUTHOR("Ondrej Zary, Richard Betts"); +MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/toshsd.h b/drivers/mmc/host/toshsd.h new file mode 100644 index 000000000000..b6c0d89e53a6 --- /dev/null +++ b/drivers/mmc/host/toshsd.h @@ -0,0 +1,176 @@ +/* + * Toshiba PCI Secure Digital Host Controller Interface driver + * + * Copyright (C) 2014 Ondrej Zary + * Copyright (C) 2007 Richard Betts, All Rights Reserved. + * + * Based on asic3_mmc.c Copyright (c) 2005 SDG Systems, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#define HCLK 33000000 /* 33 MHz (PCI clock) */ + +#define SD_PCICFG_CLKSTOP 0x40 /* 0x1f = clock controller, 0 = stop */ +#define SD_PCICFG_GATEDCLK 0x41 /* Gated clock */ +#define SD_PCICFG_CLKMODE 0x42 /* Control clock of SD controller */ +#define SD_PCICFG_PINSTATUS 0x44 /* R/O: read status of SD pins */ +#define SD_PCICFG_POWER1 0x48 +#define SD_PCICFG_POWER2 0x49 +#define SD_PCICFG_POWER3 0x4a +#define SD_PCICFG_CARDDETECT 0x4c +#define SD_PCICFG_SLOTS 0x50 /* R/O: define support slot number */ +#define SD_PCICFG_EXTGATECLK1 0xf0 /* Could be used for gated clock */ +#define SD_PCICFG_EXTGATECLK2 0xf1 /* Could be used for gated clock */ +#define SD_PCICFG_EXTGATECLK3 0xf9 /* Bit 1: double buffer/single buffer */ +#define SD_PCICFG_SDLED_ENABLE1 0xfa +#define SD_PCICFG_SDLED_ENABLE2 0xfe + +#define SD_PCICFG_CLKMODE_DIV_DISABLE BIT(0) +#define SD_PCICFG_CLKSTOP_ENABLE_ALL 0x1f +#define SD_PCICFG_LED_ENABLE1_START 0x12 +#define SD_PCICFG_LED_ENABLE2_START 0x80 + +#define SD_PCICFG_PWR1_33V 0x08 /* Set for 3.3 volts */ +#define SD_PCICFG_PWR1_OFF 0x00 /* Turn off power */ +#define SD_PCICFG_PWR2_AUTO 0x02 + +#define SD_CMD 0x00 /* also for SDIO */ +#define SD_ARG0 0x04 /* also for SDIO */ +#define SD_ARG1 0x06 /* also for SDIO */ +#define SD_STOPINTERNAL 0x08 +#define SD_BLOCKCOUNT 0x0a /* also for SDIO */ +#define SD_RESPONSE0 0x0c /* also for SDIO */ +#define SD_RESPONSE1 0x0e /* also for SDIO */ +#define SD_RESPONSE2 0x10 /* also for SDIO */ +#define SD_RESPONSE3 0x12 /* also for SDIO */ +#define SD_RESPONSE4 0x14 /* also for SDIO */ +#define SD_RESPONSE5 0x16 /* also for SDIO */ +#define SD_RESPONSE6 0x18 /* also for SDIO */ +#define SD_RESPONSE7 0x1a /* also for SDIO */ +#define SD_CARDSTATUS 0x1c /* also for SDIO */ +#define SD_BUFFERCTRL 0x1e /* also for SDIO */ +#define SD_INTMASKCARD 0x20 /* also for SDIO */ +#define SD_INTMASKBUFFER 0x22 /* also for SDIO */ +#define SD_CARDCLOCKCTRL 0x24 +#define SD_CARDXFERDATALEN 0x26 /* also for SDIO */ +#define SD_CARDOPTIONSETUP 0x28 /* also for SDIO */ +#define SD_ERRORSTATUS0 0x2c /* also for SDIO */ +#define SD_ERRORSTATUS1 0x2e /* also for SDIO */ +#define SD_DATAPORT 0x30 /* also for SDIO */ +#define SD_TRANSACTIONCTRL 0x34 /* also for SDIO */ +#define SD_SOFTWARERESET 0xe0 /* also for SDIO */ + +/* registers above marked "also for SDIO" and all SDIO registers below can be + * accessed at SDIO_BASE + reg address */ +#define SDIO_BASE 0x100 + +#define SDIO_CARDPORTSEL 0x02 +#define SDIO_CARDINTCTRL 0x36 +#define SDIO_CLOCKNWAITCTRL 0x38 +#define SDIO_HOSTINFORMATION 0x3a +#define SDIO_ERRORCTRL 0x3c +#define SDIO_LEDCTRL 0x3e + +#define SD_TRANSCTL_SET BIT(8) + +#define SD_CARDCLK_DIV_DISABLE BIT(15) +#define SD_CARDCLK_ENABLE_CLOCK BIT(8) +#define SD_CARDCLK_CLK_DIV_512 BIT(7) +#define SD_CARDCLK_CLK_DIV_256 BIT(6) +#define SD_CARDCLK_CLK_DIV_128 BIT(5) +#define SD_CARDCLK_CLK_DIV_64 BIT(4) +#define SD_CARDCLK_CLK_DIV_32 BIT(3) +#define SD_CARDCLK_CLK_DIV_16 BIT(2) +#define SD_CARDCLK_CLK_DIV_8 BIT(1) +#define SD_CARDCLK_CLK_DIV_4 BIT(0) +#define SD_CARDCLK_CLK_DIV_2 0 + +#define SD_CARDOPT_REQUIRED 0x000e +#define SD_CARDOPT_DATA_RESP_TIMEOUT(x) (((x) & 0x0f) << 4) /* 4 bits */ +#define SD_CARDOPT_C2_MODULE_ABSENT BIT(14) +#define SD_CARDOPT_DATA_XFR_WIDTH_1 (1 << 15) +#define SD_CARDOPT_DATA_XFR_WIDTH_4 (0 << 15) + +#define SD_CMD_TYPE_CMD (0 << 6) +#define SD_CMD_TYPE_ACMD (1 << 6) +#define SD_CMD_TYPE_AUTHEN (2 << 6) +#define SD_CMD_RESP_TYPE_NONE (3 << 8) +#define SD_CMD_RESP_TYPE_EXT_R1 (4 << 8) +#define SD_CMD_RESP_TYPE_EXT_R1B (5 << 8) +#define SD_CMD_RESP_TYPE_EXT_R2 (6 << 8) +#define SD_CMD_RESP_TYPE_EXT_R3 (7 << 8) +#define SD_CMD_RESP_TYPE_EXT_R6 (4 << 8) +#define SD_CMD_RESP_TYPE_EXT_R7 (4 << 8) +#define SD_CMD_DATA_PRESENT BIT(11) +#define SD_CMD_TRANSFER_READ BIT(12) +#define SD_CMD_MULTI_BLOCK BIT(13) +#define SD_CMD_SECURITY_CMD BIT(14) + +#define SD_STOPINT_ISSUE_CMD12 BIT(0) +#define SD_STOPINT_AUTO_ISSUE_CMD12 BIT(8) + +#define SD_CARD_RESP_END BIT(0) +#define SD_CARD_RW_END BIT(2) +#define SD_CARD_CARD_REMOVED_0 BIT(3) +#define SD_CARD_CARD_INSERTED_0 BIT(4) +#define SD_CARD_PRESENT_0 BIT(5) +#define SD_CARD_UNK6 BIT(6) +#define SD_CARD_WRITE_PROTECT BIT(7) +#define SD_CARD_CARD_REMOVED_3 BIT(8) +#define SD_CARD_CARD_INSERTED_3 BIT(9) +#define SD_CARD_PRESENT_3 BIT(10) + +#define SD_BUF_CMD_INDEX_ERR BIT(16) +#define SD_BUF_CRC_ERR BIT(17) +#define SD_BUF_STOP_BIT_END_ERR BIT(18) +#define SD_BUF_DATA_TIMEOUT BIT(19) +#define SD_BUF_OVERFLOW BIT(20) +#define SD_BUF_UNDERFLOW BIT(21) +#define SD_BUF_CMD_TIMEOUT BIT(22) +#define SD_BUF_UNK7 BIT(23) +#define SD_BUF_READ_ENABLE BIT(24) +#define SD_BUF_WRITE_ENABLE BIT(25) +#define SD_BUF_ILLEGAL_FUNCTION BIT(29) +#define SD_BUF_CMD_BUSY BIT(30) +#define SD_BUF_ILLEGAL_ACCESS BIT(31) + +#define SD_ERR0_RESP_CMD_ERR BIT(0) +#define SD_ERR0_RESP_NON_CMD12_END_BIT_ERR BIT(2) +#define SD_ERR0_RESP_CMD12_END_BIT_ERR BIT(3) +#define SD_ERR0_READ_DATA_END_BIT_ERR BIT(4) +#define SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR BIT(5) +#define SD_ERR0_RESP_NON_CMD12_CRC_ERR BIT(8) +#define SD_ERR0_RESP_CMD12_CRC_ERR BIT(9) +#define SD_ERR0_READ_DATA_CRC_ERR BIT(10) +#define SD_ERR0_WRITE_CMD_CRC_ERR BIT(11) + +#define SD_ERR1_NO_CMD_RESP BIT(16) +#define SD_ERR1_TIMEOUT_READ_DATA BIT(20) +#define SD_ERR1_TIMEOUT_CRS_STATUS BIT(21) +#define SD_ERR1_TIMEOUT_CRC_BUSY BIT(22) + +#define IRQ_DONT_CARE_BITS (SD_CARD_PRESENT_3 \ + | SD_CARD_WRITE_PROTECT \ + | SD_CARD_UNK6 \ + | SD_CARD_PRESENT_0 \ + | SD_BUF_UNK7 \ + | SD_BUF_CMD_BUSY) + +struct toshsd_host { + struct pci_dev *pdev; + struct mmc_host *mmc; + + spinlock_t lock; + + struct mmc_request *mrq;/* Current request */ + struct mmc_command *cmd;/* Current command */ + struct mmc_data *data; /* Current data request */ + + struct sg_mapping_iter sg_miter; /* for PIO */ + + void __iomem *ioaddr; /* mapped address */ +}; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c13d83e15ace..45f09a66e6c9 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -225,7 +225,12 @@ static int bond_changelink(struct net_device *bond_dev, bond_option_arp_ip_targets_clear(bond); nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) { - __be32 target = nla_get_be32(attr); + __be32 target; + + if (nla_len(attr) < sizeof(target)) + return -EINVAL; + + target = nla_get_be32(attr); bond_opt_initval(&newval, (__force u64)target); err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index b9625968daac..4f4c2a7888e5 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -377,6 +377,29 @@ static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv) +{ + unsigned int timeout = 1000; + u32 reg; + + reg = core_readl(priv, CORE_WATCHDOG_CTRL); + reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET; + core_writel(priv, reg, CORE_WATCHDOG_CTRL); + + do { + reg = core_readl(priv, CORE_WATCHDOG_CTRL); + if (!(reg & SOFTWARE_RESET)) + break; + + usleep_range(1000, 2000); + } while (timeout-- > 0); + + if (timeout == 0) + return -ETIMEDOUT; + + return 0; +} + static int bcm_sf2_sw_setup(struct dsa_switch *ds) { const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; @@ -404,11 +427,18 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) *base = of_iomap(dn, i); if (*base == NULL) { pr_err("unable to find register: %s\n", reg_names[i]); - return -ENODEV; + ret = -ENOMEM; + goto out_unmap; } base++; } + ret = bcm_sf2_sw_rst(priv); + if (ret) { + pr_err("unable to software reset switch: %d\n", ret); + goto out_unmap; + } + /* Disable all interrupts and request them */ intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); @@ -484,7 +514,8 @@ out_free_irq0: out_unmap: base = &priv->core; for (i = 0; i < BCM_SF2_REGS_NUM; i++) { - iounmap(*base); + if (*base) + iounmap(*base); base++; } return ret; @@ -733,29 +764,6 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) return 0; } -static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv) -{ - unsigned int timeout = 1000; - u32 reg; - - reg = core_readl(priv, CORE_WATCHDOG_CTRL); - reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET; - core_writel(priv, reg, CORE_WATCHDOG_CTRL); - - do { - reg = core_readl(priv, CORE_WATCHDOG_CTRL); - if (!(reg & SOFTWARE_RESET)) - break; - - usleep_range(1000, 2000); - } while (timeout-- > 0); - - if (timeout == 0) - return -ETIMEDOUT; - - return 0; -} - static int bcm_sf2_sw_resume(struct dsa_switch *ds) { struct bcm_sf2_priv *priv = ds_to_priv(ds); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index dbb41c1923e6..77f8f836cbbe 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -8563,7 +8563,8 @@ static int tg3_init_rings(struct tg3 *tp) if (tnapi->rx_rcb) memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); - if (tg3_rx_prodring_alloc(tp, &tnapi->prodring)) { + if (tnapi->prodring.rx_std && + tg3_rx_prodring_alloc(tp, &tnapi->prodring)) { tg3_free_rings(tp); return -ENOMEM; } diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index 9e089d24466e..3564fe9d3f69 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -22,7 +22,7 @@ if NET_CADENCE config ARM_AT91_ETHER tristate "AT91RM9200 Ethernet support" - depends on HAS_DMA && (ARCH_AT91RM9200 || COMPILE_TEST) + depends on HAS_DMA && (ARCH_AT91 || COMPILE_TEST) select MACB ---help--- If you wish to compile a kernel for the AT91RM9200 and enable diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 8520d5529df8..279873cb6e3a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2442,9 +2442,13 @@ static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps) SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full | SUPPORTED_10000baseKX4_Full; else if (type == FW_PORT_TYPE_FIBER_XFI || - type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) + type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) { v |= SUPPORTED_FIBRE; - else if (type == FW_PORT_TYPE_BP40_BA) + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_BP40_BA) v |= SUPPORTED_40000baseSR4_Full; if (caps & FW_PORT_CAP_ANEG) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 3e8475cae4f9..597c463e384d 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4309,11 +4309,16 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh) return -EOPNOTSUPP; br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (!br_spec) + return -EINVAL; nla_for_each_nested(attr, br_spec, rem) { if (nla_type(attr) != IFLA_BRIDGE_MODE) continue; + if (nla_len(attr) < sizeof(mode)) + return -EINVAL; + mode = nla_get_u16(attr); if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB) return -EINVAL; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index a2d72a87cbde..487cd9c4ac0d 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1012,7 +1012,8 @@ static void igb_free_q_vector(struct igb_adapter *adapter, int v_idx) /* igb_get_stats64() might access the rings on this vector, * we must wait a grace period before freeing it. */ - kfree_rcu(q_vector, rcu); + if (q_vector) + kfree_rcu(q_vector, rcu); } /** @@ -1792,8 +1793,10 @@ void igb_down(struct igb_adapter *adapter) adapter->flags &= ~IGB_FLAG_NEED_LINK_UPDATE; for (i = 0; i < adapter->num_q_vectors; i++) { - napi_synchronize(&(adapter->q_vector[i]->napi)); - napi_disable(&(adapter->q_vector[i]->napi)); + if (adapter->q_vector[i]) { + napi_synchronize(&adapter->q_vector[i]->napi); + napi_disable(&adapter->q_vector[i]->napi); + } } @@ -3717,7 +3720,8 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_tx_queues; i++) - igb_free_tx_resources(adapter->tx_ring[i]); + if (adapter->tx_ring[i]) + igb_free_tx_resources(adapter->tx_ring[i]); } void igb_unmap_and_free_tx_resource(struct igb_ring *ring, @@ -3782,7 +3786,8 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_tx_queues; i++) - igb_clean_tx_ring(adapter->tx_ring[i]); + if (adapter->tx_ring[i]) + igb_clean_tx_ring(adapter->tx_ring[i]); } /** @@ -3819,7 +3824,8 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_rx_queues; i++) - igb_free_rx_resources(adapter->rx_ring[i]); + if (adapter->rx_ring[i]) + igb_free_rx_resources(adapter->rx_ring[i]); } /** @@ -3874,7 +3880,8 @@ static void igb_clean_all_rx_rings(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_rx_queues; i++) - igb_clean_rx_ring(adapter->rx_ring[i]); + if (adapter->rx_ring[i]) + igb_clean_rx_ring(adapter->rx_ring[i]); } /** @@ -7404,6 +7411,8 @@ static int igb_resume(struct device *dev) pci_restore_state(pdev); pci_save_state(pdev); + if (!pci_device_is_present(pdev)) + return -ENODEV; err = pci_enable_device_mem(pdev); if (err) { dev_err(&pdev->dev, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d2df4e3d1032..cc51554c9e99 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3936,8 +3936,8 @@ void ixgbe_set_rx_mode(struct net_device *netdev) * if SR-IOV and VMDQ are disabled - otherwise ensure * that hardware VLAN filters remain enabled. */ - if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED | - IXGBE_FLAG_SRIOV_ENABLED))) + if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED | + IXGBE_FLAG_SRIOV_ENABLED)) vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN); } else { if (netdev->flags & IFF_ALLMULTI) { @@ -7669,6 +7669,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, return -EOPNOTSUPP; br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (!br_spec) + return -EINVAL; nla_for_each_nested(attr, br_spec, rem) { __u16 mode; @@ -7677,6 +7679,9 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, if (nla_type(attr) != IFLA_BRIDGE_MODE) continue; + if (nla_len(attr) < sizeof(mode)) + return -EINVAL; + mode = nla_get_u16(attr); if (mode == BRIDGE_MODE_VEPA) { reg = 0; @@ -7979,6 +7984,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int i, err, pci_using_dac, expected_gts; unsigned int indices = MAX_TX_QUEUES; u8 part_str[IXGBE_PBANUM_LENGTH]; + bool disable_dev = false; #ifdef IXGBE_FCOE u16 device_caps; #endif @@ -8369,13 +8375,14 @@ err_sw_init: iounmap(adapter->io_addr); kfree(adapter->mac_table); err_ioremap: + disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); err_alloc_etherdev: pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); err_pci_reg: err_dma: - if (!adapter || !test_and_set_bit(__IXGBE_DISABLED, &adapter->state)) + if (!adapter || disable_dev) pci_disable_device(pdev); return err; } @@ -8393,6 +8400,7 @@ static void ixgbe_remove(struct pci_dev *pdev) { struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; + bool disable_dev; ixgbe_dbg_adapter_exit(adapter); @@ -8442,11 +8450,12 @@ static void ixgbe_remove(struct pci_dev *pdev) e_dev_info("complete\n"); kfree(adapter->mac_table); + disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); pci_disable_pcie_error_reporting(pdev); - if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state)) + if (disable_dev) pci_disable_device(pdev); } diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 5d2498dcf536..cd5cf6d957c7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1546,7 +1546,7 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, switch (op) { case RES_OP_RESERVE: - count = get_param_l(&in_param); + count = get_param_l(&in_param) & 0xffffff; align = get_param_h(&in_param); err = mlx4_grant_resource(dev, slave, RES_QP, count, 0); if (err) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 60e9c2cd051e..b5db6b3f939f 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -917,21 +917,13 @@ static int sh_eth_reset(struct net_device *ndev) return ret; } -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static void sh_eth_set_receive_align(struct sk_buff *skb) { - int reserve; + uintptr_t reserve = (uintptr_t)skb->data & (SH_ETH_RX_ALIGN - 1); - reserve = SH4_SKB_RX_ALIGN - ((u32)skb->data & (SH4_SKB_RX_ALIGN - 1)); if (reserve) - skb_reserve(skb, reserve); + skb_reserve(skb, SH_ETH_RX_ALIGN - reserve); } -#else -static void sh_eth_set_receive_align(struct sk_buff *skb) -{ - skb_reserve(skb, SH2_SH3_SKB_RX_ALIGN); -} -#endif /* CPU <-> EDMAC endian convert */ @@ -1119,6 +1111,7 @@ static void sh_eth_ring_format(struct net_device *ndev) struct sh_eth_txdesc *txdesc = NULL; int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring; int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring; + int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; mdp->cur_rx = 0; mdp->cur_tx = 0; @@ -1131,21 +1124,21 @@ static void sh_eth_ring_format(struct net_device *ndev) for (i = 0; i < mdp->num_rx_ring; i++) { /* skb */ mdp->rx_skbuff[i] = NULL; - skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); + skb = netdev_alloc_skb(ndev, skbuff_size); mdp->rx_skbuff[i] = skb; if (skb == NULL) break; - dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz, - DMA_FROM_DEVICE); sh_eth_set_receive_align(skb); /* RX descriptor */ rxdesc = &mdp->rx_ring[i]; + /* The size of the buffer is a multiple of 16 bytes. */ + rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); + dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length, + DMA_FROM_DEVICE); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP); - /* The size of the buffer is 16 byte boundary. */ - rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); /* Rx descriptor address set */ if (i == 0) { sh_eth_write(ndev, mdp->rx_desc_dma, RDLAR); @@ -1397,6 +1390,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) struct sk_buff *skb; u16 pkt_len = 0; u32 desc_status; + int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; rxdesc = &mdp->rx_ring[entry]; while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) { @@ -1448,7 +1442,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) if (mdp->cd->rpadir) skb_reserve(skb, NET_IP_ALIGN); dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr, - mdp->rx_buf_sz, + ALIGN(mdp->rx_buf_sz, 16), DMA_FROM_DEVICE); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); @@ -1468,13 +1462,13 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); if (mdp->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); + skb = netdev_alloc_skb(ndev, skbuff_size); mdp->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ - dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz, - DMA_FROM_DEVICE); sh_eth_set_receive_align(skb); + dma_map_single(&ndev->dev, skb->data, + rxdesc->buffer_length, DMA_FROM_DEVICE); skb_checksum_none_assert(skb); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); @@ -2042,6 +2036,8 @@ static int sh_eth_open(struct net_device *ndev) if (ret) goto out_free_irq; + mdp->is_opened = 1; + return ret; out_free_irq: @@ -2131,6 +2127,36 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_OK; } +static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + if (sh_eth_is_rz_fast_ether(mdp)) + return &ndev->stats; + + if (!mdp->is_opened) + return &ndev->stats; + + ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR); + sh_eth_write(ndev, 0, TROCR); /* (write clear) */ + ndev->stats.collisions += sh_eth_read(ndev, CDCR); + sh_eth_write(ndev, 0, CDCR); /* (write clear) */ + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR); + sh_eth_write(ndev, 0, LCCR); /* (write clear) */ + + if (sh_eth_is_gether(mdp)) { + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR); + sh_eth_write(ndev, 0, CERCR); /* (write clear) */ + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR); + sh_eth_write(ndev, 0, CEECR); /* (write clear) */ + } else { + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR); + sh_eth_write(ndev, 0, CNDCR); /* (write clear) */ + } + + return &ndev->stats; +} + /* device close function */ static int sh_eth_close(struct net_device *ndev) { @@ -2145,6 +2171,7 @@ static int sh_eth_close(struct net_device *ndev) sh_eth_write(ndev, 0, EDTRR); sh_eth_write(ndev, 0, EDRRR); + sh_eth_get_stats(ndev); /* PHY Disconnect */ if (mdp->phydev) { phy_stop(mdp->phydev); @@ -2163,36 +2190,9 @@ static int sh_eth_close(struct net_device *ndev) pm_runtime_put_sync(&mdp->pdev->dev); - return 0; -} - -static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (sh_eth_is_rz_fast_ether(mdp)) - return &ndev->stats; + mdp->is_opened = 0; - pm_runtime_get_sync(&mdp->pdev->dev); - - ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR); - sh_eth_write(ndev, 0, TROCR); /* (write clear) */ - ndev->stats.collisions += sh_eth_read(ndev, CDCR); - sh_eth_write(ndev, 0, CDCR); /* (write clear) */ - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR); - sh_eth_write(ndev, 0, LCCR); /* (write clear) */ - if (sh_eth_is_gether(mdp)) { - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR); - sh_eth_write(ndev, 0, CERCR); /* (write clear) */ - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR); - sh_eth_write(ndev, 0, CEECR); /* (write clear) */ - } else { - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR); - sh_eth_write(ndev, 0, CNDCR); /* (write clear) */ - } - pm_runtime_put_sync(&mdp->pdev->dev); - - return &ndev->stats; + return 0; } /* ioctl to device function */ diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index b37c427144ee..22301bf9c21d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -162,9 +162,9 @@ enum { /* Driver's parameters */ #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) -#define SH4_SKB_RX_ALIGN 32 +#define SH_ETH_RX_ALIGN 32 #else -#define SH2_SH3_SKB_RX_ALIGN 2 +#define SH_ETH_RX_ALIGN 2 #endif /* Register's bits @@ -522,6 +522,7 @@ struct sh_eth_private { unsigned no_ether_link:1; unsigned ether_link_active_low:1; + unsigned is_opened:1; }; static inline void sh_eth_soft_swap(char *src, int len) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index db56fa7ce8f9..58a1a0a423d4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -177,12 +177,6 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, */ plat->maxmtu = JUMBO_LEN; - /* Set default value for multicast hash bins */ - plat->multicast_filter_bins = HASH_TABLE_SIZE; - - /* Set default value for unicast filter entries */ - plat->unicast_filter_entries = 1; - /* * Currently only the properties needed on SPEAr600 * are provided. All other properties should be added @@ -270,16 +264,23 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) return PTR_ERR(addr); plat_dat = dev_get_platdata(&pdev->dev); - if (pdev->dev.of_node) { - if (!plat_dat) - plat_dat = devm_kzalloc(&pdev->dev, + + if (!plat_dat) + plat_dat = devm_kzalloc(&pdev->dev, sizeof(struct plat_stmmacenet_data), GFP_KERNEL); - if (!plat_dat) { - pr_err("%s: ERROR: no memory", __func__); - return -ENOMEM; - } + if (!plat_dat) { + pr_err("%s: ERROR: no memory", __func__); + return -ENOMEM; + } + + /* Set default value for multicast hash bins */ + plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; + /* Set default value for unicast filter entries */ + plat_dat->unicast_filter_entries = 1; + + if (pdev->dev.of_node) { ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); if (ret) { pr_err("%s: main dt probe failed", __func__); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e1e335c339e3..be4649a49c5e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2306,9 +2306,9 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6, if (ipv6) { udp_conf.family = AF_INET6; udp_conf.use_udp6_tx_checksums = - !!(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); + !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); udp_conf.use_udp6_rx_checksums = - !!(flags & VXLAN_F_UDP_ZERO_CSUM6_RX); + !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX); } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = INADDR_ANY; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 4f6e66892acc..b894a84e8393 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -155,6 +155,7 @@ enum iwl_ucode_tlv_api { * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), * which also implies support for the scheduler configuration command + * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command */ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), @@ -163,6 +164,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = BIT(10), IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = BIT(11), IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12), + IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18), }; /* The default calibrate table size if not specified by firmware file */ diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index b62405865b25..b6d2683da3a9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2448,9 +2448,15 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_STATION: - /* Use aux roc framework (HS20) */ - ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, - vif, duration); + if (mvm->fw->ucode_capa.capa[0] & + IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) { + /* Use aux roc framework (HS20) */ + ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, + vif, duration); + goto out_unlock; + } + IWL_ERR(mvm, "hotspot not supported\n"); + ret = -EINVAL; goto out_unlock; case NL80211_IFTYPE_P2P_DEVICE: /* handle below */ diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 61f5d36eca6a..846a2e6e34d8 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -2249,6 +2249,16 @@ int rtl_pci_probe(struct pci_dev *pdev, /*like read eeprom and so on */ rtlpriv->cfg->ops->read_eeprom_info(hw); + if (rtlpriv->cfg->ops->init_sw_vars(hw)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n"); + err = -ENODEV; + goto fail3; + } + rtlpriv->cfg->ops->init_sw_leds(hw); + + /*aspm */ + rtl_pci_init_aspm(hw); + /* Init mac80211 sw */ err = rtl_init_core(hw); if (err) { @@ -2264,16 +2274,6 @@ int rtl_pci_probe(struct pci_dev *pdev, goto fail3; } - if (rtlpriv->cfg->ops->init_sw_vars(hw)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n"); - err = -ENODEV; - goto fail3; - } - rtlpriv->cfg->ops->init_sw_leds(hw); - - /*aspm */ - rtl_pci_init_aspm(hw); - err = ieee80211_register_hw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c index 310d3163dc5b..8ec8200002c7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c @@ -3672,8 +3672,9 @@ static void rtl8821ae_update_hal_rate_mask(struct ieee80211_hw *hw, mac->opmode == NL80211_IFTYPE_ADHOC) macid = sta->aid + 1; if (wirelessmode == WIRELESS_MODE_N_5G || - wirelessmode == WIRELESS_MODE_AC_5G) - ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ]; + wirelessmode == WIRELESS_MODE_AC_5G || + wirelessmode == WIRELESS_MODE_A) + ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ] << 4; else ratr_bitmap = sta->supp_rates[NL80211_BAND_2GHZ]; diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 4e56a27f9689..fab0d4b42f58 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -39,7 +39,7 @@ struct backend_info { static int connect_rings(struct backend_info *be, struct xenvif_queue *queue); static void connect(struct backend_info *be); static int read_xenbus_vif_flags(struct backend_info *be); -static void backend_create_xenvif(struct backend_info *be); +static int backend_create_xenvif(struct backend_info *be); static void unregister_hotplug_status_watch(struct backend_info *be); static void set_backend_state(struct backend_info *be, enum xenbus_state state); @@ -352,7 +352,9 @@ static int netback_probe(struct xenbus_device *dev, be->state = XenbusStateInitWait; /* This kicks hotplug scripts, so do it immediately. */ - backend_create_xenvif(be); + err = backend_create_xenvif(be); + if (err) + goto fail; return 0; @@ -397,19 +399,19 @@ static int netback_uevent(struct xenbus_device *xdev, } -static void backend_create_xenvif(struct backend_info *be) +static int backend_create_xenvif(struct backend_info *be) { int err; long handle; struct xenbus_device *dev = be->dev; if (be->vif != NULL) - return; + return 0; err = xenbus_scanf(XBT_NIL, dev->nodename, "handle", "%li", &handle); if (err != 1) { xenbus_dev_fatal(dev, err, "reading handle"); - return; + return (err < 0) ? err : -EINVAL; } be->vif = xenvif_alloc(&dev->dev, dev->otherend_id, handle); @@ -417,10 +419,11 @@ static void backend_create_xenvif(struct backend_info *be) err = PTR_ERR(be->vif); be->vif = NULL; xenbus_dev_fatal(dev, err, "creating interface"); - return; + return err; } kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE); + return 0; } static void backend_disconnect(struct backend_info *be) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index cca871346a0f..ece8d1804d13 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -496,9 +496,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, len = skb_frag_size(frag); offset = frag->page_offset; - /* Data must not cross a page boundary. */ - BUG_ON(len + offset > PAGE_SIZE<<compound_order(page)); - /* Skip unused frames from start of page */ page += offset >> PAGE_SHIFT; offset &= ~PAGE_MASK; @@ -506,8 +503,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, while (len > 0) { unsigned long bytes; - BUG_ON(offset >= PAGE_SIZE); - bytes = PAGE_SIZE - offset; if (bytes > len) bytes = len; diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c index 5066a7ef7b6c..3319cf19deeb 100644 --- a/drivers/nubus/nubus.c +++ b/drivers/nubus/nubus.c @@ -920,14 +920,10 @@ void __init nubus_probe_slot(int slot) rp = nubus_rom_addr(slot); for(i = 4; i; i--) { - unsigned long flags; int card_present; rp--; - local_irq_save(flags); card_present = hwreg_present(rp); - local_irq_restore(flags); - if (!card_present) continue; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 30e97bcc4f88..d134710de96d 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -964,8 +964,6 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, bool nomap) { - if (memblock_is_region_reserved(base, size)) - return -EBUSY; if (nomap) return memblock_remove(base, size); return memblock_reserve(base, size); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 3d43874319be..19bb19c7db4a 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -276,6 +276,7 @@ struct tegra_pcie { struct resource all; struct resource io; + struct resource pio; struct resource mem; struct resource prefetch; struct resource busn; @@ -658,7 +659,6 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { struct tegra_pcie *pcie = sys_to_pcie(sys); int err; - phys_addr_t io_start; err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem); if (err < 0) @@ -668,14 +668,12 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) if (err) return err; - io_start = pci_pio_to_address(pcie->io.start); - pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource_offset(&sys->resources, &pcie->prefetch, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); - pci_ioremap_io(nr * SZ_64K, io_start); + pci_ioremap_io(pcie->pio.start, pcie->io.start); return 1; } @@ -786,7 +784,6 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) { u32 fpci_bar, size, axi_address; - phys_addr_t io_start = pci_pio_to_address(pcie->io.start); /* Bar 0: type 1 extended configuration space */ fpci_bar = 0xfe100000; @@ -799,7 +796,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) /* Bar 1: downstream IO bar */ fpci_bar = 0xfdfc0000; size = resource_size(&pcie->io); - axi_address = io_start; + axi_address = pcie->io.start; afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); @@ -1690,8 +1687,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) switch (res.flags & IORESOURCE_TYPE_BITS) { case IORESOURCE_IO: - memcpy(&pcie->io, &res, sizeof(res)); - pcie->io.name = np->full_name; + memcpy(&pcie->pio, &res, sizeof(res)); + pcie->pio.name = np->full_name; + + /* + * The Tegra PCIe host bridge uses this to program the + * mapping of the I/O space to the physical address, + * so we override the .start and .end fields here that + * of_pci_range_to_resource() converted to I/O space. + * We also set the IORESOURCE_MEM type to clarify that + * the resource is in the physical memory space. + */ + pcie->io.start = range.cpu_addr; + pcie->io.end = range.cpu_addr + range.size - 1; + pcie->io.flags = IORESOURCE_MEM; + pcie->io.name = "I/O"; + + memcpy(&res, &pcie->io, sizeof(res)); break; case IORESOURCE_MEM: diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 9fab30af0e75..084587d7cd13 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -590,6 +590,20 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev) return entry; } +static int msi_verify_entries(struct pci_dev *dev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &dev->msi_list, list) { + if (!dev->no_64bit_msi || !entry->msg.address_hi) + continue; + dev_err(&dev->dev, "Device has broken 64-bit MSI but arch" + " tried to assign one above 4G\n"); + return -EIO; + } + return 0; +} + /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function @@ -627,6 +641,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) return ret; } + ret = msi_verify_entries(dev); + if (ret) { + msi_mask_irq(entry, mask, ~mask); + free_msi_irqs(dev); + return ret; + } + ret = populate_msi_sysfs(dev); if (ret) { msi_mask_irq(entry, mask, ~mask); @@ -739,6 +760,11 @@ static int msix_capability_init(struct pci_dev *dev, if (ret) goto out_avail; + /* Check if all MSI entries honor device restrictions */ + ret = msi_verify_entries(dev); + if (ret) + goto out_free; + /* * Some devices require MSI-X to be enabled before we can touch the * MSI-X registers. We need to mask all the vectors to prevent diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f65ff49bb275..028e76504519 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -71,6 +71,15 @@ config POWER_RESET_HISI help Reboot support for Hisilicon boards. +config POWER_RESET_IMX + bool "IMX6 power-off driver" + depends on POWER_RESET && SOC_IMX6 + help + This driver support power off external PMIC by PMIC_ON_REQ on i.mx6 + boards.If you want to use other pin to control external power,please + say N here or disable in dts to make sure pm_power_off never be + overwrote wrongly by this driver. + config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 76ce1c59469b..1d4804d6b323 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o +obj-$(CONFIG_POWER_RESET_IMX) += imx-snvs-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 3cb36693343a..69a75d99ae92 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -19,8 +19,8 @@ #include <asm/system_misc.h> -#include <mach/at91sam9_ddrsdr.h> -#include <mach/at91sam9_sdramc.h> +#include <soc/at91/at91sam9_ddrsdr.h> +#include <soc/at91/at91sam9_sdramc.h> #define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ #define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */ diff --git a/drivers/power/reset/imx-snvs-poweroff.c b/drivers/power/reset/imx-snvs-poweroff.c new file mode 100644 index 000000000000..ad6ce5020ea7 --- /dev/null +++ b/drivers/power/reset/imx-snvs-poweroff.c @@ -0,0 +1,66 @@ +/* Power off driver for i.mx6 + * Copyright (c) 2014, FREESCALE CORPORATION. All rights reserved. + * + * based on msm-poweroff.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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. + * + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +static void __iomem *snvs_base; + +static void do_imx_poweroff(void) +{ + u32 value = readl(snvs_base); + + /* set TOP and DP_EN bit */ + writel(value | 0x60, snvs_base); +} + +static int imx_poweroff_probe(struct platform_device *pdev) +{ + snvs_base = of_iomap(pdev->dev.of_node, 0); + if (!snvs_base) { + dev_err(&pdev->dev, "failed to get memory\n"); + return -ENODEV; + } + + pm_power_off = do_imx_poweroff; + return 0; +} + +static const struct of_device_id of_imx_poweroff_match[] = { + { .compatible = "fsl,sec-v4.0-poweroff", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_imx_poweroff_match); + +static struct platform_driver imx_poweroff_driver = { + .probe = imx_poweroff_probe, + .driver = { + .name = "imx-snvs-poweroff", + .of_match_table = of_match_ptr(of_imx_poweroff_match), + }, +}; + +static int __init imx_poweroff_init(void) +{ + return platform_driver_register(&imx_poweroff_driver); +} +device_initcall(imx_poweroff_init); diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 6d77dcd7dcf6..3fe47bd66153 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -330,7 +330,8 @@ static int pm8607_regulator_dt_init(struct platform_device *pdev, for_each_child_of_node(nproot, np) { if (!of_node_cmp(np->name, info->desc.name)) { config->init_data = - of_get_regulator_init_data(&pdev->dev, np); + of_get_regulator_init_data(&pdev->dev, np, + &info->desc); config->of_node = np; break; } diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 55d7b7b0f2e0..c3a60b57a865 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -521,6 +521,14 @@ config REGULATOR_RN5T618 help Say y here to support the regulators found on Ricoh RN5T618 PMIC. +config REGULATOR_RT5033 + tristate "Richtek RT5033 Regulators" + depends on MFD_RT5033 + help + This adds support for voltage and current regulators in Richtek + RT5033 PMIC. The device supports multiple regulators like + current source, LDO and Buck. + config REGULATOR_S2MPA01 tristate "Samsung S2MPA01 voltage regulator" depends on MFD_SEC_CORE @@ -529,13 +537,13 @@ config REGULATOR_S2MPA01 via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. config REGULATOR_S2MPS11 - tristate "Samsung S2MPS11/S2MPS14/S2MPU02 voltage regulator" + tristate "Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage regulator" depends on MFD_SEC_CORE help - This driver supports a Samsung S2MPS11/S2MPS14/S2MPU02 voltage output - regulator via I2C bus. The chip is comprised of high efficient Buck - converters including Dual-Phase Buck converter, Buck-Boost converter, - various LDOs. + This driver supports a Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage + output regulator via I2C bus. The chip is comprised of high efficient + Buck converters including Dual-Phase Buck converter, Buck-Boost + converter, various LDOs. config REGULATOR_S5M8767 tristate "Samsung S5M8767A voltage regulator" @@ -547,7 +555,7 @@ config REGULATOR_S5M8767 config REGULATOR_SKY81452 tristate "Skyworks Solutions SKY81452 voltage regulator" - depends on SKY81452 + depends on MFD_SKY81452 help This driver supports Skyworks SKY81452 voltage output regulator via I2C bus. SKY81452 has one voltage linear regulator can be diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 1029ed39c512..1f28ebfc6f3a 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o +obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index afd06f92dfdf..9eec453b745d 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -61,6 +61,8 @@ #define ACT8846_REG12_VSET 0xa0 #define ACT8846_REG12_CTRL 0xa1 #define ACT8846_REG13_CTRL 0xb1 +#define ACT8846_GLB_OFF_CTRL 0xc3 +#define ACT8846_OFF_SYSMASK 0x18 /* * ACT8865 Global Register Map. @@ -84,6 +86,7 @@ #define ACT8865_LDO3_CTRL 0x61 #define ACT8865_LDO4_VSET 0x64 #define ACT8865_LDO4_CTRL 0x65 +#define ACT8865_MSTROFF 0x20 /* * Field Definitions. @@ -98,6 +101,8 @@ struct act8865 { struct regmap *regmap; + int off_reg; + int off_mask; }; static const struct regmap_config act8865_regmap_config = { @@ -275,6 +280,16 @@ static struct regulator_init_data return NULL; } +static struct i2c_client *act8865_i2c_client; +static void act8865_power_off(void) +{ + struct act8865 *act8865; + + act8865 = i2c_get_clientdata(act8865_i2c_client); + regmap_write(act8865->regmap, act8865->off_reg, act8865->off_mask); + while (1); +} + static int act8865_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { @@ -285,6 +300,7 @@ static int act8865_pmic_probe(struct i2c_client *client, int i, ret, num_regulators; struct act8865 *act8865; unsigned long type; + int off_reg, off_mask; pdata = dev_get_platdata(dev); @@ -304,10 +320,14 @@ static int act8865_pmic_probe(struct i2c_client *client, case ACT8846: regulators = act8846_regulators; num_regulators = ARRAY_SIZE(act8846_regulators); + off_reg = ACT8846_GLB_OFF_CTRL; + off_mask = ACT8846_OFF_SYSMASK; break; case ACT8865: regulators = act8865_regulators; num_regulators = ARRAY_SIZE(act8865_regulators); + off_reg = ACT8865_SYS_CTRL; + off_mask = ACT8865_MSTROFF; break; default: dev_err(dev, "invalid device id %lu\n", type); @@ -345,6 +365,17 @@ static int act8865_pmic_probe(struct i2c_client *client, return ret; } + if (of_device_is_system_power_controller(dev->of_node)) { + if (!pm_power_off) { + act8865_i2c_client = client; + act8865->off_reg = off_reg; + act8865->off_mask = off_mask; + pm_power_off = act8865_power_off; + } else { + dev_err(dev, "Failed to set poweroff capability, already defined\n"); + } + } + /* Finally register devices */ for (i = 0; i < num_regulators; i++) { const struct regulator_desc *desc = ®ulators[i]; diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 4f730af70e7c..3586571814b2 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -189,17 +189,18 @@ static int anatop_regulator_probe(struct platform_device *pdev) int ret = 0; u32 val; - initdata = of_get_regulator_init_data(dev, np); sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); if (!sreg) return -ENOMEM; - sreg->initdata = initdata; sreg->name = of_get_property(np, "regulator-name", NULL); rdesc = &sreg->rdesc; rdesc->name = sreg->name; rdesc->type = REGULATOR_VOLTAGE; rdesc->owner = THIS_MODULE; + initdata = of_get_regulator_init_data(dev, np, rdesc); + sreg->initdata = initdata; + anatop_np = of_get_parent(np); if (!anatop_np) return -ENODEV; @@ -283,6 +284,19 @@ static int anatop_regulator_probe(struct platform_device *pdev) sreg->sel = 0; sreg->bypass = true; } + + /* + * In case vddpu was disabled by the bootloader, we need to set + * a sane default until imx6-cpufreq was probed and changes the + * voltage to the correct value. In this case we set 1.25V. + */ + if (!sreg->sel && !strcmp(sreg->name, "vddpu")) + sreg->sel = 22; + + if (!sreg->sel) { + dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n"); + return -EINVAL; + } } else { rdesc->ops = &anatop_rops; } diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 4c9db589f6c1..d071b2119a60 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -179,7 +179,8 @@ static const struct regulator_init_data arizona_ldo1_default = { }; static int arizona_ldo1_of_get_pdata(struct arizona *arizona, - struct regulator_config *config) + struct regulator_config *config, + const struct regulator_desc *desc) { struct arizona_pdata *pdata = &arizona->pdata; struct arizona_ldo1 *ldo1 = config->driver_data; @@ -194,7 +195,8 @@ static int arizona_ldo1_of_get_pdata(struct arizona *arizona, if (init_node) { config->of_node = init_node; - init_data = of_get_regulator_init_data(arizona->dev, init_node); + init_data = of_get_regulator_init_data(arizona->dev, init_node, + desc); if (init_data) { init_data->consumer_supplies = &ldo1->supply; @@ -257,9 +259,11 @@ static int arizona_ldo1_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_ldo1_of_get_pdata(arizona, &config); + ret = arizona_ldo1_of_get_pdata(arizona, &config, desc); if (ret < 0) return ret; + + config.ena_gpio_initialized = true; } } diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index ce9aca5f8ee7..c313ef4c3a2f 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -198,7 +198,8 @@ static const struct regulator_init_data arizona_micsupp_ext_default = { }; static int arizona_micsupp_of_get_pdata(struct arizona *arizona, - struct regulator_config *config) + struct regulator_config *config, + const struct regulator_desc *desc) { struct arizona_pdata *pdata = &arizona->pdata; struct arizona_micsupp *micsupp = config->driver_data; @@ -210,7 +211,7 @@ static int arizona_micsupp_of_get_pdata(struct arizona *arizona, if (np) { config->of_node = np; - init_data = of_get_regulator_init_data(arizona->dev, np); + init_data = of_get_regulator_init_data(arizona->dev, np, desc); if (init_data) { init_data->consumer_supplies = &micsupp->supply; @@ -264,7 +265,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_micsupp_of_get_pdata(arizona, &config); + ret = arizona_micsupp_of_get_pdata(arizona, &config, + desc); if (ret < 0) return ret; } diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index cd87c0c37034..e225711bb8bc 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -828,7 +828,7 @@ static void print_constraints(struct regulator_dev *rdev) if (!count) sprintf(buf, "no parameters"); - rdev_info(rdev, "%s\n", buf); + rdev_dbg(rdev, "%s\n", buf); if ((constraints->min_uV != constraints->max_uV) && !(constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) @@ -1713,6 +1713,8 @@ static void regulator_ena_gpio_free(struct regulator_dev *rdev) gpiod_put(pin->gpiod); list_del(&pin->list); kfree(pin); + rdev->ena_pin = NULL; + return; } else { pin->request_count--; } @@ -1976,9 +1978,18 @@ static int _regulator_disable(struct regulator_dev *rdev) /* we are last user */ if (_regulator_can_change_status(rdev)) { + ret = _notifier_call_chain(rdev, + REGULATOR_EVENT_PRE_DISABLE, + NULL); + if (ret & NOTIFY_STOP_MASK) + return -EINVAL; + ret = _regulator_do_disable(rdev); if (ret < 0) { rdev_err(rdev, "failed to disable\n"); + _notifier_call_chain(rdev, + REGULATOR_EVENT_ABORT_DISABLE, + NULL); return ret; } _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, @@ -2035,9 +2046,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; + ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_PRE_DISABLE, NULL); + if (ret & NOTIFY_STOP_MASK) + return -EINVAL; + ret = _regulator_do_disable(rdev); if (ret < 0) { rdev_err(rdev, "failed to force disable\n"); + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_ABORT_DISABLE, NULL); return ret; } @@ -3650,7 +3668,8 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); - if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { + if ((config->ena_gpio || config->ena_gpio_initialized) && + gpio_is_valid(config->ena_gpio)) { ret = regulator_ena_gpio_request(rdev, config); if (ret != 0) { rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 00033625a09c..3945f1006d23 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -436,7 +436,8 @@ static int da9052_regulator_probe(struct platform_device *pdev) if (!of_node_cmp(np->name, regulator->info->reg_desc.name)) { config.init_data = of_get_regulator_init_data( - &pdev->dev, np); + &pdev->dev, np, + ®ulator->info->reg_desc); config.of_node = np; break; } diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 7c9461d13313..37dd42759ca9 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -867,17 +867,14 @@ static int da9063_regulator_probe(struct platform_device *pdev) return irq; } - regulators->irq_ldo_lim = regmap_irq_get_virq(da9063->regmap_irq, irq); - if (regulators->irq_ldo_lim >= 0) { - ret = request_threaded_irq(regulators->irq_ldo_lim, - NULL, da9063_ldo_lim_event, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "LDO_LIM", regulators); - if (ret) { - dev_err(&pdev->dev, - "Failed to request LDO_LIM IRQ.\n"); - regulators->irq_ldo_lim = -ENXIO; - } + ret = request_threaded_irq(irq, + NULL, da9063_ldo_lim_event, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "LDO_LIM", regulators); + if (ret) { + dev_err(&pdev->dev, + "Failed to request LDO_LIM IRQ.\n"); + regulators->irq_ldo_lim = -ENXIO; } return 0; diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index 7a320dd11c46..bc6100103f7f 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -147,7 +147,7 @@ static int da9210_i2c_probe(struct i2c_client *i2c, config.dev = &i2c->dev; config.init_data = pdata ? &pdata->da9210_constraints : - of_get_regulator_init_data(dev, dev->of_node); + of_get_regulator_init_data(dev, dev->of_node, &da9210_reg); config.driver_data = chip; config.regmap = chip->regmap; config.of_node = dev->of_node; diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index 2436db9e2ca3..7aef9e4c6fbf 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -33,7 +33,7 @@ static struct regulator_init_data dummy_initdata = { static struct regulator_ops dummy_ops; -static struct regulator_desc dummy_desc = { +static const struct regulator_desc dummy_desc = { .name = "regulator-dummy", .id = -1, .type = REGULATOR_VOLTAGE, diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index f8e4257aef92..6c43ab2d5121 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -302,7 +302,8 @@ static struct regmap_config fan53555_regmap_config = { }; static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev, - struct device_node *np) + struct device_node *np, + const struct regulator_desc *desc) { struct fan53555_platform_data *pdata; int ret; @@ -312,7 +313,7 @@ static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev, if (!pdata) return NULL; - pdata->regulator = of_get_regulator_init_data(dev, np); + pdata->regulator = of_get_regulator_init_data(dev, np, desc); ret = of_property_read_u32(np, "fcs,suspend-voltage-selector", &tmp); @@ -347,20 +348,20 @@ static int fan53555_regulator_probe(struct i2c_client *client, unsigned int val; int ret; + di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info), + GFP_KERNEL); + if (!di) + return -ENOMEM; + pdata = dev_get_platdata(&client->dev); if (!pdata) - pdata = fan53555_parse_dt(&client->dev, np); + pdata = fan53555_parse_dt(&client->dev, np, &di->desc); if (!pdata || !pdata->regulator) { dev_err(&client->dev, "Platform data not found!\n"); return -ENODEV; } - di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info), - GFP_KERNEL); - if (!di) - return -ENOMEM; - di->regulator = pdata->regulator; if (client->dev.of_node) { const struct of_device_id *match; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 354105eff1f8..d21da27c0eb6 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -40,13 +40,15 @@ struct fixed_voltage_data { /** * of_get_fixed_voltage_config - extract fixed_voltage_config structure info * @dev: device requesting for fixed_voltage_config + * @desc: regulator description * * Populates fixed_voltage_config structure by extracting data from device * tree node, returns a pointer to the populated structure of NULL if memory * alloc fails. */ static struct fixed_voltage_config * -of_get_fixed_voltage_config(struct device *dev) +of_get_fixed_voltage_config(struct device *dev, + const struct regulator_desc *desc) { struct fixed_voltage_config *config; struct device_node *np = dev->of_node; @@ -57,7 +59,7 @@ of_get_fixed_voltage_config(struct device *dev) if (!config) return ERR_PTR(-ENOMEM); - config->init_data = of_get_regulator_init_data(dev, dev->of_node); + config->init_data = of_get_regulator_init_data(dev, dev->of_node, desc); if (!config->init_data) return ERR_PTR(-EINVAL); @@ -112,8 +114,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) struct regulator_config cfg = { }; int ret; + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + if (pdev->dev.of_node) { - config = of_get_fixed_voltage_config(&pdev->dev); + config = of_get_fixed_voltage_config(&pdev->dev, + &drvdata->desc); if (IS_ERR(config)) return PTR_ERR(config); } else { @@ -123,11 +131,6 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) if (!config) return -ENOMEM; - drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), - GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - drvdata->desc.name = devm_kstrdup(&pdev->dev, config->supply_name, GFP_KERNEL); @@ -157,8 +160,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.fixed_uV = config->microvolts; - if (config->gpio >= 0) + if (gpio_is_valid(config->gpio)) { cfg.ena_gpio = config->gpio; + if (pdev->dev.of_node) + cfg.ena_gpio_initialized = true; + } cfg.ena_gpio_invert = !config->enable_high; if (config->enabled_at_boot) { if (config->enable_high) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 989b23b377c0..c888a9a9482c 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -133,7 +133,8 @@ static struct regulator_ops gpio_regulator_voltage_ops = { }; static struct gpio_regulator_config * -of_get_gpio_regulator_config(struct device *dev, struct device_node *np) +of_get_gpio_regulator_config(struct device *dev, struct device_node *np, + const struct regulator_desc *desc) { struct gpio_regulator_config *config; const char *regtype; @@ -146,7 +147,7 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) if (!config) return ERR_PTR(-ENOMEM); - config->init_data = of_get_regulator_init_data(dev, np); + config->init_data = of_get_regulator_init_data(dev, np, desc); if (!config->init_data) return ERR_PTR(-EINVAL); @@ -162,34 +163,41 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); - /* Fetch GPIOs. */ - config->nr_gpios = of_gpio_count(np); - - config->gpios = devm_kzalloc(dev, - sizeof(struct gpio) * config->nr_gpios, - GFP_KERNEL); - if (!config->gpios) - return ERR_PTR(-ENOMEM); - - proplen = of_property_count_u32_elems(np, "gpios-states"); - /* optional property */ - if (proplen < 0) - proplen = 0; - - if (proplen > 0 && proplen != config->nr_gpios) { - dev_warn(dev, "gpios <-> gpios-states mismatch\n"); - proplen = 0; - } + /* Fetch GPIOs. - optional property*/ + ret = of_gpio_count(np); + if ((ret < 0) && (ret != -ENOENT)) + return ERR_PTR(ret); + + if (ret > 0) { + config->nr_gpios = ret; + config->gpios = devm_kzalloc(dev, + sizeof(struct gpio) * config->nr_gpios, + GFP_KERNEL); + if (!config->gpios) + return ERR_PTR(-ENOMEM); + + proplen = of_property_count_u32_elems(np, "gpios-states"); + /* optional property */ + if (proplen < 0) + proplen = 0; + + if (proplen > 0 && proplen != config->nr_gpios) { + dev_warn(dev, "gpios <-> gpios-states mismatch\n"); + proplen = 0; + } - for (i = 0; i < config->nr_gpios; i++) { - gpio = of_get_named_gpio(np, "gpios", i); - if (gpio < 0) - break; - config->gpios[i].gpio = gpio; - if (proplen > 0) { - of_property_read_u32_index(np, "gpios-states", i, &ret); - if (ret) - config->gpios[i].flags = GPIOF_OUT_INIT_HIGH; + for (i = 0; i < config->nr_gpios; i++) { + gpio = of_get_named_gpio(np, "gpios", i); + if (gpio < 0) + break; + config->gpios[i].gpio = gpio; + if (proplen > 0) { + of_property_read_u32_index(np, "gpios-states", + i, &ret); + if (ret) + config->gpios[i].flags = + GPIOF_OUT_INIT_HIGH; + } } } @@ -243,17 +251,18 @@ static int gpio_regulator_probe(struct platform_device *pdev) struct regulator_config cfg = { }; int ptr, ret, state; - if (np) { - config = of_get_gpio_regulator_config(&pdev->dev, np); - if (IS_ERR(config)) - return PTR_ERR(config); - } - drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), GFP_KERNEL); if (drvdata == NULL) return -ENOMEM; + if (np) { + config = of_get_gpio_regulator_config(&pdev->dev, np, + &drvdata->desc); + if (IS_ERR(config)) + return PTR_ERR(config); + } + drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); if (drvdata->desc.name == NULL) { dev_err(&pdev->dev, "Failed to allocate supply name\n"); @@ -261,13 +270,23 @@ static int gpio_regulator_probe(struct platform_device *pdev) goto err; } - drvdata->gpios = kmemdup(config->gpios, - config->nr_gpios * sizeof(struct gpio), - GFP_KERNEL); - if (drvdata->gpios == NULL) { - dev_err(&pdev->dev, "Failed to allocate gpio data\n"); - ret = -ENOMEM; - goto err_name; + if (config->nr_gpios != 0) { + drvdata->gpios = kmemdup(config->gpios, + config->nr_gpios * sizeof(struct gpio), + GFP_KERNEL); + if (drvdata->gpios == NULL) { + dev_err(&pdev->dev, "Failed to allocate gpio data\n"); + ret = -ENOMEM; + goto err_name; + } + + drvdata->nr_gpios = config->nr_gpios; + ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator setting GPIOs: %d\n", ret); + goto err_memstate; + } } drvdata->states = kmemdup(config->states, @@ -301,14 +320,6 @@ static int gpio_regulator_probe(struct platform_device *pdev) goto err_memgpio; } - drvdata->nr_gpios = config->nr_gpios; - ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); - if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator setting GPIOs: %d\n", ret); - goto err_memstate; - } - /* build initial state from gpio init data. */ state = 0; for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { @@ -322,8 +333,10 @@ static int gpio_regulator_probe(struct platform_device *pdev) cfg.driver_data = drvdata; cfg.of_node = np; - if (config->enable_gpio >= 0) + if (gpio_is_valid(config->enable_gpio)) { cfg.ena_gpio = config->enable_gpio; + cfg.ena_gpio_initialized = true; + } cfg.ena_gpio_invert = !config->enable_high; if (config->enabled_at_boot) { if (config->enable_high) diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index f69320e1738f..871b96bcd2d0 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -45,6 +45,23 @@ #define MAX77686_DVS_MINUV 600000 #define MAX77686_DVS_UVSTEP 12500 +/* + * Values used for configuring LDOs and bucks. + * Forcing low power mode: LDO1, 3-5, 9, 13, 17-26 + */ +#define MAX77686_LDO_LOWPOWER 0x1 +/* + * On/off controlled by PWRREQ: + * - LDO2, 6-8, 10-12, 14-16 + * - buck[1234] + */ +#define MAX77686_OFF_PWRREQ 0x1 +/* Low power mode controlled by PWRREQ: All LDOs */ +#define MAX77686_LDO_LOWPOWER_PWRREQ 0x2 +/* Forcing low power mode: buck[234] */ +#define MAX77686_BUCK_LOWPOWER 0x2 +#define MAX77686_NORMAL 0x3 + #define MAX77686_OPMODE_SHIFT 6 #define MAX77686_OPMODE_BUCK234_SHIFT 4 #define MAX77686_OPMODE_MASK 0x3 @@ -65,23 +82,36 @@ enum max77686_ramp_rate { }; struct max77686_data { + /* Array indexed by regulator id */ unsigned int opmode[MAX77686_REGULATORS]; }; -/* Some BUCKS supports Normal[ON/OFF] mode during suspend */ -static int max77686_buck_set_suspend_disable(struct regulator_dev *rdev) +static unsigned int max77686_get_opmode_shift(int id) { - unsigned int val; + switch (id) { + case MAX77686_BUCK1: + case MAX77686_BUCK5 ... MAX77686_BUCK9: + return 0; + case MAX77686_BUCK2 ... MAX77686_BUCK4: + return MAX77686_OPMODE_BUCK234_SHIFT; + default: + /* all LDOs */ + return MAX77686_OPMODE_SHIFT; + } +} + +/* Some BUCKs and LDOs supports Normal[ON/OFF] mode during suspend */ +static int max77686_set_suspend_disable(struct regulator_dev *rdev) +{ + unsigned int val, shift; struct max77686_data *max77686 = rdev_get_drvdata(rdev); int ret, id = rdev_get_id(rdev); - if (id == MAX77686_BUCK1) - val = 0x1; - else - val = 0x1 << MAX77686_OPMODE_BUCK234_SHIFT; + shift = max77686_get_opmode_shift(id); + val = MAX77686_OFF_PWRREQ; ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, val); + rdev->desc->enable_mask, val << shift); if (ret) return ret; @@ -103,10 +133,10 @@ static int max77686_set_suspend_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_IDLE: /* ON in LP Mode */ - val = 0x2 << MAX77686_OPMODE_SHIFT; + val = MAX77686_LDO_LOWPOWER_PWRREQ; break; case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = 0x3 << MAX77686_OPMODE_SHIFT; + val = MAX77686_NORMAL; break; default: pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", @@ -115,7 +145,8 @@ static int max77686_set_suspend_mode(struct regulator_dev *rdev, } ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, val); + rdev->desc->enable_mask, + val << MAX77686_OPMODE_SHIFT); if (ret) return ret; @@ -133,13 +164,13 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_STANDBY: /* switch off */ - val = 0x1 << MAX77686_OPMODE_SHIFT; + val = MAX77686_OFF_PWRREQ; break; case REGULATOR_MODE_IDLE: /* ON in LP Mode */ - val = 0x2 << MAX77686_OPMODE_SHIFT; + val = MAX77686_LDO_LOWPOWER_PWRREQ; break; case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = 0x3 << MAX77686_OPMODE_SHIFT; + val = MAX77686_NORMAL; break; default: pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", @@ -148,7 +179,8 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, } ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, val); + rdev->desc->enable_mask, + val << MAX77686_OPMODE_SHIFT); if (ret) return ret; @@ -159,10 +191,17 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, static int max77686_enable(struct regulator_dev *rdev) { struct max77686_data *max77686 = rdev_get_drvdata(rdev); + unsigned int shift; + int id = rdev_get_id(rdev); + + shift = max77686_get_opmode_shift(id); + + if (max77686->opmode[id] == MAX77686_OFF_PWRREQ) + max77686->opmode[id] = MAX77686_NORMAL; return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, - max77686->opmode[rdev_get_id(rdev)]); + max77686->opmode[id] << shift); } static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) @@ -212,6 +251,7 @@ static struct regulator_ops max77686_ldo_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_suspend_mode = max77686_ldo_set_suspend_mode, + .set_suspend_disable = max77686_set_suspend_disable, }; static struct regulator_ops max77686_buck1_ops = { @@ -223,7 +263,7 @@ static struct regulator_ops max77686_buck1_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_suspend_disable = max77686_buck_set_suspend_disable, + .set_suspend_disable = max77686_set_suspend_disable, }; static struct regulator_ops max77686_buck_dvs_ops = { @@ -236,11 +276,13 @@ static struct regulator_ops max77686_buck_dvs_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_ramp_delay = max77686_set_ramp_delay, - .set_suspend_disable = max77686_buck_set_suspend_disable, + .set_suspend_disable = max77686_set_suspend_disable, }; #define regulator_desc_ldo(num) { \ .name = "LDO"#num, \ + .of_match = of_match_ptr("LDO"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_LDO##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -257,6 +299,8 @@ static struct regulator_ops max77686_buck_dvs_ops = { } #define regulator_desc_lpm_ldo(num) { \ .name = "LDO"#num, \ + .of_match = of_match_ptr("LDO"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_LDO##num, \ .ops = &max77686_ldo_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -273,6 +317,8 @@ static struct regulator_ops max77686_buck_dvs_ops = { } #define regulator_desc_ldo_low(num) { \ .name = "LDO"#num, \ + .of_match = of_match_ptr("LDO"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_LDO##num, \ .ops = &max77686_ldo_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -289,6 +335,8 @@ static struct regulator_ops max77686_buck_dvs_ops = { } #define regulator_desc_ldo1_low(num) { \ .name = "LDO"#num, \ + .of_match = of_match_ptr("LDO"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_LDO##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -305,6 +353,8 @@ static struct regulator_ops max77686_buck_dvs_ops = { } #define regulator_desc_buck(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_BUCK##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -320,6 +370,8 @@ static struct regulator_ops max77686_buck_dvs_ops = { } #define regulator_desc_buck1(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_BUCK##num, \ .ops = &max77686_buck1_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -335,6 +387,8 @@ static struct regulator_ops max77686_buck_dvs_ops = { } #define regulator_desc_buck_dvs(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("voltage-regulators"), \ .id = MAX77686_BUCK##num, \ .ops = &max77686_buck_dvs_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -350,7 +404,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { << MAX77686_OPMODE_BUCK234_SHIFT, \ } -static struct regulator_desc regulators[] = { +static const struct regulator_desc regulators[] = { regulator_desc_ldo1_low(1), regulator_desc_ldo_low(2), regulator_desc_ldo(3), @@ -388,103 +442,37 @@ static struct regulator_desc regulators[] = { regulator_desc_buck(9), }; -#ifdef CONFIG_OF -static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, - struct max77686_platform_data *pdata) -{ - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct device_node *pmic_np, *regulators_np; - struct max77686_regulator_data *rdata; - struct of_regulator_match rmatch = { }; - unsigned int i; - - pmic_np = iodev->dev->of_node; - regulators_np = of_get_child_by_name(pmic_np, "voltage-regulators"); - if (!regulators_np) { - dev_err(&pdev->dev, "could not find regulators sub-node\n"); - return -EINVAL; - } - - pdata->num_regulators = ARRAY_SIZE(regulators); - rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * - pdata->num_regulators, GFP_KERNEL); - if (!rdata) { - of_node_put(regulators_np); - return -ENOMEM; - } - - for (i = 0; i < pdata->num_regulators; i++) { - rmatch.name = regulators[i].name; - rmatch.init_data = NULL; - rmatch.of_node = NULL; - of_regulator_match(&pdev->dev, regulators_np, &rmatch, 1); - rdata[i].initdata = rmatch.init_data; - rdata[i].of_node = rmatch.of_node; - } - - pdata->regulators = rdata; - of_node_put(regulators_np); - - return 0; -} -#else -static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, - struct max77686_platform_data *pdata) -{ - return 0; -} -#endif /* CONFIG_OF */ - static int max77686_pmic_probe(struct platform_device *pdev) { struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev); struct max77686_data *max77686; - int i, ret = 0; + int i; struct regulator_config config = { }; dev_dbg(&pdev->dev, "%s\n", __func__); - if (!pdata) { - dev_err(&pdev->dev, "no platform data found for regulator\n"); - return -ENODEV; - } - - if (iodev->dev->of_node) { - ret = max77686_pmic_dt_parse_pdata(pdev, pdata); - if (ret) - return ret; - } - - if (pdata->num_regulators != MAX77686_REGULATORS) { - dev_err(&pdev->dev, - "Invalid initial data for regulator's initialiation\n"); - return -EINVAL; - } - max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data), GFP_KERNEL); if (!max77686) return -ENOMEM; - config.dev = &pdev->dev; + config.dev = iodev->dev; config.regmap = iodev->regmap; config.driver_data = max77686; platform_set_drvdata(pdev, max77686); for (i = 0; i < MAX77686_REGULATORS; i++) { struct regulator_dev *rdev; + int id = regulators[i].id; - config.init_data = pdata->regulators[i].initdata; - config.of_node = pdata->regulators[i].of_node; - - max77686->opmode[i] = regulators[i].enable_mask; + max77686->opmode[id] = MAX77686_NORMAL; rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(rdev)) { + int ret = PTR_ERR(rdev); dev_err(&pdev->dev, - "regulator init failed for %d\n", i); - return PTR_ERR(rdev); + "regulator init failed for %d: %d\n", i, ret); + return ret; } } diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index d158f71fa128..7b9755a6c3b5 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -139,7 +139,7 @@ static struct regulator_ops max77693_charger_ops = { .enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \ } -static struct regulator_desc regulators[] = { +static const struct regulator_desc regulators[] = { regulator_desc_esafeout(1), regulator_desc_esafeout(2), { diff --git a/drivers/regulator/max77802.c b/drivers/regulator/max77802.c index 45fa240fe243..0766615c60bc 100644 --- a/drivers/regulator/max77802.c +++ b/drivers/regulator/max77802.c @@ -33,6 +33,7 @@ #include <linux/regulator/of_regulator.h> #include <linux/mfd/max77686.h> #include <linux/mfd/max77686-private.h> +#include <dt-bindings/regulator/maxim,max77802.h> /* Default ramp delay in case it is not manually set */ #define MAX77802_RAMP_DELAY 100000 /* uV/us */ @@ -49,6 +50,10 @@ #define MAX77802_RAMP_RATE_MASK_4BIT 0xF0 #define MAX77802_RAMP_RATE_SHIFT_4BIT 4 +#define MAX77802_STATUS_OFF 0x0 +#define MAX77802_OFF_PWRREQ 0x1 +#define MAX77802_LP_PWRREQ 0x2 + /* MAX77802 has two register formats: 2-bit and 4-bit */ static const unsigned int ramp_table_77802_2bit[] = { 12500, @@ -65,9 +70,16 @@ static unsigned int ramp_table_77802_4bit[] = { }; struct max77802_regulator_prv { + /* Array indexed by regulator id */ unsigned int opmode[MAX77802_REG_MAX]; }; +static inline unsigned int max77802_map_mode(unsigned int mode) +{ + return mode == MAX77802_OPMODE_NORMAL ? + REGULATOR_MODE_NORMAL : REGULATOR_MODE_STANDBY; +} + static int max77802_get_opmode_shift(int id) { if (id == MAX77802_BUCK1 || (id >= MAX77802_BUCK5 && @@ -83,17 +95,16 @@ static int max77802_get_opmode_shift(int id) return -EINVAL; } -/* - * Some BUCKS supports Normal[ON/OFF] mode during suspend +/** + * max77802_set_suspend_disable - Disable the regulator during system suspend + * @rdev: regulator to mark as disabled * - * BUCK 1, 6, 2-4, 5, 7-10 (all) - * - * The other mode (0x02) will make PWRREQ switch between normal - * and low power. + * All regulators expect LDO 1, 3, 20 and 21 support OFF by PWRREQ. + * Configure the regulator so the PMIC will turn it OFF during system suspend. */ -static int max77802_buck_set_suspend_disable(struct regulator_dev *rdev) +static int max77802_set_suspend_disable(struct regulator_dev *rdev) { - unsigned int val = MAX77802_OPMODE_STANDBY; + unsigned int val = MAX77802_OFF_PWRREQ; struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev); int id = rdev_get_id(rdev); int shift = max77802_get_opmode_shift(id); @@ -104,14 +115,11 @@ static int max77802_buck_set_suspend_disable(struct regulator_dev *rdev) } /* - * Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state - * (Enable Control Logic1 by PWRREQ) - * - * LDOs 2, 4-19, 22-35. + * Some LDOs support Low Power Mode while the system is running. * + * LDOs 1, 3, 20, 21. */ -static int max77802_ldo_set_suspend_mode_logic1(struct regulator_dev *rdev, - unsigned int mode) +static int max77802_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev); int id = rdev_get_id(rdev); @@ -119,14 +127,11 @@ static int max77802_ldo_set_suspend_mode_logic1(struct regulator_dev *rdev, int shift = max77802_get_opmode_shift(id); switch (mode) { - case REGULATOR_MODE_IDLE: /* ON in LP Mode */ - val = MAX77802_OPMODE_LP; + case REGULATOR_MODE_STANDBY: + val = MAX77802_OPMODE_LP; /* ON in Low Power Mode */ break; - case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = MAX77802_OPMODE_NORMAL; - break; - case REGULATOR_MODE_STANDBY: /* ON/OFF by PWRREQ */ - val = MAX77802_OPMODE_STANDBY; + case REGULATOR_MODE_NORMAL: + val = MAX77802_OPMODE_NORMAL; /* ON in Normal Mode */ break; default: dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n", @@ -139,35 +144,76 @@ static int max77802_ldo_set_suspend_mode_logic1(struct regulator_dev *rdev, rdev->desc->enable_mask, val << shift); } -/* - * Mode 1 (Output[ON/OFF] by PWRREQ) is not supported on some LDOs - * (Enable Control Logic2 by PWRREQ) +static unsigned max77802_get_mode(struct regulator_dev *rdev) +{ + struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + + return max77802_map_mode(max77802->opmode[id]); +} + +/** + * max77802_set_suspend_mode - set regulator opmode when the system is suspended + * @rdev: regulator to change mode + * @mode: operating mode to be set * - * LDOs 1, 20, 21, and 3, + * Will set the operating mode for the regulators during system suspend. + * This function is valid for the three different enable control logics: * + * Enable Control Logic1 by PWRREQ (BUCK 2-4 and LDOs 2, 4-19, 22-35) + * Enable Control Logic2 by PWRREQ (LDOs 1, 20, 21) + * Enable Control Logic3 by PWRREQ (LDO 3) + * + * If setting the regulator mode fails, the function only warns but does + * not return an error code to avoid the regulator core to stop setting + * the operating mode for the remaining regulators. */ -static int max77802_ldo_set_suspend_mode_logic2(struct regulator_dev *rdev, - unsigned int mode) +static int max77802_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) { struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev); int id = rdev_get_id(rdev); unsigned int val; int shift = max77802_get_opmode_shift(id); + /* + * If the regulator has been disabled for suspend + * then is invalid to try setting a suspend mode. + */ + if (max77802->opmode[id] == MAX77802_OFF_PWRREQ) { + dev_warn(&rdev->dev, "%s: is disabled, mode: 0x%x not set\n", + rdev->desc->name, mode); + return 0; + } + switch (mode) { - case REGULATOR_MODE_IDLE: /* ON in LP Mode */ - val = MAX77802_OPMODE_LP; - break; - case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = MAX77802_OPMODE_NORMAL; + case REGULATOR_MODE_STANDBY: + /* + * If the regulator opmode is normal then enable + * ON in Low Power Mode by PWRREQ. If the mode is + * already Low Power then no action is required. + */ + if (max77802->opmode[id] == MAX77802_OPMODE_NORMAL) + val = MAX77802_LP_PWRREQ; + else + return 0; break; + case REGULATOR_MODE_NORMAL: + /* + * If the regulator operating mode is Low Power then + * normal is not a valid opmode in suspend. If the + * mode is already normal then no action is required. + */ + if (max77802->opmode[id] == MAX77802_OPMODE_LP) + dev_warn(&rdev->dev, "%s: in Low Power: 0x%x invalid\n", + rdev->desc->name, mode); + return 0; default: dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n", rdev->desc->name, mode); return -EINVAL; } - max77802->opmode[id] = val; return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, val << shift); } @@ -178,6 +224,9 @@ static int max77802_enable(struct regulator_dev *rdev) int id = rdev_get_id(rdev); int shift = max77802_get_opmode_shift(id); + if (max77802->opmode[id] == MAX77802_OFF_PWRREQ) + max77802->opmode[id] = MAX77802_OPMODE_NORMAL; + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, max77802->opmode[id] << shift); @@ -247,7 +296,8 @@ static struct regulator_ops max77802_ldo_ops_logic1 = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_suspend_mode = max77802_ldo_set_suspend_mode_logic1, + .set_suspend_disable = max77802_set_suspend_disable, + .set_suspend_mode = max77802_set_suspend_mode, }; /* @@ -262,7 +312,9 @@ static struct regulator_ops max77802_ldo_ops_logic2 = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_suspend_mode = max77802_ldo_set_suspend_mode_logic2, + .set_mode = max77802_set_mode, + .get_mode = max77802_get_mode, + .set_suspend_mode = max77802_set_suspend_mode, }; /* BUCKS 1, 6 */ @@ -276,10 +328,25 @@ static struct regulator_ops max77802_buck_16_dvs_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_ramp_delay = max77802_set_ramp_delay_4bit, - .set_suspend_disable = max77802_buck_set_suspend_disable, + .set_suspend_disable = max77802_set_suspend_disable, }; -/* BUCKs 2-4, 5, 7-10 */ +/* BUCKs 2-4 */ +static struct regulator_ops max77802_buck_234_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = max77802_enable, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = max77802_set_ramp_delay_2bit, + .set_suspend_disable = max77802_set_suspend_disable, + .set_suspend_mode = max77802_set_suspend_mode, +}; + +/* BUCKs 5, 7-10 */ static struct regulator_ops max77802_buck_dvs_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -290,12 +357,14 @@ static struct regulator_ops max77802_buck_dvs_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_ramp_delay = max77802_set_ramp_delay_2bit, - .set_suspend_disable = max77802_buck_set_suspend_disable, + .set_suspend_disable = max77802_set_suspend_disable, }; /* LDOs 3-7, 9-14, 18-26, 28, 29, 32-34 */ #define regulator_77802_desc_p_ldo(num, supply, log) { \ .name = "LDO"#num, \ + .of_match = of_match_ptr("LDO"#num), \ + .regulators_node = of_match_ptr("regulators"), \ .id = MAX77802_LDO##num, \ .supply_name = "inl"#supply, \ .ops = &max77802_ldo_ops_logic##log, \ @@ -309,11 +378,14 @@ static struct regulator_ops max77802_buck_dvs_ops = { .vsel_mask = MAX77802_VSEL_MASK, \ .enable_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \ .enable_mask = MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \ + .of_map_mode = max77802_map_mode, \ } /* LDOs 1, 2, 8, 15, 17, 27, 30, 35 */ #define regulator_77802_desc_n_ldo(num, supply, log) { \ .name = "LDO"#num, \ + .of_match = of_match_ptr("LDO"#num), \ + .regulators_node = of_match_ptr("regulators"), \ .id = MAX77802_LDO##num, \ .supply_name = "inl"#supply, \ .ops = &max77802_ldo_ops_logic##log, \ @@ -327,11 +399,14 @@ static struct regulator_ops max77802_buck_dvs_ops = { .vsel_mask = MAX77802_VSEL_MASK, \ .enable_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \ .enable_mask = MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \ + .of_map_mode = max77802_map_mode, \ } /* BUCKs 1, 6 */ #define regulator_77802_desc_16_buck(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("regulators"), \ .id = MAX77802_BUCK##num, \ .supply_name = "inb"#num, \ .ops = &max77802_buck_16_dvs_ops, \ @@ -345,14 +420,17 @@ static struct regulator_ops max77802_buck_dvs_ops = { .vsel_mask = MAX77802_DVS_VSEL_MASK, \ .enable_reg = MAX77802_REG_BUCK ## num ## CTRL, \ .enable_mask = MAX77802_OPMODE_MASK, \ + .of_map_mode = max77802_map_mode, \ } /* BUCKS 2-4 */ #define regulator_77802_desc_234_buck(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("regulators"), \ .id = MAX77802_BUCK##num, \ .supply_name = "inb"#num, \ - .ops = &max77802_buck_dvs_ops, \ + .ops = &max77802_buck_234_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ .min_uV = 600000, \ @@ -364,11 +442,14 @@ static struct regulator_ops max77802_buck_dvs_ops = { .enable_reg = MAX77802_REG_BUCK ## num ## CTRL1, \ .enable_mask = MAX77802_OPMODE_MASK << \ MAX77802_OPMODE_BUCK234_SHIFT, \ + .of_map_mode = max77802_map_mode, \ } /* BUCK 5 */ #define regulator_77802_desc_buck5(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("regulators"), \ .id = MAX77802_BUCK##num, \ .supply_name = "inb"#num, \ .ops = &max77802_buck_dvs_ops, \ @@ -382,11 +463,14 @@ static struct regulator_ops max77802_buck_dvs_ops = { .vsel_mask = MAX77802_VSEL_MASK, \ .enable_reg = MAX77802_REG_BUCK5CTRL, \ .enable_mask = MAX77802_OPMODE_MASK, \ + .of_map_mode = max77802_map_mode, \ } /* BUCKs 7-10 */ #define regulator_77802_desc_buck7_10(num) { \ .name = "BUCK"#num, \ + .of_match = of_match_ptr("BUCK"#num), \ + .regulators_node = of_match_ptr("regulators"), \ .id = MAX77802_BUCK##num, \ .supply_name = "inb"#num, \ .ops = &max77802_buck_dvs_ops, \ @@ -400,9 +484,10 @@ static struct regulator_ops max77802_buck_dvs_ops = { .vsel_mask = MAX77802_VSEL_MASK, \ .enable_reg = MAX77802_REG_BUCK7CTRL + (num - 7) * 3, \ .enable_mask = MAX77802_OPMODE_MASK, \ + .of_map_mode = max77802_map_mode, \ } -static struct regulator_desc regulators[] = { +static const struct regulator_desc regulators[] = { regulator_77802_desc_16_buck(1), regulator_77802_desc_234_buck(2), regulator_77802_desc_234_buck(3), @@ -447,85 +532,19 @@ static struct regulator_desc regulators[] = { regulator_77802_desc_n_ldo(35, 2, 1), }; -#ifdef CONFIG_OF -static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev, - struct max77686_platform_data *pdata) -{ - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct device_node *pmic_np, *regulators_np; - struct max77686_regulator_data *rdata; - struct of_regulator_match rmatch = { }; - unsigned int i; - - pmic_np = iodev->dev->of_node; - regulators_np = of_get_child_by_name(pmic_np, "regulators"); - if (!regulators_np) { - dev_err(&pdev->dev, "could not find regulators sub-node\n"); - return -EINVAL; - } - - pdata->num_regulators = ARRAY_SIZE(regulators); - rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * - pdata->num_regulators, GFP_KERNEL); - if (!rdata) { - of_node_put(regulators_np); - return -ENOMEM; - } - - for (i = 0; i < pdata->num_regulators; i++) { - rmatch.name = regulators[i].name; - rmatch.init_data = NULL; - rmatch.of_node = NULL; - if (of_regulator_match(&pdev->dev, regulators_np, &rmatch, - 1) != 1) { - dev_warn(&pdev->dev, "No matching regulator for '%s'\n", - rmatch.name); - continue; - } - rdata[i].initdata = rmatch.init_data; - rdata[i].of_node = rmatch.of_node; - rdata[i].id = regulators[i].id; - } - - pdata->regulators = rdata; - of_node_put(regulators_np); - - return 0; -} -#else -static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev, - struct max77686_platform_data *pdata) -{ - return 0; -} -#endif /* CONFIG_OF */ - static int max77802_pmic_probe(struct platform_device *pdev) { struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev); struct max77802_regulator_prv *max77802; - int i, ret = 0, val; + int i, val; struct regulator_config config = { }; - /* This is allocated by the MFD driver */ - if (!pdata) { - dev_err(&pdev->dev, "no platform data found for regulator\n"); - return -ENODEV; - } - max77802 = devm_kzalloc(&pdev->dev, sizeof(struct max77802_regulator_prv), GFP_KERNEL); if (!max77802) return -ENOMEM; - if (iodev->dev->of_node) { - ret = max77802_pmic_dt_parse_pdata(pdev, pdata); - if (ret) - return ret; - } - config.dev = iodev->dev; config.regmap = iodev->regmap; config.driver_data = max77802; @@ -533,21 +552,25 @@ static int max77802_pmic_probe(struct platform_device *pdev) for (i = 0; i < MAX77802_REG_MAX; i++) { struct regulator_dev *rdev; - int id = pdata->regulators[i].id; + int id = regulators[i].id; int shift = max77802_get_opmode_shift(id); - - config.init_data = pdata->regulators[i].initdata; - config.of_node = pdata->regulators[i].of_node; + int ret; ret = regmap_read(iodev->regmap, regulators[i].enable_reg, &val); - val = val >> shift & MAX77802_OPMODE_MASK; + if (ret < 0) { + dev_warn(&pdev->dev, + "cannot read current mode for %d\n", i); + val = MAX77802_OPMODE_NORMAL; + } else { + val = val >> shift & MAX77802_OPMODE_MASK; + } /* * If the regulator is disabled and the system warm rebooted, * the hardware reports OFF as the regulator operating mode. * Default to operating mode NORMAL in that case. */ - if (val == MAX77802_OPMODE_OFF) + if (val == MAX77802_STATUS_OFF) max77802->opmode[id] = MAX77802_OPMODE_NORMAL; else max77802->opmode[id] = val; @@ -555,9 +578,10 @@ static int max77802_pmic_probe(struct platform_device *pdev) rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); dev_err(&pdev->dev, - "regulator init failed for %d\n", i); - return PTR_ERR(rdev); + "regulator init failed for %d: %d\n", i, ret); + return ret; } } diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index f7f9efcfedb7..1af8f4a2ab86 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -174,7 +174,7 @@ static struct max8952_platform_data *max8952_parse_dt(struct device *dev) if (of_property_read_u32(np, "max8952,ramp-speed", &pd->ramp_speed)) dev_warn(dev, "max8952,ramp-speed property not specified, defaulting to 32mV/us\n"); - pd->reg_data = of_get_regulator_init_data(dev, np); + pd->reg_data = of_get_regulator_init_data(dev, np, ®ulator); if (!pd->reg_data) { dev_err(dev, "Failed to parse regulator init data\n"); return NULL; @@ -225,6 +225,8 @@ static int max8952_pmic_probe(struct i2c_client *client, config.of_node = client->dev.of_node; config.ena_gpio = pdata->gpio_en; + if (client->dev.of_node) + config.ena_gpio_initialized = true; if (pdata->reg_data->constraints.boot_on) config.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index dbedf1768db0..c3d55c2db593 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -458,7 +458,8 @@ static int max8973_probe(struct i2c_client *client, config.dev = &client->dev; config.init_data = pdata ? pdata->reg_init_data : - of_get_regulator_init_data(&client->dev, client->dev.of_node); + of_get_regulator_init_data(&client->dev, client->dev.of_node, + &max->desc); config.driver_data = max; config.of_node = client->dev.of_node; config.regmap = max->regmap; diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 9c31e215a521..726fde1d883e 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -953,7 +953,8 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, rdata->id = i; rdata->initdata = of_get_regulator_init_data(&pdev->dev, - reg_np); + reg_np, + ®ulators[i]); rdata->reg_node = reg_np; rdata++; } diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 961091b46557..59e34a05a4a2 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -686,8 +686,9 @@ static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev, continue; rdata->id = regulators[i].id; - rdata->initdata = of_get_regulator_init_data( - iodev->dev, reg_np); + rdata->initdata = of_get_regulator_init_data(iodev->dev, + reg_np, + ®ulators[i]); rdata->reg_node = reg_np; ++rdata; } diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index afba024953e1..0281c31ae2ed 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -194,7 +194,8 @@ struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( regulators[i].desc.name)) { p->id = i; p->init_data = of_get_regulator_init_data( - &pdev->dev, child); + &pdev->dev, child, + ®ulators[i].desc); p->node = child; p++; diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 5a1d4afa4776..91eaaf010524 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -19,12 +19,20 @@ #include "internal.h" +static const char *const regulator_states[PM_SUSPEND_MAX + 1] = { + [PM_SUSPEND_MEM] = "regulator-state-mem", + [PM_SUSPEND_MAX] = "regulator-state-disk", +}; + static void of_get_regulation_constraints(struct device_node *np, - struct regulator_init_data **init_data) + struct regulator_init_data **init_data, + const struct regulator_desc *desc) { const __be32 *min_uV, *max_uV; struct regulation_constraints *constraints = &(*init_data)->constraints; - int ret; + struct regulator_state *suspend_state; + struct device_node *suspend_np; + int ret, i; u32 pval; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -73,18 +81,84 @@ static void of_get_regulation_constraints(struct device_node *np, ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval); if (!ret) constraints->enable_time = pval; + + if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) { + if (desc && desc->of_map_mode) { + ret = desc->of_map_mode(pval); + if (ret == -EINVAL) + pr_err("%s: invalid mode %u\n", np->name, pval); + else + constraints->initial_mode = ret; + } else { + pr_warn("%s: mapping for mode %d not defined\n", + np->name, pval); + } + } + + for (i = 0; i < ARRAY_SIZE(regulator_states); i++) { + switch (i) { + case PM_SUSPEND_MEM: + suspend_state = &constraints->state_mem; + break; + case PM_SUSPEND_MAX: + suspend_state = &constraints->state_disk; + break; + case PM_SUSPEND_ON: + case PM_SUSPEND_FREEZE: + case PM_SUSPEND_STANDBY: + default: + continue; + }; + + suspend_np = of_get_child_by_name(np, regulator_states[i]); + if (!suspend_np || !suspend_state) + continue; + + if (!of_property_read_u32(suspend_np, "regulator-mode", + &pval)) { + if (desc && desc->of_map_mode) { + ret = desc->of_map_mode(pval); + if (ret == -EINVAL) + pr_err("%s: invalid mode %u\n", + np->name, pval); + else + suspend_state->mode = ret; + } else { + pr_warn("%s: mapping for mode %d not defined\n", + np->name, pval); + } + } + + if (of_property_read_bool(suspend_np, + "regulator-on-in-suspend")) + suspend_state->enabled = true; + else if (of_property_read_bool(suspend_np, + "regulator-off-in-suspend")) + suspend_state->disabled = true; + + if (!of_property_read_u32(suspend_np, + "regulator-suspend-microvolt", &pval)) + suspend_state->uV = pval; + + of_node_put(suspend_np); + suspend_state = NULL; + suspend_np = NULL; + } } /** * of_get_regulator_init_data - extract regulator_init_data structure info * @dev: device requesting for regulator_init_data + * @node: regulator device node + * @desc: regulator description * * Populates regulator_init_data structure by extracting data from device * tree node, returns a pointer to the populated struture or NULL if memory * alloc fails. */ struct regulator_init_data *of_get_regulator_init_data(struct device *dev, - struct device_node *node) + struct device_node *node, + const struct regulator_desc *desc) { struct regulator_init_data *init_data; @@ -95,7 +169,7 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev, if (!init_data) return NULL; /* Out of memory? */ - of_get_regulation_constraints(node, &init_data); + of_get_regulation_constraints(node, &init_data, desc); return init_data; } EXPORT_SYMBOL_GPL(of_get_regulator_init_data); @@ -176,7 +250,8 @@ int of_regulator_match(struct device *dev, struct device_node *node, continue; match->init_data = - of_get_regulator_init_data(dev, child); + of_get_regulator_init_data(dev, child, + match->desc); if (!match->init_data) { dev_err(dev, "failed to parse DT for regulator %s\n", @@ -224,7 +299,7 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev, if (strcmp(desc->of_match, name)) continue; - init_data = of_get_regulator_init_data(dev, child); + init_data = of_get_regulator_init_data(dev, child, desc); if (!init_data) { dev_err(dev, "failed to parse DT for regulator %s\n", diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index d3f55eaea058..91f34ca3a9ac 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -149,7 +149,8 @@ static int pwm_regulator_probe(struct platform_device *pdev) return ret; } - config.init_data = of_get_regulator_init_data(&pdev->dev, np); + config.init_data = of_get_regulator_init_data(&pdev->dev, np, + &drvdata->desc); if (!config.init_data) return -ENOMEM; diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c index b55cd5b50ebe..183598b111f9 100644 --- a/drivers/regulator/qcom_rpm-regulator.c +++ b/drivers/regulator/qcom_rpm-regulator.c @@ -183,6 +183,13 @@ static const struct regulator_linear_range ftsmps_ranges[] = { REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000), }; +static const struct regulator_linear_range smb208_ranges[] = { + REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500), + REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500), + REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000), + REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000), +}; + static const struct regulator_linear_range ncp_ranges[] = { REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000), }; @@ -559,6 +566,16 @@ static const struct qcom_rpm_reg pm8921_switch = { .parts = &rpm8960_switch_parts, }; +static const struct qcom_rpm_reg smb208_smps = { + .desc.linear_ranges = smb208_ranges, + .desc.n_linear_ranges = ARRAY_SIZE(smb208_ranges), + .desc.n_voltages = 235, + .desc.ops = &uV_ops, + .parts = &rpm8960_smps_parts, + .supports_force_mode_auto = false, + .supports_force_mode_bypass = false, +}; + static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo }, { .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo }, @@ -578,6 +595,8 @@ static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps }, { .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp }, { .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch }, + + { .compatible = "qcom,rpm-smb208", .data = &smb208_smps }, { } }; MODULE_DEVICE_TABLE(of, rpm_of_match); @@ -643,10 +662,6 @@ static int rpm_reg_probe(struct platform_device *pdev) match = of_match_device(rpm_of_match, &pdev->dev); template = match->data; - initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); - if (!initdata) - return -EINVAL; - vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); if (!vreg) { dev_err(&pdev->dev, "failed to allocate vreg\n"); @@ -666,6 +681,11 @@ static int rpm_reg_probe(struct platform_device *pdev) return -ENODEV; } + initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, + &vreg->desc); + if (!initdata) + return -EINVAL; + key = "reg"; ret = of_property_read_u32(pdev->dev.of_node, key, &val); if (ret) { diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 196a5c8838c4..ea9d05eabd0a 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -36,6 +36,12 @@ #define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET) #define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET) +/* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */ +#define RK808_SLP_REG_OFFSET 1 + +/* Offset from XXX_EN_REG to SLEEP_SET_OFF_XXX */ +#define RK808_SLP_SET_OFF_REG_OFFSET 2 + static const int rk808_buck_config_regs[] = { RK808_BUCK1_CONFIG_REG, RK808_BUCK2_CONFIG_REG, @@ -91,6 +97,43 @@ static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) RK808_RAMP_RATE_MASK, ramp_value); } +int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) +{ + unsigned int reg; + int sel = regulator_map_voltage_linear_range(rdev, uv, uv); + + if (sel < 0) + return -EINVAL; + + reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET; + + return regmap_update_bits(rdev->regmap, reg, + rdev->desc->vsel_mask, + sel); +} + +int rk808_set_suspend_enable(struct regulator_dev *rdev) +{ + unsigned int reg; + + reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET; + + return regmap_update_bits(rdev->regmap, reg, + rdev->desc->enable_mask, + 0); +} + +int rk808_set_suspend_disable(struct regulator_dev *rdev) +{ + unsigned int reg; + + reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET; + + return regmap_update_bits(rdev->regmap, reg, + rdev->desc->enable_mask, + rdev->desc->enable_mask); +} + static struct regulator_ops rk808_buck1_2_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, @@ -100,6 +143,9 @@ static struct regulator_ops rk808_buck1_2_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .set_ramp_delay = rk808_set_ramp_delay, + .set_suspend_voltage = rk808_set_suspend_voltage, + .set_suspend_enable = rk808_set_suspend_enable, + .set_suspend_disable = rk808_set_suspend_disable, }; static struct regulator_ops rk808_reg_ops = { @@ -110,12 +156,17 @@ static struct regulator_ops rk808_reg_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_suspend_voltage = rk808_set_suspend_voltage, + .set_suspend_enable = rk808_set_suspend_enable, + .set_suspend_disable = rk808_set_suspend_disable, }; static struct regulator_ops rk808_switch_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = rk808_set_suspend_enable, + .set_suspend_disable = rk808_set_suspend_disable, }; static const struct regulator_desc rk808_reg[] = { diff --git a/drivers/regulator/rn5t618-regulator.c b/drivers/regulator/rn5t618-regulator.c index e58d79aeb393..b85ceb8ff911 100644 --- a/drivers/regulator/rn5t618-regulator.c +++ b/drivers/regulator/rn5t618-regulator.c @@ -31,6 +31,8 @@ static struct regulator_ops rn5t618_reg_ops = { #define REG(rid, ereg, emask, vreg, vmask, min, max, step) \ [RN5T618_##rid] = { \ .name = #rid, \ + .of_match = of_match_ptr(#rid), \ + .regulators_node = of_match_ptr("regulators"), \ .id = RN5T618_##rid, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ @@ -60,60 +62,15 @@ static struct regulator_desc rn5t618_regulators[] = { REG(LDORTC2, LDOEN2, BIT(5), LDORTC2DAC, 0x7f, 900000, 3500000, 25000), }; -static struct of_regulator_match rn5t618_matches[] = { - [RN5T618_DCDC1] = { .name = "DCDC1" }, - [RN5T618_DCDC2] = { .name = "DCDC2" }, - [RN5T618_DCDC3] = { .name = "DCDC3" }, - [RN5T618_LDO1] = { .name = "LDO1" }, - [RN5T618_LDO2] = { .name = "LDO2" }, - [RN5T618_LDO3] = { .name = "LDO3" }, - [RN5T618_LDO4] = { .name = "LDO4" }, - [RN5T618_LDO5] = { .name = "LDO5" }, - [RN5T618_LDORTC1] = { .name = "LDORTC1" }, - [RN5T618_LDORTC2] = { .name = "LDORTC2" }, -}; - -static int rn5t618_regulator_parse_dt(struct platform_device *pdev) -{ - struct device_node *np, *regulators; - int ret; - - np = of_node_get(pdev->dev.parent->of_node); - if (!np) - return 0; - - regulators = of_get_child_by_name(np, "regulators"); - if (!regulators) { - dev_err(&pdev->dev, "regulators node not found\n"); - return -EINVAL; - } - - ret = of_regulator_match(&pdev->dev, regulators, rn5t618_matches, - ARRAY_SIZE(rn5t618_matches)); - of_node_put(regulators); - if (ret < 0) { - dev_err(&pdev->dev, "error parsing regulator init data: %d\n", - ret); - } - - return 0; -} - static int rn5t618_regulator_probe(struct platform_device *pdev) { struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); struct regulator_config config = { }; struct regulator_dev *rdev; - int ret, i; - - ret = rn5t618_regulator_parse_dt(pdev); - if (ret) - return ret; + int i; for (i = 0; i < RN5T618_REG_NUM; i++) { - config.dev = &pdev->dev; - config.init_data = rn5t618_matches[i].init_data; - config.of_node = rn5t618_matches[i].of_node; + config.dev = pdev->dev.parent; config.regmap = rn5t618->regmap; rdev = devm_regulator_register(&pdev->dev, diff --git a/drivers/regulator/rt5033-regulator.c b/drivers/regulator/rt5033-regulator.c new file mode 100644 index 000000000000..870cc49438db --- /dev/null +++ b/drivers/regulator/rt5033-regulator.c @@ -0,0 +1,123 @@ +/* + * Regulator driver for the Richtek RT5033 + * + * Copyright (C) 2014 Samsung Electronics, Co., Ltd. + * Author: Beomho Seo <beomho.seo@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published bythe Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/rt5033.h> +#include <linux/mfd/rt5033-private.h> +#include <linux/regulator/of_regulator.h> + +static struct regulator_ops rt5033_safe_ldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops rt5033_buck_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_desc rt5033_supported_regulators[] = { + [RT5033_BUCK] = { + .name = "BUCK", + .id = RT5033_BUCK, + .ops = &rt5033_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM, + .min_uV = RT5033_REGULATOR_BUCK_VOLTAGE_MIN, + .uV_step = RT5033_REGULATOR_BUCK_VOLTAGE_STEP, + .enable_reg = RT5033_REG_CTRL, + .enable_mask = RT5033_CTRL_EN_BUCK_MASK, + .vsel_reg = RT5033_REG_BUCK_CTRL, + .vsel_mask = RT5033_BUCK_CTRL_MASK, + }, + [RT5033_LDO] = { + .name = "LDO", + .id = RT5033_LDO, + .ops = &rt5033_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM, + .min_uV = RT5033_REGULATOR_LDO_VOLTAGE_MIN, + .uV_step = RT5033_REGULATOR_LDO_VOLTAGE_STEP, + .enable_reg = RT5033_REG_CTRL, + .enable_mask = RT5033_CTRL_EN_LDO_MASK, + .vsel_reg = RT5033_REG_LDO_CTRL, + .vsel_mask = RT5033_LDO_CTRL_MASK, + }, + [RT5033_SAFE_LDO] = { + .name = "SAFE_LDO", + .id = RT5033_SAFE_LDO, + .ops = &rt5033_safe_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 1, + .min_uV = RT5033_REGULATOR_SAFE_LDO_VOLTAGE, + .enable_reg = RT5033_REG_CTRL, + .enable_mask = RT5033_CTRL_EN_SAFE_LDO_MASK, + }, +}; + +static int rt5033_regulator_probe(struct platform_device *pdev) +{ + struct rt5033_dev *rt5033 = dev_get_drvdata(pdev->dev.parent); + int ret, i; + struct regulator_config config = {}; + + config.dev = &pdev->dev; + config.driver_data = rt5033; + + for (i = 0; i < ARRAY_SIZE(rt5033_supported_regulators); i++) { + struct regulator_dev *regulator; + + config.regmap = rt5033->regmap; + + regulator = devm_regulator_register(&pdev->dev, + &rt5033_supported_regulators[i], &config); + if (IS_ERR(regulator)) { + ret = PTR_ERR(regulator); + dev_err(&pdev->dev, + "Regulator init failed %d: with error: %d\n", + i, ret); + return ret; + } + } + + return 0; +} + +static const struct platform_device_id rt5033_regulator_id[] = { + { "rt5033-regulator", }, + { } +}; +MODULE_DEVICE_TABLE(platform, rt5033_regulator_id); + +static struct platform_driver rt5033_regulator_driver = { + .driver = { + .name = "rt5033-regulator", + }, + .probe = rt5033_regulator_probe, + .id_table = rt5033_regulator_id, +}; +module_platform_driver(rt5033_regulator_driver); + +MODULE_DESCRIPTION("Richtek RT5033 Regulator driver"); +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index 7633b9bfbe6e..5db4e12a7e04 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -298,7 +298,7 @@ static struct regulator_ops s2mpa01_buck_ops = { .enable_mask = S2MPA01_ENABLE_MASK \ } -static struct regulator_desc regulators[] = { +static const struct regulator_desc regulators[] = { regulator_desc_ldo(1, STEP_25_MV), regulator_desc_ldo(2, STEP_50_MV), regulator_desc_ldo(3, STEP_50_MV), diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index adab82d5279f..b345cf51225a 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -30,6 +30,7 @@ #include <linux/of_gpio.h> #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mpu02.h> @@ -45,10 +46,10 @@ struct s2mps11_info { enum sec_device_type dev_type; /* - * One bit for each S2MPS14/S2MPU02 regulator whether the suspend mode - * was enabled. + * One bit for each S2MPS13/S2MPS14/S2MPU02 regulator whether + * the suspend mode was enabled. */ - unsigned long long s2mps14_suspend_state:35; + unsigned long long s2mps14_suspend_state:50; /* Array of size rdev_num with GPIO-s for external sleep control */ int *ext_control_gpio; @@ -369,12 +370,101 @@ static const struct regulator_desc s2mps11_regulators[] = { regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV), }; +static struct regulator_ops s2mps14_reg_ops; + +#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \ + .name = "LDO"#num, \ + .id = S2MPS13_LDO##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS13_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS14_LDO_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +#define regulator_desc_s2mps13_buck(num, min, step, min_sel) { \ + .name = "BUCK"#num, \ + .id = S2MPS13_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS13_REG_B1OUT + (num - 1) * 2, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +static const struct regulator_desc s2mps13_regulators[] = { + regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(3, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(4, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(5, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(6, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(7, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(8, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(9, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(10, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(11, MIN_800_MV, STEP_25_MV, 0x10), + regulator_desc_s2mps13_ldo(12, MIN_800_MV, STEP_25_MV, 0x10), + regulator_desc_s2mps13_ldo(13, MIN_800_MV, STEP_25_MV, 0x10), + regulator_desc_s2mps13_ldo(14, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(15, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(16, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(17, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(18, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(19, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(20, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(21, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(22, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(23, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(24, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(25, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(26, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(27, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(28, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(29, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(30, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(31, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(32, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(33, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(34, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(35, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(36, MIN_800_MV, STEP_12_5_MV, 0x00), + regulator_desc_s2mps13_ldo(37, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(38, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_ldo(39, MIN_1000_MV, STEP_25_MV, 0x08), + regulator_desc_s2mps13_ldo(40, MIN_1400_MV, STEP_50_MV, 0x0C), + regulator_desc_s2mps13_buck(1, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(2, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(3, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(7, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck(8, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck(9, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck(10, MIN_500_MV, STEP_6_25_MV, 0x10), +}; + static int s2mps14_regulator_enable(struct regulator_dev *rdev) { struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); unsigned int val; switch (s2mps11->dev_type) { + case S2MPS13X: case S2MPS14X: if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) val = S2MPS14_ENABLE_SUSPEND; @@ -406,6 +496,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev) /* Below LDO should be always on or does not support suspend mode. */ switch (s2mps11->dev_type) { + case S2MPS13X: case S2MPS14X: switch (rdev_id) { case S2MPS14_LDO3: @@ -831,6 +922,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) s2mps11->rdev_num = ARRAY_SIZE(s2mps11_regulators); regulators = s2mps11_regulators; break; + case S2MPS13X: + s2mps11->rdev_num = ARRAY_SIZE(s2mps13_regulators); + regulators = s2mps13_regulators; + break; case S2MPS14X: s2mps11->rdev_num = ARRAY_SIZE(s2mps14_regulators); regulators = s2mps14_regulators; @@ -845,7 +940,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; }; - s2mps11->ext_control_gpio = devm_kzalloc(&pdev->dev, + s2mps11->ext_control_gpio = devm_kmalloc(&pdev->dev, sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num, GFP_KERNEL); if (!s2mps11->ext_control_gpio) @@ -886,6 +981,7 @@ common_reg: config.regmap = iodev->regmap_pmic; config.driver_data = s2mps11; config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + config.ena_gpio_initialized = true; for (i = 0; i < s2mps11->rdev_num; i++) { struct regulator_dev *regulator; @@ -927,6 +1023,7 @@ out: static const struct platform_device_id s2mps11_pmic_id[] = { { "s2mps11-pmic", S2MPS11X}, + { "s2mps13-pmic", S2MPS13X}, { "s2mps14-pmic", S2MPS14X}, { "s2mpu02-pmic", S2MPU02}, { }, diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 0ab5cbeeb797..dc1328c0c71c 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -581,7 +581,8 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, rdata->id = i; rdata->initdata = of_get_regulator_init_data( - &pdev->dev, reg_np); + &pdev->dev, reg_np, + ®ulators[i]); rdata->reg_node = reg_np; rdata++; rmode->id = i; @@ -950,6 +951,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.of_node = pdata->regulators[i].reg_node; config.ena_gpio = -EINVAL; config.ena_gpio_flags = 0; + config.ena_gpio_initialized = true; if (gpio_is_valid(pdata->regulators[i].ext_control_gpio)) s5m8767_regulator_config_ext_control(s5m8767, &pdata->regulators[i], &config); diff --git a/drivers/regulator/sky81452-regulator.c b/drivers/regulator/sky81452-regulator.c index 97aff0ccd65f..647860611916 100644 --- a/drivers/regulator/sky81452-regulator.c +++ b/drivers/regulator/sky81452-regulator.c @@ -5,9 +5,8 @@ * Author : Gyungoh Yoo <jack.yoo@skyworksinc.com> * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. + * under the terms of the GNU General Public License version 2 + * 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 @@ -52,6 +51,8 @@ static const struct regulator_linear_range sky81452_reg_ranges[] = { static const struct regulator_desc sky81452_reg = { .name = "LOUT", + .of_match = of_match_ptr("lout"), + .regulators_node = of_match_ptr("regulator"), .ops = &sky81452_reg_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, @@ -64,30 +65,6 @@ static const struct regulator_desc sky81452_reg = { .enable_mask = SKY81452_LEN, }; -#ifdef CONFIG_OF -static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev) -{ - struct regulator_init_data *init_data; - struct device_node *np; - - np = of_get_child_by_name(dev->parent->of_node, "regulator"); - if (unlikely(!np)) { - dev_err(dev, "regulator node not found"); - return NULL; - } - - init_data = of_get_regulator_init_data(dev, np); - - of_node_put(np); - return init_data; -} -#else -static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev) -{ - return ERR_PTR(-EINVAL); -} -#endif - static int sky81452_reg_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -95,20 +72,16 @@ static int sky81452_reg_probe(struct platform_device *pdev) struct regulator_config config = { }; struct regulator_dev *rdev; - if (!init_data) { - init_data = sky81452_reg_parse_dt(dev); - if (IS_ERR(init_data)) - return PTR_ERR(init_data); - } - - config.dev = dev; + config.dev = dev->parent; config.init_data = init_data; config.of_node = dev->of_node; config.regmap = dev_get_drvdata(dev->parent); rdev = devm_regulator_register(dev, &sky81452_reg, &config); - if (IS_ERR(rdev)) + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(rdev)); return PTR_ERR(rdev); + } platform_set_drvdata(pdev, rdev); @@ -126,5 +99,4 @@ module_platform_driver(sky81452_reg_driver); MODULE_DESCRIPTION("Skyworks SKY81452 Regulator driver"); MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index a7e152696a02..b4f1696456a7 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -72,7 +72,8 @@ static int stw481x_vmmc_regulator_probe(struct platform_device *pdev) config.regmap = stw481x->map; config.of_node = pdev->dev.of_node; config.init_data = of_get_regulator_init_data(&pdev->dev, - pdev->dev.of_node); + pdev->dev.of_node, + &vmmc_regulator); stw481x->vmmc_regulator = devm_regulator_register(&pdev->dev, &vmmc_regulator, &config); diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index a2dabb575b97..1ef5aba96f17 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -837,7 +837,8 @@ skip_opt: return -EINVAL; } - initdata = of_get_regulator_init_data(dev, pdev->dev.of_node); + initdata = of_get_regulator_init_data(dev, pdev->dev.of_node, + &abb->rdesc); if (!initdata) { dev_err(dev, "%s: Unable to alloc regulator init data\n", __func__); diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index f31f22e3e1bd..c213e37eb69e 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -221,7 +221,8 @@ static const struct of_device_id tps51632_of_match[] = { MODULE_DEVICE_TABLE(of, tps51632_of_match); static struct tps51632_regulator_platform_data * - of_get_tps51632_platform_data(struct device *dev) + of_get_tps51632_platform_data(struct device *dev, + const struct regulator_desc *desc) { struct tps51632_regulator_platform_data *pdata; struct device_node *np = dev->of_node; @@ -230,7 +231,8 @@ static struct tps51632_regulator_platform_data * if (!pdata) return NULL; - pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); + pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node, + desc); if (!pdata->reg_init_data) { dev_err(dev, "Not able to get OF regulator init data\n"); return NULL; @@ -248,7 +250,8 @@ static struct tps51632_regulator_platform_data * } #else static struct tps51632_regulator_platform_data * - of_get_tps51632_platform_data(struct device *dev) + of_get_tps51632_platform_data(struct device *dev, + const struct regulator_desc *desc) { return NULL; } @@ -273,9 +276,25 @@ static int tps51632_probe(struct i2c_client *client, } } + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + tps->dev = &client->dev; + tps->desc.name = client->name; + tps->desc.id = 0; + tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY; + tps->desc.min_uV = TPS51632_MIN_VOLTAGE; + tps->desc.uV_step = TPS51632_VOLTAGE_STEP_10mV; + tps->desc.linear_min_sel = TPS51632_MIN_VSEL; + tps->desc.n_voltages = TPS51632_MAX_VSEL + 1; + tps->desc.ops = &tps51632_dcdc_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + pdata = dev_get_platdata(&client->dev); if (!pdata && client->dev.of_node) - pdata = of_get_tps51632_platform_data(&client->dev); + pdata = of_get_tps51632_platform_data(&client->dev, &tps->desc); if (!pdata) { dev_err(&client->dev, "No Platform data\n"); return -EINVAL; @@ -296,22 +315,6 @@ static int tps51632_probe(struct i2c_client *client, } } - tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); - if (!tps) - return -ENOMEM; - - tps->dev = &client->dev; - tps->desc.name = client->name; - tps->desc.id = 0; - tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY; - tps->desc.min_uV = TPS51632_MIN_VOLTAGE; - tps->desc.uV_step = TPS51632_VOLTAGE_STEP_10mV; - tps->desc.linear_min_sel = TPS51632_MIN_VSEL; - tps->desc.n_voltages = TPS51632_MAX_VSEL + 1; - tps->desc.ops = &tps51632_dcdc_ops; - tps->desc.type = REGULATOR_VOLTAGE; - tps->desc.owner = THIS_MODULE; - if (pdata->enable_pwm_dvfs) tps->desc.vsel_reg = TPS51632_VOLTAGE_BASE_REG; else diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index a1672044e519..a1fd626c6c96 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -293,7 +293,8 @@ static const struct regmap_config tps62360_regmap_config = { }; static struct tps62360_regulator_platform_data * - of_get_tps62360_platform_data(struct device *dev) + of_get_tps62360_platform_data(struct device *dev, + const struct regulator_desc *desc) { struct tps62360_regulator_platform_data *pdata; struct device_node *np = dev->of_node; @@ -302,7 +303,8 @@ static struct tps62360_regulator_platform_data * if (!pdata) return NULL; - pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); + pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node, + desc); if (!pdata->reg_init_data) { dev_err(dev, "Not able to get OF regulator init data\n"); return NULL; @@ -350,6 +352,17 @@ static int tps62360_probe(struct i2c_client *client, pdata = dev_get_platdata(&client->dev); + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + tps->desc.name = client->name; + tps->desc.id = 0; + tps->desc.ops = &tps62360_dcdc_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + tps->desc.uV_step = 10000; + if (client->dev.of_node) { const struct of_device_id *match; match = of_match_device(of_match_ptr(tps62360_of_match), @@ -360,7 +373,8 @@ static int tps62360_probe(struct i2c_client *client, } chip_id = (int)(long)match->data; if (!pdata) - pdata = of_get_tps62360_platform_data(&client->dev); + pdata = of_get_tps62360_platform_data(&client->dev, + &tps->desc); } else if (id) { chip_id = id->driver_data; } else { @@ -374,10 +388,6 @@ static int tps62360_probe(struct i2c_client *client, return -EIO; } - tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); - if (!tps) - return -ENOMEM; - tps->en_discharge = pdata->en_discharge; tps->en_internal_pulldn = pdata->en_internal_pulldn; tps->vsel0_gpio = pdata->vsel0_gpio; @@ -401,13 +411,6 @@ static int tps62360_probe(struct i2c_client *client, return -ENODEV; } - tps->desc.name = client->name; - tps->desc.id = 0; - tps->desc.ops = &tps62360_dcdc_ops; - tps->desc.type = REGULATOR_VOLTAGE; - tps->desc.owner = THIS_MODULE; - tps->desc.uV_step = 10000; - tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config); if (IS_ERR(tps->regmap)) { ret = PTR_ERR(tps->regmap); diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index d5df1e9ad1da..f1df4423d361 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -312,7 +312,11 @@ static void tps65090_configure_regulator_config( gpio_flag = GPIOF_OUT_INIT_HIGH; config->ena_gpio = tps_pdata->gpio; + config->ena_gpio_initialized = true; config->ena_gpio_flags = gpio_flag; + } else { + config->ena_gpio = -EINVAL; + config->ena_gpio_initialized = false; } } diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index f0a40281b9c1..263cc85d6202 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -231,7 +231,8 @@ static int tps65218_regulator_probe(struct platform_device *pdev) template = match->data; id = template->id; - init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, + ®ulators[id]); platform_set_drvdata(pdev, tps); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 0b4f8660fdb4..dd727bca1983 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -1104,7 +1104,8 @@ static int twlreg_probe(struct platform_device *pdev) template = match->data; id = template->desc.id; initdata = of_get_regulator_init_data(&pdev->dev, - pdev->dev.of_node); + pdev->dev.of_node, + &template->desc); drvdata = NULL; } else { id = pdev->id; diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c index 02e7267ccf92..5e7c789023a9 100644 --- a/drivers/regulator/vexpress.c +++ b/drivers/regulator/vexpress.c @@ -74,7 +74,8 @@ static int vexpress_regulator_probe(struct platform_device *pdev) reg->desc.owner = THIS_MODULE; reg->desc.continuous_voltage_range = true; - init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, + ®->desc); if (!init_data) return -EINVAL; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index c24346db8a71..88f5064e412b 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -145,10 +145,12 @@ static int wm8994_ldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm8994->regmap; config.init_data = &ldo->init_data; - if (pdata) + if (pdata) { config.ena_gpio = pdata->ldo[id].enable; - else if (wm8994->dev->of_node) + } else if (wm8994->dev->of_node) { config.ena_gpio = wm8994->pdata.ldo[id].enable; + config.ena_gpio_initialized = true; + } /* Use default constraints if none set up */ if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) { diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 60fed3d7820b..157d421f755b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o +obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ diff --git a/drivers/reset/core.c b/drivers/reset/core.c index baeaf82d40d9..7955e00d04d4 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -126,6 +126,21 @@ int reset_control_deassert(struct reset_control *rstc) EXPORT_SYMBOL_GPL(reset_control_deassert); /** + * reset_control_status - returns a negative errno if not supported, a + * positive value if the reset line is asserted, or zero if the reset + * line is not asserted. + * @rstc: reset controller + */ +int reset_control_status(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->status) + return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_status); + +/** * of_reset_control_get - Lookup and obtain a reference to a reset controller. * @node: device to be reset by the controller * @id: reset line name diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c new file mode 100644 index 000000000000..f8b48a13cf0b --- /dev/null +++ b/drivers/reset/reset-berlin.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Tenart <antoine.tenart@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define BERLIN_MAX_RESETS 32 + +#define to_berlin_reset_priv(p) \ + container_of((p), struct berlin_reset_priv, rcdev) + +struct berlin_reset_priv { + void __iomem *base; + unsigned int size; + struct reset_controller_dev rcdev; +}; + +static int berlin_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev); + int offset = id >> 8; + int mask = BIT(id & 0x1f); + + writel(mask, priv->base + offset); + + /* let the reset be effective */ + udelay(10); + + return 0; +} + +static struct reset_control_ops berlin_reset_ops = { + .reset = berlin_reset_reset, +}; + +static int berlin_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev); + unsigned offset, bit; + + if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) + return -EINVAL; + + offset = reset_spec->args[0]; + bit = reset_spec->args[1]; + + if (offset >= priv->size) + return -EINVAL; + + if (bit >= BERLIN_MAX_RESETS) + return -EINVAL; + + return (offset << 8) | bit; +} + +static int __berlin_reset_init(struct device_node *np) +{ + struct berlin_reset_priv *priv; + struct resource res; + resource_size_t size; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + goto err; + + size = resource_size(&res); + priv->base = ioremap(res.start, size); + if (!priv->base) { + ret = -ENOMEM; + goto err; + } + priv->size = size; + + priv->rcdev.owner = THIS_MODULE; + priv->rcdev.ops = &berlin_reset_ops; + priv->rcdev.of_node = np; + priv->rcdev.of_reset_n_cells = 2; + priv->rcdev.of_xlate = berlin_reset_xlate; + + reset_controller_register(&priv->rcdev); + + return 0; + +err: + kfree(priv); + return ret; +} + +static const struct of_device_id berlin_reset_of_match[] __initconst = { + { .compatible = "marvell,berlin2-chip-ctrl" }, + { .compatible = "marvell,berlin2cd-chip-ctrl" }, + { .compatible = "marvell,berlin2q-chip-ctrl" }, + { }, +}; + +static int __init berlin_reset_init(void) +{ + struct device_node *np; + int ret; + + for_each_matching_node(np, berlin_reset_of_match) { + ret = __berlin_reset_init(np); + if (ret) + return ret; + } + + return 0; +} +arch_initcall(berlin_reset_init); diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 79c32ca84ef1..40582089474a 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -76,9 +76,24 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, return 0; } +static int socfpga_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct socfpga_reset_data *data = container_of(rcdev, + struct socfpga_reset_data, rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + u32 reg; + + reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); + + return !(reg & BIT(offset)); +} + static struct reset_control_ops socfpga_reset_ops = { .assert = socfpga_reset_assert, .deassert = socfpga_reset_deassert, + .status = socfpga_reset_status, }; static int socfpga_reset_probe(struct platform_device *pdev) diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig index 88d2d0316613..f8c15a37fb35 100644 --- a/drivers/reset/sti/Kconfig +++ b/drivers/reset/sti/Kconfig @@ -12,4 +12,8 @@ config STIH416_RESET bool select STI_RESET_SYSCFG +config STIH407_RESET + bool + select STI_RESET_SYSCFG + endif diff --git a/drivers/reset/sti/Makefile b/drivers/reset/sti/Makefile index be1c97647871..dc85dfbe56a9 100644 --- a/drivers/reset/sti/Makefile +++ b/drivers/reset/sti/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_STI_RESET_SYSCFG) += reset-syscfg.o obj-$(CONFIG_STIH415_RESET) += reset-stih415.o obj-$(CONFIG_STIH416_RESET) += reset-stih416.o +obj-$(CONFIG_STIH407_RESET) += reset-stih407.o diff --git a/drivers/reset/sti/reset-stih407.c b/drivers/reset/sti/reset-stih407.c new file mode 100644 index 000000000000..d83db5d72d08 --- /dev/null +++ b/drivers/reset/sti/reset-stih407.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 STMicroelectronics (R&D) Limited + * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <dt-bindings/reset-controller/stih407-resets.h> +#include "reset-syscfg.h" + +/* STiH407 Peripheral powerdown definitions. */ +static const char stih407_core[] = "st,stih407-core-syscfg"; +static const char stih407_sbc_reg[] = "st,stih407-sbc-reg-syscfg"; +static const char stih407_lpm[] = "st,stih407-lpm-syscfg"; + +#define STIH407_PDN_0(_bit) \ + _SYSCFG_RST_CH(stih407_core, SYSCFG_5000, _bit, SYSSTAT_5500, _bit) +#define STIH407_PDN_1(_bit) \ + _SYSCFG_RST_CH(stih407_core, SYSCFG_5001, _bit, SYSSTAT_5501, _bit) +#define STIH407_PDN_ETH(_bit, _stat) \ + _SYSCFG_RST_CH(stih407_sbc_reg, SYSCFG_4032, _bit, SYSSTAT_4520, _stat) + +/* Powerdown requests control 0 */ +#define SYSCFG_5000 0x0 +#define SYSSTAT_5500 0x7d0 +/* Powerdown requests control 1 (High Speed Links) */ +#define SYSCFG_5001 0x4 +#define SYSSTAT_5501 0x7d4 + +/* Ethernet powerdown/status/reset */ +#define SYSCFG_4032 0x80 +#define SYSSTAT_4520 0x820 +#define SYSCFG_4002 0x8 + +static const struct syscfg_reset_channel_data stih407_powerdowns[] = { + [STIH407_EMISS_POWERDOWN] = STIH407_PDN_0(1), + [STIH407_NAND_POWERDOWN] = STIH407_PDN_0(0), + [STIH407_USB3_POWERDOWN] = STIH407_PDN_1(6), + [STIH407_USB2_PORT1_POWERDOWN] = STIH407_PDN_1(5), + [STIH407_USB2_PORT0_POWERDOWN] = STIH407_PDN_1(4), + [STIH407_PCIE1_POWERDOWN] = STIH407_PDN_1(3), + [STIH407_PCIE0_POWERDOWN] = STIH407_PDN_1(2), + [STIH407_SATA1_POWERDOWN] = STIH407_PDN_1(1), + [STIH407_SATA0_POWERDOWN] = STIH407_PDN_1(0), + [STIH407_ETH1_POWERDOWN] = STIH407_PDN_ETH(0, 2), +}; + +/* Reset Generator control 0/1 */ +#define SYSCFG_5131 0x20c +#define SYSCFG_5132 0x210 + +#define LPM_SYSCFG_1 0x4 /* Softreset IRB & SBC UART */ + +#define STIH407_SRST_CORE(_reg, _bit) \ + _SYSCFG_RST_CH_NO_ACK(stih407_core, _reg, _bit) + +#define STIH407_SRST_SBC(_reg, _bit) \ + _SYSCFG_RST_CH_NO_ACK(stih407_sbc_reg, _reg, _bit) + +#define STIH407_SRST_LPM(_reg, _bit) \ + _SYSCFG_RST_CH_NO_ACK(stih407_lpm, _reg, _bit) + +static const struct syscfg_reset_channel_data stih407_softresets[] = { + [STIH407_ETH1_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 4), + [STIH407_MMC1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 3), + [STIH407_USB2_PORT0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 28), + [STIH407_USB2_PORT1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 29), + [STIH407_PICOPHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 30), + [STIH407_IRB_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 6), + [STIH407_PCIE0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 6), + [STIH407_PCIE1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 15), + [STIH407_SATA0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 7), + [STIH407_SATA1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 16), + [STIH407_MIPHY0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 4), + [STIH407_MIPHY1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 13), + [STIH407_MIPHY2_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 22), + [STIH407_SATA0_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 5), + [STIH407_SATA1_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 14), + [STIH407_DELTA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 3), + [STIH407_BLITTER_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 10), + [STIH407_HDTVOUT_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 11), + [STIH407_HDQVDP_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 12), + [STIH407_VDP_AUX_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 14), + [STIH407_COMPO_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 15), + [STIH407_HDMI_TX_PHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 21), + [STIH407_JPEG_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 23), + [STIH407_VP8_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 24), + [STIH407_GPU_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 30), + [STIH407_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 0), + [STIH407_ERAM_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 1), + [STIH407_LPM_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 2), + [STIH407_KEYSCAN_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 8), +}; + +/* PicoPHY reset/control */ +#define SYSCFG_5061 0x0f4 + +static const struct syscfg_reset_channel_data stih407_picophyresets[] = { + [STIH407_PICOPHY0_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 5), + [STIH407_PICOPHY1_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 6), + [STIH407_PICOPHY2_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 7), +}; + +static const struct syscfg_reset_controller_data stih407_powerdown_controller = { + .wait_for_ack = true, + .nr_channels = ARRAY_SIZE(stih407_powerdowns), + .channels = stih407_powerdowns, +}; + +static const struct syscfg_reset_controller_data stih407_softreset_controller = { + .wait_for_ack = false, + .active_low = true, + .nr_channels = ARRAY_SIZE(stih407_softresets), + .channels = stih407_softresets, +}; + +static const struct syscfg_reset_controller_data stih407_picophyreset_controller = { + .wait_for_ack = false, + .nr_channels = ARRAY_SIZE(stih407_picophyresets), + .channels = stih407_picophyresets, +}; + +static struct of_device_id stih407_reset_match[] = { + { + .compatible = "st,stih407-powerdown", + .data = &stih407_powerdown_controller, + }, + { + .compatible = "st,stih407-softreset", + .data = &stih407_softreset_controller, + }, + { + .compatible = "st,stih407-picophyreset", + .data = &stih407_picophyreset_controller, + }, + { /* sentinel */ }, +}; + +static struct platform_driver stih407_reset_driver = { + .probe = syscfg_reset_probe, + .driver = { + .name = "reset-stih407", + .of_match_table = stih407_reset_match, + }, +}; + +static int __init stih407_reset_init(void) +{ + return platform_driver_register(&stih407_reset_driver); +} + +arch_initcall(stih407_reset_init); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6dd12ddbabc6..b682651b5307 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1109,36 +1109,42 @@ config RTC_DRV_AT91RM9200 this is powered by the backup power supply. config RTC_DRV_AT91SAM9 - tristate "AT91SAM9x/AT91CAP9 RTT as RTC" - depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40) + tristate "AT91SAM9 RTT as RTC" + depends on ARCH_AT91 + select MFD_SYSCON help - RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT - (Real Time Timer). These timers are powered by the backup power - supply (such as a small coin cell battery), but do not need to - be used as RTCs. - - (On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the - dedicated RTC module and leave the RTT available for other uses.) + Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which + can be used as an RTC thanks to the backup power supply (e.g. a + small coin cell battery) which keeps this block and the GPBR + (General Purpose Backup Registers) block powered when the device + is shutdown. + Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd + probably want to use the real RTC block instead of the "RTT as an + RTC" driver. config RTC_DRV_AT91SAM9_RTT int range 0 1 default 0 - prompt "RTT module Number" if ARCH_AT91SAM9263 depends on RTC_DRV_AT91SAM9 help + This option is only relevant for legacy board support and + won't be used when booting a DT board. + More than one RTT module is available. You can choose which one will be used as an RTC. The default of zero is normally OK to use, though some systems use that for non-RTC purposes. config RTC_DRV_AT91SAM9_GPBR int - range 0 3 if !ARCH_AT91SAM9263 - range 0 15 if ARCH_AT91SAM9263 + range 0 3 default 0 prompt "Backup Register Number" depends on RTC_DRV_AT91SAM9 help + This option is only relevant for legacy board support and + won't be used when booting a DT board. + The RTC driver needs to use one of the General Purpose Backup Registers (GPBRs) as well as the RTT. You can choose which one will be used. The default of zero is normally OK to use, but diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 596374304532..abac38abd38e 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -21,10 +21,9 @@ #include <linux/slab.h> #include <linux/platform_data/atmel.h> #include <linux/io.h> - -#include <mach/at91_rtt.h> -#include <mach/cpu.h> -#include <mach/hardware.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/clk.h> /* * This driver uses two configurable hardware resources that live in the @@ -47,6 +46,22 @@ * registers available, likewise usable for more than "RTC" support. */ +#define AT91_RTT_MR 0x00 /* Real-time Mode Register */ +#define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */ +#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */ +#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */ +#define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */ + +#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */ +#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */ + +#define AT91_RTT_VR 0x08 /* Real-time Value Register */ +#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */ + +#define AT91_RTT_SR 0x0c /* Real-time Status Register */ +#define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */ +#define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */ + /* * We store ALARM_DISABLED in ALMV to record that no alarm is set. * It's also the reset value for that field. @@ -58,19 +73,30 @@ struct sam9_rtc { void __iomem *rtt; struct rtc_device *rtcdev; u32 imr; - void __iomem *gpbr; + struct regmap *gpbr; + unsigned int gpbr_offset; int irq; + struct clk *sclk; }; #define rtt_readl(rtc, field) \ - __raw_readl((rtc)->rtt + AT91_RTT_ ## field) + readl((rtc)->rtt + AT91_RTT_ ## field) #define rtt_writel(rtc, field, val) \ - __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) + writel((val), (rtc)->rtt + AT91_RTT_ ## field) + +static inline unsigned int gpbr_readl(struct sam9_rtc *rtc) +{ + unsigned int val; + + regmap_read(rtc->gpbr, rtc->gpbr_offset, &val); -#define gpbr_readl(rtc) \ - __raw_readl((rtc)->gpbr) -#define gpbr_writel(rtc, val) \ - __raw_writel((val), (rtc)->gpbr) + return val; +} + +static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val) +{ + regmap_write(rtc->gpbr, rtc->gpbr_offset, val); +} /* * Read current time and date in RTC @@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = { .alarm_irq_enable = at91_rtc_alarm_irq_enable, }; +static struct regmap_config gpbr_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + /* * Initialize and install RTC driver */ static int at91_rtc_probe(struct platform_device *pdev) { - struct resource *r, *r_gpbr; + struct resource *r; struct sam9_rtc *rtc; int ret, irq; u32 mr; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r || !r_gpbr) { - dev_err(&pdev->dev, "need 2 ressources\n"); - return -ENODEV; - } + unsigned int sclk_rate; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, rtc); - rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (!rtc->rtt) { - dev_err(&pdev->dev, "failed to map registers, aborting.\n"); - return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtc->rtt = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(rtc->rtt)) + return PTR_ERR(rtc->rtt); + + if (!pdev->dev.of_node) { + /* + * TODO: Remove this code chunk when removing non DT board + * support. Remember to remove the gpbr_regmap_config + * variable too. + */ + void __iomem *gpbr; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + gpbr = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(gpbr)) + return PTR_ERR(gpbr); + + rtc->gpbr = regmap_init_mmio(NULL, gpbr, + &gpbr_regmap_config); + } else { + struct of_phandle_args args; + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "atmel,rtt-rtc-time-reg", 1, 0, + &args); + if (ret) + return ret; + + rtc->gpbr = syscon_node_to_regmap(args.np); + rtc->gpbr_offset = args.args[0]; } - rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start, - resource_size(r_gpbr)); - if (!rtc->gpbr) { - dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); + if (IS_ERR(rtc->gpbr)) { + dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); return -ENOMEM; } + rtc->sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rtc->sclk)) + return PTR_ERR(rtc->sclk); + + sclk_rate = clk_get_rate(rtc->sclk); + if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) { + dev_err(&pdev->dev, "Invalid slow clock rate\n"); + return -EINVAL; + } + + ret = clk_prepare_enable(rtc->sclk); + if (ret) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return ret; + } + mr = rtt_readl(rtc, MR); /* unless RTT is counting at 1 Hz, re-initialize it */ - if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { - mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); + if ((mr & AT91_RTT_RTPRES) != sclk_rate) { + mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES); gpbr_writel(rtc, 0); } @@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev) /* disable all interrupts */ rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + if (!IS_ERR(rtc->sclk)) + clk_disable_unprepare(rtc->sclk); + return 0; } @@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); +#ifdef CONFIG_OF +static const struct of_device_id at91_rtc_dt_ids[] = { + { .compatible = "atmel,at91sam9260-rtt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); +#endif + static struct platform_driver at91_rtc_driver = { .probe = at91_rtc_probe, .remove = at91_rtc_remove, @@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = { .name = "rtc-at91sam9", .owner = THIS_MODULE, .pm = &at91_rtc_pm_ops, + .of_match_table = of_match_ptr(at91_rtc_dt_ids), }, }; diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8004b071a9f2..01a73395a017 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -353,9 +353,11 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) adapter->ccw_device = ccw_device; INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); - INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); + INIT_DELAYED_WORK(&adapter->scan_work, zfcp_fc_scan_ports); INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update); + adapter->next_port_scan = jiffies; + if (zfcp_qdio_setup(adapter)) goto failed; @@ -420,7 +422,7 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter) { struct ccw_device *cdev = adapter->ccw_device; - cancel_work_sync(&adapter->scan_work); + cancel_delayed_work_sync(&adapter->scan_work); cancel_work_sync(&adapter->stat_work); cancel_work_sync(&adapter->ns_up_work); zfcp_destroy_adapter_work_queue(adapter); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index f9879d400d0e..54c7b48fdb46 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -56,8 +56,22 @@ static int zfcp_ccw_activate(struct ccw_device *cdev, int clear, char *tag) zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, tag); + + /* + * We want to scan ports here, with some random backoff and without + * rate limit. Recovery has already scheduled a port scan for us, + * but with both random delay and rate limit. Nevertheless we get + * what we want here by flushing the scheduled work after sleeping + * an equivalent random time. + * Let the port scan random delay elapse first. If recovery finishes + * up to that point in time, that would be perfect for both recovery + * and port scan. If not, i.e. recovery takes ages, there was no + * point in waiting a random delay on top of the time consumed by + * recovery. + */ + msleep(zfcp_fc_port_scan_backoff()); zfcp_erp_wait(adapter); - flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + flush_delayed_work(&adapter->scan_work); zfcp_ccw_adapter_put(adapter); @@ -162,11 +176,19 @@ static int zfcp_ccw_set_online(struct ccw_device *cdev) adapter->req_no = 0; zfcp_ccw_activate(cdev, 0, "ccsonl1"); - /* scan for remote ports - either at the end of any successful adapter recovery - or only after the adapter recovery for setting a device online */ + + /* + * We want to scan ports here, always, with some random delay and + * without rate limit - basically what zfcp_ccw_activate() has + * achieved for us. Not quite! That port scan depended on + * !no_auto_port_rescan. So let's cover the no_auto_port_rescan + * case here to make sure a port scan is done unconditionally. + * Since zfcp_ccw_activate() has waited the desired random time, + * we can immediately schedule and flush a port scan for the + * remaining cases. + */ zfcp_fc_inverse_conditional_port_scan(adapter); - flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + flush_delayed_work(&adapter->scan_work); zfcp_ccw_adapter_put(adapter); return 0; } diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index d91173f326c5..b8e853e53546 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -186,12 +186,13 @@ struct zfcp_adapter { struct fc_host_statistics *fc_stats; struct fsf_qtcb_bottom_port *stats_reset_data; unsigned long stats_reset; - struct work_struct scan_work; + struct delayed_work scan_work; struct work_struct ns_up_work; struct service_level service_level; struct workqueue_struct *work_queue; struct device_dma_parameters dma_parms; struct zfcp_fc_events events; + unsigned long next_port_scan; }; struct zfcp_port { diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index c82fe65c4128..2c5d4567d1da 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -821,11 +821,6 @@ static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act) return ZFCP_ERP_CONTINUES; } -static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port) -{ - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status); -} - static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action) { struct zfcp_port *port = erp_action->port; @@ -833,7 +828,6 @@ static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action) switch (erp_action->step) { case ZFCP_ERP_STEP_UNINITIALIZED: - zfcp_erp_port_strategy_clearstati(port); if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) && (status & ZFCP_STATUS_COMMON_OPEN)) return zfcp_erp_port_forced_strategy_close(erp_action); @@ -933,7 +927,6 @@ static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action) switch (erp_action->step) { case ZFCP_ERP_STEP_UNINITIALIZED: - zfcp_erp_port_strategy_clearstati(port); if (p_status & ZFCP_STATUS_COMMON_OPEN) return zfcp_erp_port_strategy_close(erp_action); break; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index a9c570a09b85..5b500652572b 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -85,6 +85,7 @@ extern void zfcp_fc_gs_destroy(struct zfcp_adapter *); extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *); extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *); extern void zfcp_fc_sym_name_update(struct work_struct *); +extern unsigned int zfcp_fc_port_scan_backoff(void); extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *); extern void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index ca28e1c66115..25d49f32ca63 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -12,6 +12,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/utsname.h> +#include <linux/random.h> #include <scsi/fc/fc_els.h> #include <scsi/libfc.h> #include "zfcp_ext.h" @@ -31,12 +32,54 @@ module_param_named(no_auto_port_rescan, no_auto_port_rescan, bool, 0600); MODULE_PARM_DESC(no_auto_port_rescan, "no automatic port_rescan (default off)"); +static unsigned int port_scan_backoff = 500; +module_param(port_scan_backoff, uint, 0600); +MODULE_PARM_DESC(port_scan_backoff, + "upper limit of port scan random backoff in msecs (default 500)"); + +static unsigned int port_scan_ratelimit = 60000; +module_param(port_scan_ratelimit, uint, 0600); +MODULE_PARM_DESC(port_scan_ratelimit, + "minimum interval between port scans in msecs (default 60000)"); + +unsigned int zfcp_fc_port_scan_backoff(void) +{ + if (!port_scan_backoff) + return 0; + return get_random_int() % port_scan_backoff; +} + +static void zfcp_fc_port_scan_time(struct zfcp_adapter *adapter) +{ + unsigned long interval = msecs_to_jiffies(port_scan_ratelimit); + unsigned long backoff = msecs_to_jiffies(zfcp_fc_port_scan_backoff()); + + adapter->next_port_scan = jiffies + interval + backoff; +} + +static void zfcp_fc_port_scan(struct zfcp_adapter *adapter) +{ + unsigned long now = jiffies; + unsigned long next = adapter->next_port_scan; + unsigned long delay = 0, max; + + /* delay only needed within waiting period */ + if (time_before(now, next)) { + delay = next - now; + /* paranoia: never ever delay scans longer than specified */ + max = msecs_to_jiffies(port_scan_ratelimit + port_scan_backoff); + delay = min(delay, max); + } + + queue_delayed_work(adapter->work_queue, &adapter->scan_work, delay); +} + void zfcp_fc_conditional_port_scan(struct zfcp_adapter *adapter) { if (no_auto_port_rescan) return; - queue_work(adapter->work_queue, &adapter->scan_work); + zfcp_fc_port_scan(adapter); } void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter) @@ -44,7 +87,7 @@ void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter) if (!no_auto_port_rescan) return; - queue_work(adapter->work_queue, &adapter->scan_work); + zfcp_fc_port_scan(adapter); } /** @@ -680,12 +723,15 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_req *fc_req, */ void zfcp_fc_scan_ports(struct work_struct *work) { - struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, + struct delayed_work *dw = to_delayed_work(work); + struct zfcp_adapter *adapter = container_of(dw, struct zfcp_adapter, scan_work); int ret, i; struct zfcp_fc_req *fc_req; int chain, max_entries, buf_num, max_bytes; + zfcp_fc_port_scan_time(adapter); + chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS; buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1; max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE; diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h index b1d2024ed513..df2b541c8287 100644 --- a/drivers/s390/scsi/zfcp_fc.h +++ b/drivers/s390/scsi/zfcp_fc.h @@ -212,8 +212,6 @@ static inline void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi, u8 tm_flags) { - char tag[2]; - int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun); if (unlikely(tm_flags)) { @@ -221,17 +219,7 @@ void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi, return; } - if (scsi_populate_tag_msg(scsi, tag)) { - switch (tag[0]) { - case MSG_ORDERED_TAG: - fcp->fc_pri_ta |= FCP_PTA_ORDERED; - break; - case MSG_SIMPLE_TAG: - fcp->fc_pri_ta |= FCP_PTA_SIMPLE; - break; - }; - } else - fcp->fc_pri_ta = FCP_PTA_SIMPLE; + fcp->fc_pri_ta = FCP_PTA_SIMPLE; if (scsi->sc_data_direction == DMA_FROM_DEVICE) fcp->fc_flags |= FCP_CFL_RDDATA; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 0fe8d5d95119..21ec5e2f584c 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1396,8 +1396,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) port->handle = header->port_handle; atomic_set_mask(ZFCP_STATUS_COMMON_OPEN | ZFCP_STATUS_PORT_PHYS_OPEN, &port->status); - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | - ZFCP_STATUS_COMMON_ACCESS_BOXED, + atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED, &port->status); /* check whether D_ID has changed during open */ /* diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 7b353647cb90..75f4bfc2b98a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -32,25 +32,6 @@ static bool allow_lun_scan = 1; module_param(allow_lun_scan, bool, 0600); MODULE_PARM_DESC(allow_lun_scan, "For NPIV, scan and attach all storage LUNs"); -static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth, - int reason) -{ - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, depth); - break; - case SCSI_QDEPTH_RAMP_UP: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - default: - return -EOPNOTSUPP; - } - return sdev->queue_depth; -} - static void zfcp_scsi_slave_destroy(struct scsi_device *sdev) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); @@ -66,9 +47,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdev) static int zfcp_scsi_slave_configure(struct scsi_device *sdp) { if (sdp->tagged_supported) - scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, default_depth); - else - scsi_adjust_queue_depth(sdp, 0, 1); + scsi_change_queue_depth(sdp, default_depth); return 0; } @@ -307,7 +286,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .slave_alloc = zfcp_scsi_slave_alloc, .slave_configure = zfcp_scsi_slave_configure, .slave_destroy = zfcp_scsi_slave_destroy, - .change_queue_depth = zfcp_scsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .proc_name = "zfcp", .can_queue = 4096, .this_id = -1, @@ -322,6 +301,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .use_clustering = 1, .shost_attrs = zfcp_sysfs_shost_attrs, .sdev_attrs = zfcp_sysfs_sdev_attrs, + .track_queue_depth = 1, }; /** diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 672b57219e11..96a0be13e841 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -73,9 +73,7 @@ ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n", ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n", (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) != 0); -ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n", - (atomic_read(&port->status) & - ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); +ZFCP_DEFINE_ATTR_CONST(port, access_denied, "%d\n", 0); ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n", zfcp_unit_sdev_status(unit)); @@ -223,9 +221,13 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, if (!adapter) return -ENODEV; - /* sync the user-space- with the kernel-invocation of scan_work */ - queue_work(adapter->work_queue, &adapter->scan_work); - flush_work(&adapter->scan_work); + /* + * Users wish is our command: immediately schedule and flush a + * worker to conduct a synchronous port scan, that is, neither + * a random delay nor a rate limit is applied here. + */ + queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0); + flush_delayed_work(&adapter->scan_work); zfcp_ccw_adapter_put(adapter); return (ssize_t) count; @@ -439,16 +441,15 @@ static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \ { \ struct scsi_device *sdev = to_scsi_device(dev); \ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); \ - struct zfcp_port *port = zfcp_sdev->port; \ \ return sprintf(buf, _format, _value); \ } \ static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL); ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n", - dev_name(&port->adapter->ccw_device->dev)); + dev_name(&zfcp_sdev->port->adapter->ccw_device->dev)); ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", - (unsigned long long) port->wwpn); + (unsigned long long) zfcp_sdev->port->wwpn); static ssize_t zfcp_sysfs_scsi_fcp_lun_show(struct device *dev, struct device_attribute *attr, @@ -460,6 +461,49 @@ static ssize_t zfcp_sysfs_scsi_fcp_lun_show(struct device *dev, } static DEVICE_ATTR(fcp_lun, S_IRUGO, zfcp_sysfs_scsi_fcp_lun_show, NULL); +ZFCP_DEFINE_SCSI_ATTR(zfcp_access_denied, "%d\n", + (atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); + +static ssize_t zfcp_sysfs_scsi_zfcp_failed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + unsigned int status = atomic_read(&sdev_to_zfcp(sdev)->status); + unsigned int failed = status & ZFCP_STATUS_COMMON_ERP_FAILED ? 1 : 0; + + return sprintf(buf, "%d\n", failed); +} + +static ssize_t zfcp_sysfs_scsi_zfcp_failed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val) || val != 0) + return -EINVAL; + + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, + "syufai3"); + zfcp_erp_wait(sdev_to_zfcp(sdev)->port->adapter); + + return count; +} +static DEVICE_ATTR(zfcp_failed, S_IWUSR | S_IRUGO, + zfcp_sysfs_scsi_zfcp_failed_show, + zfcp_sysfs_scsi_zfcp_failed_store); + +ZFCP_DEFINE_SCSI_ATTR(zfcp_in_recovery, "%d\n", + (atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_ERP_INUSE) != 0); + +ZFCP_DEFINE_SCSI_ATTR(zfcp_status, "0x%08x\n", + atomic_read(&zfcp_sdev->status)); + struct device_attribute *zfcp_sysfs_sdev_attrs[] = { &dev_attr_fcp_lun, &dev_attr_wwpn, @@ -467,6 +511,10 @@ struct device_attribute *zfcp_sysfs_sdev_attrs[] = { &dev_attr_read_latency, &dev_attr_write_latency, &dev_attr_cmd_latency, + &dev_attr_zfcp_access_denied, + &dev_attr_zfcp_failed, + &dev_attr_zfcp_in_recovery, + &dev_attr_zfcp_status, NULL }; diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 0a7325361d29..cd4129ff7ae4 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -189,19 +189,6 @@ static ssize_t twa_show_stats(struct device *dev, return len; } /* End twa_show_stats() */ -/* This function will set a devices queue depth */ -static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth, - int reason) -{ - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - - if (queue_depth > TW_Q_LENGTH-2) - queue_depth = TW_Q_LENGTH-2; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); - return queue_depth; -} /* End twa_change_queue_depth() */ - /* Create sysfs 'stats' entry */ static struct device_attribute twa_host_stats_attr = { .attr = { @@ -2016,7 +2003,7 @@ static struct scsi_host_template driver_template = { .queuecommand = twa_scsi_queue, .eh_host_reset_handler = twa_scsi_eh_reset, .bios_param = twa_scsi_biosparam, - .change_queue_depth = twa_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .can_queue = TW_Q_LENGTH-2, .slave_configure = twa_slave_configure, .this_id = -1, diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c index 6da6cec9a651..2361772d5909 100644 --- a/drivers/scsi/3w-sas.c +++ b/drivers/scsi/3w-sas.c @@ -191,19 +191,6 @@ static ssize_t twl_show_stats(struct device *dev, return len; } /* End twl_show_stats() */ -/* This function will set a devices queue depth */ -static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth, - int reason) -{ - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - - if (queue_depth > TW_Q_LENGTH-2) - queue_depth = TW_Q_LENGTH-2; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); - return queue_depth; -} /* End twl_change_queue_depth() */ - /* stats sysfs attribute initializer */ static struct device_attribute twl_host_stats_attr = { .attr = { @@ -1590,7 +1577,7 @@ static struct scsi_host_template driver_template = { .queuecommand = twl_scsi_queue, .eh_host_reset_handler = twl_scsi_eh_reset, .bios_param = twl_scsi_biosparam, - .change_queue_depth = twl_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .can_queue = TW_Q_LENGTH-2, .slave_configure = twl_slave_configure, .this_id = -1, diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 752624e6bc00..c75f2048319f 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -523,19 +523,6 @@ static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr, return len; } /* End tw_show_stats() */ -/* This function will set a devices queue depth */ -static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth, - int reason) -{ - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - - if (queue_depth > TW_Q_LENGTH-2) - queue_depth = TW_Q_LENGTH-2; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); - return queue_depth; -} /* End tw_change_queue_depth() */ - /* Create sysfs 'stats' entry */ static struct device_attribute tw_host_stats_attr = { .attr = { @@ -2270,7 +2257,7 @@ static struct scsi_host_template driver_template = { .queuecommand = tw_scsi_queue, .eh_host_reset_handler = tw_scsi_eh_reset, .bios_param = tw_scsi_biosparam, - .change_queue_depth = tw_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .can_queue = TW_Q_LENGTH-2, .slave_configure = tw_slave_configure, .this_id = -1, diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index fabd4be2c985..aa915da2a5e5 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -175,7 +175,7 @@ STATIC void NCR_700_chip_reset(struct Scsi_Host *host); STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt); STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt); STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt); -static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason); +static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth); static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth); STATIC struct device_attribute *NCR_700_dev_attrs[]; @@ -327,6 +327,7 @@ NCR_700_detect(struct scsi_host_template *tpnt, tpnt->slave_alloc = NCR_700_slave_alloc; tpnt->change_queue_depth = NCR_700_change_queue_depth; tpnt->change_queue_type = NCR_700_change_queue_type; + tpnt->use_blk_tags = 1; if(tpnt->name == NULL) tpnt->name = "53c700"; @@ -592,19 +593,14 @@ NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, hostdata->cmd = NULL; if(SCp != NULL) { - struct NCR_700_command_slot *slot = + struct NCR_700_command_slot *slot = (struct NCR_700_command_slot *)SCp->host_scribble; - + dma_unmap_single(hostdata->dev, slot->pCmd, MAX_COMMAND_SIZE, DMA_TO_DEVICE); if (slot->flags == NCR_700_FLAG_AUTOSENSE) { char *cmnd = NCR_700_get_sense_cmnd(SCp->device); -#ifdef NCR_700_DEBUG - printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is\n", - SCp, SCp->cmnd[7], result); - scsi_print_sense("53c700", SCp); -#endif dma_unmap_single(hostdata->dev, slot->dma_handle, SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); /* restore the old result if the request sense was @@ -906,8 +902,10 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata /* we're done negotiating */ NCR_700_set_tag_neg_state(SCp->device, NCR_700_FINISHED_TAG_NEGOTIATION); hostdata->tag_negotiated &= ~(1<<scmd_id(SCp)); + SCp->device->tagged_supported = 0; - scsi_deactivate_tcq(SCp->device, host->cmd_per_lun); + scsi_change_queue_depth(SCp->device, host->cmd_per_lun); + scsi_set_tag_type(SCp->device, 0); } else { shost_printk(KERN_WARNING, host, "(%d:%d) Unexpected REJECT Message %s\n", @@ -1432,7 +1430,7 @@ NCR_700_start_command(struct scsi_cmnd *SCp) if((hostdata->tag_negotiated & (1<<scmd_id(SCp))) && (slot->tag != SCSI_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE && slot->flags != NCR_700_FLAG_AUTOSENSE)) { - count += scsi_populate_tag_msg(SCp, &hostdata->msgout[count]); + count += spi_populate_tag_msg(&hostdata->msgout[count], SCp); } if(hostdata->fast && @@ -1772,7 +1770,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *) */ if(NCR_700_get_depth(SCp->device) != 0 && (!(hostdata->tag_negotiated & (1<<scmd_id(SCp))) - || !blk_rq_tagged(SCp->request))) { + || !(SCp->flags & SCMD_TAGGED))) { CDEBUG(KERN_ERR, SCp, "has non zero depth %d\n", NCR_700_get_depth(SCp->device)); return SCSI_MLQUEUE_DEVICE_BUSY; @@ -1800,7 +1798,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *) printk("53c700: scsi%d, command ", SCp->device->host->host_no); scsi_print_command(SCp); #endif - if(blk_rq_tagged(SCp->request) + if ((SCp->flags & SCMD_TAGGED) && (hostdata->tag_negotiated &(1<<scmd_id(SCp))) == 0 && NCR_700_get_tag_neg_state(SCp->device) == NCR_700_START_TAG_NEGOTIATION) { scmd_printk(KERN_ERR, SCp, "Enabling Tag Command Queuing\n"); @@ -1814,7 +1812,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *) * * FIXME: This will royally screw up on multiple LUN devices * */ - if(!blk_rq_tagged(SCp->request) + if (!(SCp->flags & SCMD_TAGGED) && (hostdata->tag_negotiated &(1<<scmd_id(SCp)))) { scmd_printk(KERN_INFO, SCp, "Disabling Tag Command Queuing\n"); hostdata->tag_negotiated &= ~(1<<scmd_id(SCp)); @@ -1911,9 +1909,7 @@ NCR_700_abort(struct scsi_cmnd * SCp) { struct NCR_700_command_slot *slot; - scmd_printk(KERN_INFO, SCp, - "New error handler wants to abort command\n\t"); - scsi_print_command(SCp); + scmd_printk(KERN_INFO, SCp, "abort command\n"); slot = (struct NCR_700_command_slot *)SCp->host_scribble; @@ -2056,13 +2052,10 @@ NCR_700_slave_configure(struct scsi_device *SDp) /* to do here: allocate memory; build a queue_full list */ if(SDp->tagged_supported) { - scsi_set_tag_type(SDp, MSG_ORDERED_TAG); - scsi_activate_tcq(SDp, NCR_700_DEFAULT_TAGS); + scsi_change_queue_depth(SDp, NCR_700_DEFAULT_TAGS); NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION); - } else { - /* initialise to default depth */ - scsi_adjust_queue_depth(SDp, 0, SDp->host->cmd_per_lun); } + if(hostdata->fast) { /* Find the correct offset and period via domain validation */ if (!spi_initial_dv(SDp->sdev_target)) @@ -2082,16 +2075,11 @@ NCR_700_slave_destroy(struct scsi_device *SDp) } static int -NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason) +NCR_700_change_queue_depth(struct scsi_device *SDp, int depth) { - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (depth > NCR_700_MAX_TAGS) depth = NCR_700_MAX_TAGS; - - scsi_adjust_queue_depth(SDp, scsi_get_tag_type(SDp), depth); - return depth; + return scsi_change_queue_depth(SDp, depth); } static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type) @@ -2101,8 +2089,6 @@ static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type) struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; - scsi_set_tag_type(SDp, tag_type); - /* We have a global (per target) flag to track whether TCQ is * enabled, so we'll be turning it off for the entire target here. * our tag algorithm will fail if we mix tagged and untagged commands, @@ -2110,15 +2096,16 @@ static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type) if (change_tag) scsi_target_quiesce(SDp->sdev_target); + scsi_set_tag_type(SDp, tag_type); if (!tag_type) { /* shift back to the default unqueued number of commands * (the user can still raise this) */ - scsi_deactivate_tcq(SDp, SDp->host->cmd_per_lun); + scsi_change_queue_depth(SDp, SDp->host->cmd_per_lun); hostdata->tag_negotiated &= ~(1 << sdev_id(SDp)); } else { /* Here, we cleared the negotiation flag above, so this * will force the driver to renegotiate */ - scsi_activate_tcq(SDp, SDp->queue_depth); + scsi_change_queue_depth(SDp, SDp->queue_depth); if (change_tag) NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION); } diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 64c75143c89a..8d66a6469e29 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -2327,12 +2327,12 @@ static int blogic_slaveconfig(struct scsi_device *dev) if (qdepth == 0) qdepth = BLOGIC_MAX_AUTO_TAG_DEPTH; adapter->qdepth[tgt_id] = qdepth; - scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, qdepth); + scsi_change_queue_depth(dev, qdepth); } else { adapter->tagq_ok &= ~(1 << tgt_id); qdepth = adapter->untag_qdepth; adapter->qdepth[tgt_id] = qdepth; - scsi_adjust_queue_depth(dev, 0, qdepth); + scsi_change_queue_depth(dev, qdepth); } qdepth = 0; for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 3a820f61ce65..86cf3d671eb9 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1341,13 +1341,15 @@ config SCSI_DC395x To compile this driver as a module, choose M here: the module will be called dc395x. -config SCSI_DC390T - tristate "Tekram DC390(T) and Am53/79C974 SCSI support" +config SCSI_AM53C974 + tristate "Tekram DC390(T) and Am53/79C974 SCSI support (new driver)" depends on PCI && SCSI + select SCSI_SPI_ATTRS ---help--- This driver supports PCI SCSI host adapters based on the Am53C974A chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard PCscsi/PCnet (Am53/79C974) solutions. + This is a new implementation base on the generic esp_scsi driver. Documentation can be found in <file:Documentation/scsi/tmscsim.txt>. @@ -1355,7 +1357,7 @@ config SCSI_DC390T based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those. To compile this driver as a module, choose M here: the - module will be called tmscsim. + module will be called am53c974. config SCSI_T128 tristate "Trantor T128/T128F/T228 SCSI support" @@ -1451,6 +1453,14 @@ config SCSI_NSP32 To compile this driver as a module, choose M here: the module will be called nsp32. +config SCSI_WD719X + tristate "Western Digital WD7193/7197/7296 support" + depends on PCI && SCSI + select EEPROM_93CX6 + ---help--- + This is a driver for Western Digital WD7193, WD7197 and WD7296 PCI + SCSI controllers (based on WD33C296A chip). + config SCSI_DEBUG tristate "SCSI debugging host simulator" depends on SCSI @@ -1615,7 +1625,7 @@ config ATARI_SCSI_RESET_BOOT that leave the devices with SCSI operations partway completed. config MAC_SCSI - bool "Macintosh NCR5380 SCSI" + tristate "Macintosh NCR5380 SCSI" depends on MAC && SCSI=y select SCSI_SPI_ATTRS help diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 59f1ce6df2d6..58158f11ed7b 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -100,7 +100,7 @@ obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o obj-$(CONFIG_SCSI_7000FASST) += wd7000.o obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_DC395x) += dc395x.o -obj-$(CONFIG_SCSI_DC390T) += tmscsim.o +obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ @@ -143,6 +143,7 @@ obj-$(CONFIG_SCSI_VIRTIO) += virtio_scsi.o obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o obj-$(CONFIG_XEN_SCSI_FRONTEND) += xen-scsifront.o obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o +obj-$(CONFIG_SCSI_WD719X) += wd719x.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index 45da3c823322..36244d63def2 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -11,8 +11,6 @@ * drew@colorado.edu * +1 (303) 666-5836 * - * DISTRIBUTION RELEASE 6. - * * For more information, please consult * * NCR 5380 Family @@ -279,7 +277,7 @@ static void do_reset(struct Scsi_Host *host); * Set up the internal fields in the SCSI command. */ -static __inline__ void initialize_SCp(Scsi_Cmnd * cmd) +static inline void initialize_SCp(struct scsi_cmnd *cmd) { /* * Initialize the Scsi Pointer field so that all of the commands in the @@ -574,12 +572,12 @@ static int __init __maybe_unused NCR5380_probe_irq(struct Scsi_Host *instance, int trying_irqs, i, mask; NCR5380_setup(instance); - for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1) + for (trying_irqs = 0, i = 1, mask = 2; i < 16; ++i, mask <<= 1) if ((mask & possible) && (request_irq(i, &probe_intr, 0, "NCR-probe", NULL) == 0)) trying_irqs |= mask; timeout = jiffies + (250 * HZ / 1000); - probe_irq = SCSI_IRQ_NONE; + probe_irq = NO_IRQ; /* * A interrupt is triggered whenever BSY = false, SEL = true @@ -596,13 +594,13 @@ static int __init __maybe_unused NCR5380_probe_irq(struct Scsi_Host *instance, NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL); - while (probe_irq == SCSI_IRQ_NONE && time_before(jiffies, timeout)) + while (probe_irq == NO_IRQ && time_before(jiffies, timeout)) schedule_timeout_uninterruptible(1); NCR5380_write(SELECT_ENABLE_REG, 0); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - for (i = 0, mask = 1; i < 16; ++i, mask <<= 1) + for (i = 1, mask = 2; i < 16; ++i, mask <<= 1) if (trying_irqs & mask) free_irq(i, NULL); @@ -610,50 +608,70 @@ static int __init __maybe_unused NCR5380_probe_irq(struct Scsi_Host *instance, } /** - * NCR58380_print_options - show options - * @instance: unused for now + * NCR58380_info - report driver and host information + * @instance: relevant scsi host instance * - * Called by probe code indicating the NCR5380 driver options that - * were selected. At some point this will switch to runtime options - * read from the adapter in question + * For use as the host template info() handler. * * Locks: none */ -static void __init __maybe_unused -NCR5380_print_options(struct Scsi_Host *instance) +static const char *NCR5380_info(struct Scsi_Host *instance) { - printk(" generic options" -#ifdef AUTOPROBE_IRQ - " AUTOPROBE_IRQ" + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + return hostdata->info; +} + +static void prepare_info(struct Scsi_Host *instance) +{ + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + snprintf(hostdata->info, sizeof(hostdata->info), + "%s, io_port 0x%lx, n_io_port %d, " + "base 0x%lx, irq %d, " + "can_queue %d, cmd_per_lun %d, " + "sg_tablesize %d, this_id %d, " + "flags { %s%s%s}, " +#if defined(USLEEP_POLL) && defined(USLEEP_WAITLONG) + "USLEEP_POLL %d, USLEEP_WAITLONG %d, " #endif -#ifdef AUTOSENSE - " AUTOSENSE" + "options { %s} ", + instance->hostt->name, instance->io_port, instance->n_io_port, + instance->base, instance->irq, + instance->can_queue, instance->cmd_per_lun, + instance->sg_tablesize, instance->this_id, + hostdata->flags & FLAG_NCR53C400 ? "NCR53C400 " : "", + hostdata->flags & FLAG_DTC3181E ? "DTC3181E " : "", + hostdata->flags & FLAG_NO_PSEUDO_DMA ? "NO_PSEUDO_DMA " : "", +#if defined(USLEEP_POLL) && defined(USLEEP_WAITLONG) + USLEEP_POLL, USLEEP_WAITLONG, +#endif +#ifdef AUTOPROBE_IRQ + "AUTOPROBE_IRQ " #endif #ifdef DIFFERENTIAL - " DIFFERENTIAL" + "DIFFERENTIAL " #endif #ifdef REAL_DMA - " REAL DMA" + "REAL_DMA " #endif #ifdef REAL_DMA_POLL - " REAL DMA POLL" + "REAL_DMA_POLL " #endif #ifdef PARITY - " PARITY" + "PARITY " #endif #ifdef PSEUDO_DMA - " PSEUDO DMA" + "PSEUDO_DMA " #endif #ifdef UNSAFE - " UNSAFE " + "UNSAFE " #endif - ); - printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP); - printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); - if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) { - printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE); - } +#ifdef NCR53C400 + "NCR53C400 " +#endif + ""); } /** @@ -672,6 +690,7 @@ static void NCR5380_print_status(struct Scsi_Host *instance) NCR5380_dprint_phase(NDEBUG_ANY, instance); } +#ifdef PSEUDO_DMA /******************************************/ /* * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED] @@ -689,19 +708,18 @@ static void NCR5380_print_status(struct Scsi_Host *instance) static int __maybe_unused NCR5380_write_info(struct Scsi_Host *instance, char *buffer, int length) { -#ifdef DTC_PUBLIC_RELEASE - dtc_wmaxi = dtc_maxi = 0; -#endif -#ifdef PAS16_PUBLIC_RELEASE - pas_wmaxi = pas_maxi = 0; -#endif - return (-ENOSYS); /* Currently this is a no-op */ + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + hostdata->spin_max_r = 0; + hostdata->spin_max_w = 0; + return 0; } +#endif #undef SPRINTF #define SPRINTF(args...) seq_printf(m, ## args) static -void lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, struct seq_file *m); +void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m); static void lprint_command(unsigned char *cmd, struct seq_file *m); static @@ -711,56 +729,31 @@ static int __maybe_unused NCR5380_show_info(struct seq_file *m, struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata; - Scsi_Cmnd *ptr; + struct scsi_cmnd *ptr; hostdata = (struct NCR5380_hostdata *) instance->hostdata; - SPRINTF("NCR5380 core release=%d. ", NCR5380_PUBLIC_RELEASE); - if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) - SPRINTF("ncr53c400 release=%d. ", NCR53C400_PUBLIC_RELEASE); -#ifdef DTC_PUBLIC_RELEASE - SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE); -#endif -#ifdef T128_PUBLIC_RELEASE - SPRINTF("T128 release %d", T128_PUBLIC_RELEASE); -#endif -#ifdef GENERIC_NCR5380_PUBLIC_RELEASE - SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE); -#endif -#ifdef PAS16_PUBLIC_RELEASE - SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE); -#endif - - SPRINTF("\nBase Addr: 0x%05lX ", (long) instance->base); - SPRINTF("io_port: %04x ", (int) instance->io_port); - if (instance->irq == SCSI_IRQ_NONE) - SPRINTF("IRQ: None.\n"); - else - SPRINTF("IRQ: %d.\n", instance->irq); - -#ifdef DTC_PUBLIC_RELEASE - SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", dtc_wmaxi, dtc_maxi); -#endif -#ifdef PAS16_PUBLIC_RELEASE - SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi); +#ifdef PSEUDO_DMA + SPRINTF("Highwater I/O busy spin counts: write %d, read %d\n", + hostdata->spin_max_w, hostdata->spin_max_r); #endif spin_lock_irq(instance->host_lock); if (!hostdata->connected) SPRINTF("scsi%d: no currently connected command\n", instance->host_no); else - lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, m); + lprint_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected, m); SPRINTF("scsi%d: issue_queue\n", instance->host_no); - for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) + for (ptr = (struct scsi_cmnd *) hostdata->issue_queue; ptr; ptr = (struct scsi_cmnd *) ptr->host_scribble) lprint_Scsi_Cmnd(ptr, m); SPRINTF("scsi%d: disconnected_queue\n", instance->host_no); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) + for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr; ptr = (struct scsi_cmnd *) ptr->host_scribble) lprint_Scsi_Cmnd(ptr, m); spin_unlock_irq(instance->host_lock); return 0; } -static void lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, struct seq_file *m) +static void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m) { SPRINTF("scsi%d : destination target %d, lun %llu\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun); SPRINTF(" command = "); @@ -836,18 +829,6 @@ static int NCR5380_init(struct Scsi_Host *instance, int flags) INIT_DELAYED_WORK(&hostdata->coroutine, NCR5380_main); -#ifdef NCR5380_STATS - for (i = 0; i < 8; ++i) { - hostdata->time_read[i] = 0; - hostdata->time_write[i] = 0; - hostdata->bytes_read[i] = 0; - hostdata->bytes_write[i] = 0; - } - hostdata->timebase = 0; - hostdata->pendingw = 0; - hostdata->pendingr = 0; -#endif - /* The CHECK code seems to break the 53C400. Will check it later maybe */ if (flags & FLAG_NCR53C400) hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags; @@ -857,11 +838,7 @@ static int NCR5380_init(struct Scsi_Host *instance, int flags) hostdata->host = instance; hostdata->time_expires = 0; -#ifndef AUTOSENSE - if ((instance->cmd_per_lun > 1) || instance->can_queue > 1) - printk(KERN_WARNING "scsi%d : WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n" - " be incorrectly cleared.\n", instance->host_no); -#endif /* def AUTOSENSE */ + prepare_info(instance); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_write(MODE_REG, MR_BASE); @@ -935,11 +912,11 @@ static void NCR5380_exit(struct Scsi_Host *instance) * Locks: host lock taken by caller */ -static int NCR5380_queue_command_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) +static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *)) { struct Scsi_Host *instance = cmd->device->host; struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; - Scsi_Cmnd *tmp; + struct scsi_cmnd *tmp; #if (NDEBUG & NDEBUG_NO_WRITE) switch (cmd->cmnd[0]) { @@ -952,25 +929,6 @@ static int NCR5380_queue_command_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *) } #endif /* (NDEBUG & NDEBUG_NO_WRITE) */ -#ifdef NCR5380_STATS - switch (cmd->cmnd[0]) { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase); - hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd); - hostdata->pendingw++; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase); - hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd); - hostdata->pendingr++; - break; - } -#endif - /* * We use the host_scribble field as a pointer to the next command * in a queue @@ -992,7 +950,7 @@ static int NCR5380_queue_command_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *) cmd->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = cmd; } else { - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble); + for (tmp = (struct scsi_cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (struct scsi_cmnd *) tmp->host_scribble); LIST(cmd, tmp); tmp->host_scribble = (unsigned char *) cmd; } @@ -1023,7 +981,7 @@ static void NCR5380_main(struct work_struct *work) struct NCR5380_hostdata *hostdata = container_of(work, struct NCR5380_hostdata, coroutine.work); struct Scsi_Host *instance = hostdata->host; - Scsi_Cmnd *tmp, *prev; + struct scsi_cmnd *tmp, *prev; int done; spin_lock_irq(instance->host_lock); @@ -1036,7 +994,7 @@ static void NCR5380_main(struct work_struct *work) * Search through the issue_queue for a command destined * for a target that's not busy. */ - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (struct scsi_cmnd *) tmp->host_scribble) { if (prev != tmp) dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun); @@ -1048,7 +1006,7 @@ static void NCR5380_main(struct work_struct *work) prev->host_scribble = tmp->host_scribble; } else { REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble); - hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble; + hostdata->issue_queue = (struct scsi_cmnd *) tmp->host_scribble; } tmp->host_scribble = NULL; @@ -1073,14 +1031,14 @@ static void NCR5380_main(struct work_struct *work) hostdata->selecting = NULL; /* RvC: have to preset this to indicate a new command is being performed */ - if (!NCR5380_select(instance, tmp, - /* - * REQUEST SENSE commands are issued without tagged - * queueing, even on SCSI-II devices because the - * contingent allegiance condition exists for the - * entire unit. - */ - (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) { + /* + * REQUEST SENSE commands are issued without tagged + * queueing, even on SCSI-II devices because the + * contingent allegiance condition exists for the + * entire unit. + */ + + if (!NCR5380_select(instance, tmp)) { break; } else { LIST(tmp, hostdata->issue_queue); @@ -1095,9 +1053,9 @@ static void NCR5380_main(struct work_struct *work) /* exited locked */ } /* if (!hostdata->connected) */ if (hostdata->selecting) { - tmp = (Scsi_Cmnd *) hostdata->selecting; + tmp = (struct scsi_cmnd *) hostdata->selecting; /* Selection will drop and retake the lock */ - if (!NCR5380_select(instance, tmp, (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) { + if (!NCR5380_select(instance, tmp)) { /* Ok ?? */ } else { /* RvC: device failed, so we wait a long time @@ -1216,47 +1174,16 @@ static irqreturn_t NCR5380_intr(int dummy, void *dev_id) #endif -/** - * collect_stats - collect stats on a scsi command - * @hostdata: adapter - * @cmd: command being issued - * - * Update the statistical data by parsing the command in question - */ - -static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd) -{ -#ifdef NCR5380_STATS - switch (cmd->cmnd[0]) { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[scmd_id(cmd)] += (jiffies - hostdata->timebase); - hostdata->pendingw--; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[scmd_id(cmd)] += (jiffies - hostdata->timebase); - hostdata->pendingr--; - break; - } -#endif -} - - /* - * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, - * int tag); + * Function : int NCR5380_select(struct Scsi_Host *instance, + * struct scsi_cmnd *cmd) * * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command, * including ARBITRATION, SELECTION, and initial message out for * IDENTIFY and queue messages. * * Inputs : instance - instantiation of the 5380 driver on which this - * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for - * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for - * the command that is presently connected. + * target lives, cmd - SCSI command to execute. * * Returns : -1 if selection could not execute for some reason, * 0 if selection succeeded or failed because the target @@ -1278,7 +1205,7 @@ static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd) * Locks: caller holds hostdata lock in IRQ mode */ -static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag) +static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd) { NCR5380_local_declare(); struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; @@ -1476,7 +1403,6 @@ part2: return -1; } cmd->result = DID_BAD_TARGET << 16; - collect_stats(hostdata, cmd); cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); dprintk(NDEBUG_SELECTION, "scsi%d : target did not respond within 250ms\n", instance->host_no); @@ -1513,7 +1439,7 @@ part2: } dprintk(NDEBUG_SELECTION, "scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id); - tmp[0] = IDENTIFY(((instance->irq == SCSI_IRQ_NONE) ? 0 : 1), cmd->device->lun); + tmp[0] = IDENTIFY(((instance->irq == NO_IRQ) ? 0 : 1), cmd->device->lun); len = 1; cmd->tag = 0; @@ -2086,7 +2012,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { #endif unsigned char *data; unsigned char phase, tmp, extended_msg[10], old_phase = 0xff; - Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected; + struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected; /* RvC: we need to set the end of the polling time */ unsigned long poll_time = jiffies + USLEEP_POLL; @@ -2228,7 +2154,6 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { cmd->next_link->tag = cmd->tag; cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); dprintk(NDEBUG_LINKED, "scsi%d : target %d lun %llu linked request done, calling scsi_done().\n", instance->host_no, cmd->device->id, cmd->device->lun); - collect_stats(hostdata, cmd); cmd->scsi_done(cmd); cmd = hostdata->connected; break; @@ -2263,7 +2188,6 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { else if (status_byte(cmd->SCp.Status) != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); -#ifdef AUTOSENSE if ((cmd->cmnd[0] == REQUEST_SENSE) && hostdata->ses.cmd_len) { scsi_eh_restore_cmnd(cmd, &hostdata->ses); @@ -2278,12 +2202,9 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { LIST(cmd, hostdata->issue_queue); cmd->host_scribble = (unsigned char *) hostdata->issue_queue; - hostdata->issue_queue = (Scsi_Cmnd *) cmd; + hostdata->issue_queue = (struct scsi_cmnd *) cmd; dprintk(NDEBUG_QUEUES, "scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no); - } else -#endif /* def AUTOSENSE */ - { - collect_stats(hostdata, cmd); + } else { cmd->scsi_done(cmd); } @@ -2430,7 +2351,6 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xFF)); hostdata->connected = NULL; cmd->result = DID_ERROR << 16; - collect_stats(hostdata, cmd); cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return; @@ -2479,7 +2399,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { * Function : void NCR5380_reselect (struct Scsi_Host *instance) * * Purpose : does reselection, initializing the instance->connected - * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q + * field to point to the scsi_cmnd for which the I_T_L or I_T_L_Q * nexus has been reestablished, * * Inputs : instance - this instance of the NCR5380. @@ -2496,7 +2416,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) { int len; unsigned char msg[3]; unsigned char *data; - Scsi_Cmnd *tmp = NULL, *prev; + struct scsi_cmnd *tmp = NULL, *prev; int abort = 0; NCR5380_setup(instance); @@ -2562,7 +2482,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) { */ - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (struct scsi_cmnd *) tmp->host_scribble) if ((target_mask == (1 << tmp->device->id)) && (lun == (u8)tmp->device->lun) ) { if (prev) { @@ -2570,7 +2490,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) { prev->host_scribble = tmp->host_scribble; } else { REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble); - hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble; + hostdata->disconnected_queue = (struct scsi_cmnd *) tmp->host_scribble; } tmp->host_scribble = NULL; break; @@ -2601,7 +2521,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) { * * Inputs : instance - this instance of the NCR5380. * - * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L + * Returns : pointer to the scsi_cmnd structure for which the I_T_L * nexus has been reestablished, on failure NULL is returned. */ @@ -2643,32 +2563,32 @@ static void NCR5380_dma_complete(NCR5380_instance * instance) { #endif /* def REAL_DMA */ /* - * Function : int NCR5380_abort (Scsi_Cmnd *cmd) + * Function : int NCR5380_abort (struct scsi_cmnd *cmd) * * Purpose : abort a command * - * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the - * host byte of the result field to, if zero DID_ABORTED is + * Inputs : cmd - the scsi_cmnd to abort, code - code to set the + * host byte of the result field to, if zero DID_ABORTED is * used. * - * Returns : 0 - success, -1 on failure. + * Returns : SUCCESS - success, FAILED on failure. * - * XXX - there is no way to abort the command that is currently - * connected, you have to wait for it to complete. If this is + * XXX - there is no way to abort the command that is currently + * connected, you have to wait for it to complete. If this is * a problem, we could implement longjmp() / setjmp(), setjmp() * called where the loop started in NCR5380_main(). * * Locks: host lock taken by caller */ -static int NCR5380_abort(Scsi_Cmnd * cmd) { +static int NCR5380_abort(struct scsi_cmnd *cmd) +{ NCR5380_local_declare(); struct Scsi_Host *instance = cmd->device->host; struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; - Scsi_Cmnd *tmp, **prev; - - printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no); - scsi_print_command(cmd); + struct scsi_cmnd *tmp, **prev; + + scmd_printk(KERN_WARNING, cmd, "aborting command\n"); NCR5380_print_status(instance); @@ -2704,7 +2624,7 @@ static int NCR5380_abort(Scsi_Cmnd * cmd) { * aborted flag and get back into our main loop. */ - return 0; + return SUCCESS; } #endif @@ -2714,10 +2634,10 @@ static int NCR5380_abort(Scsi_Cmnd * cmd) { */ dprintk(NDEBUG_ABORT, "scsi%d : abort going into loop.\n", instance->host_no); - for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (prev = (struct scsi_cmnd **) &(hostdata->issue_queue), tmp = (struct scsi_cmnd *) hostdata->issue_queue; tmp; prev = (struct scsi_cmnd **) &(tmp->host_scribble), tmp = (struct scsi_cmnd *) tmp->host_scribble) if (cmd == tmp) { REMOVE(5, *prev, tmp, tmp->host_scribble); - (*prev) = (Scsi_Cmnd *) tmp->host_scribble; + (*prev) = (struct scsi_cmnd *) tmp->host_scribble; tmp->host_scribble = NULL; tmp->result = DID_ABORT << 16; dprintk(NDEBUG_ABORT, "scsi%d : abort removed command from issue queue.\n", instance->host_no); @@ -2770,20 +2690,20 @@ static int NCR5380_abort(Scsi_Cmnd * cmd) { * it from the disconnected queue. */ - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; tmp = (struct scsi_cmnd *) tmp->host_scribble) if (cmd == tmp) { dprintk(NDEBUG_ABORT, "scsi%d : aborting disconnected command.\n", instance->host_no); - if (NCR5380_select(instance, cmd, (int) cmd->tag)) + if (NCR5380_select(instance, cmd)) return FAILED; dprintk(NDEBUG_ABORT, "scsi%d : nexus reestablished.\n", instance->host_no); do_abort(instance); - for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (prev = (struct scsi_cmnd **) &(hostdata->disconnected_queue), tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; prev = (struct scsi_cmnd **) &(tmp->host_scribble), tmp = (struct scsi_cmnd *) tmp->host_scribble) if (cmd == tmp) { REMOVE(5, *prev, tmp, tmp->host_scribble); - *prev = (Scsi_Cmnd *) tmp->host_scribble; + *prev = (struct scsi_cmnd *) tmp->host_scribble; tmp->host_scribble = NULL; tmp->result = DID_ABORT << 16; tmp->scsi_done(tmp); @@ -2806,7 +2726,7 @@ static int NCR5380_abort(Scsi_Cmnd * cmd) { /* - * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd) + * Function : int NCR5380_bus_reset (struct scsi_cmnd *cmd) * * Purpose : reset the SCSI bus. * @@ -2815,7 +2735,7 @@ static int NCR5380_abort(Scsi_Cmnd * cmd) { * Locks: host lock taken by caller */ -static int NCR5380_bus_reset(Scsi_Cmnd * cmd) +static int NCR5380_bus_reset(struct scsi_cmnd *cmd) { struct Scsi_Host *instance = cmd->device->host; diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h index c79ddfa6f53c..162112dd1bf8 100644 --- a/drivers/scsi/NCR5380.h +++ b/drivers/scsi/NCR5380.h @@ -7,8 +7,6 @@ * drew@colorado.edu * +1 (303) 666-5836 * - * DISTRIBUTION RELEASE 7 - * * For more information, please consult * * NCR 5380 Family @@ -25,13 +23,7 @@ #define NCR5380_H #include <linux/interrupt.h> - -#ifdef AUTOSENSE #include <scsi/scsi_eh.h> -#endif - -#define NCR5380_PUBLIC_RELEASE 7 -#define NCR53C400_PUBLIC_RELEASE 2 #define NDEBUG_ARBITRATION 0x1 #define NDEBUG_AUTOSENSE 0x2 @@ -224,33 +216,44 @@ #define DISCONNECT_LONG 2 /* - * These are "special" values for the tag parameter passed to NCR5380_select. + * "Special" value for the (unsigned char) command tag, to indicate + * I_T_L nexus instead of I_T_L_Q. */ -#define TAG_NEXT -1 /* Use next free tag */ -#define TAG_NONE -2 /* - * Establish I_T_L nexus instead of I_T_L_Q - * even on SCSI-II devices. - */ +#define TAG_NONE 0xff /* * These are "special" values for the irq and dma_channel fields of the * Scsi_Host structure */ -#define SCSI_IRQ_NONE 255 #define DMA_NONE 255 #define IRQ_AUTO 254 #define DMA_AUTO 254 #define PORT_AUTO 0xffff /* autoprobe io port for 53c400a */ +#ifndef NO_IRQ +#define NO_IRQ 0 +#endif + #define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */ #define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */ #define FLAG_NCR53C400 4 /* NCR53c400 */ #define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */ #define FLAG_DTC3181E 16 /* DTC3181E */ +#define FLAG_LATE_DMA_SETUP 32 /* Setup NCR before DMA H/W */ +#define FLAG_TAGGED_QUEUING 64 /* as X3T9.2 spelled it */ #ifndef ASM + +#ifdef SUPPORT_TAGS +struct tag_alloc { + DECLARE_BITMAP(allocated, MAX_TAGS); + int nr_allocated; + int queue_size; +}; +#endif + struct NCR5380_hostdata { NCR5380_implementation_fields; /* implementation specific */ struct Scsi_Host *host; /* Host backpointer */ @@ -263,9 +266,9 @@ struct NCR5380_hostdata { volatile int dma_len; /* requested length of DMA */ #endif volatile unsigned char last_message; /* last message OUT */ - volatile Scsi_Cmnd *connected; /* currently connected command */ - volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */ - volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */ + volatile struct scsi_cmnd *connected; /* currently connected command */ + volatile struct scsi_cmnd *issue_queue; /* waiting to be issued */ + volatile struct scsi_cmnd *disconnected_queue; /* waiting for reconnect */ volatile int restart_select; /* we have disconnected, used to restart NCR5380_select() */ @@ -273,19 +276,21 @@ struct NCR5380_hostdata { int flags; unsigned long time_expires; /* in jiffies, set prior to sleeping */ int select_time; /* timer in select for target response */ - volatile Scsi_Cmnd *selecting; + volatile struct scsi_cmnd *selecting; struct delayed_work coroutine; /* our co-routine */ -#ifdef NCR5380_STATS - unsigned timebase; /* Base for time calcs */ - long time_read[8]; /* time to do reads */ - long time_write[8]; /* time to do writes */ - unsigned long bytes_read[8]; /* bytes read */ - unsigned long bytes_write[8]; /* bytes written */ - unsigned pendingr; - unsigned pendingw; -#endif -#ifdef AUTOSENSE struct scsi_eh_save ses; + char info[256]; + int read_overruns; /* number of bytes to cut from a + * transfer to handle chip overruns */ + int retain_dma_intr; + struct work_struct main_task; + volatile int main_running; +#ifdef SUPPORT_TAGS + struct tag_alloc TagAlloc[8][8]; /* 8 targets and 8 LUNs */ +#endif +#ifdef PSEUDO_DMA + unsigned spin_max_r; + unsigned spin_max_w; #endif }; @@ -296,7 +301,8 @@ struct NCR5380_hostdata { #endif #define dprintk(flg, fmt, ...) \ - do { if ((NDEBUG) & (flg)) pr_debug(fmt, ## __VA_ARGS__); } while (0) + do { if ((NDEBUG) & (flg)) \ + printk(KERN_DEBUG fmt, ## __VA_ARGS__); } while (0) #if NDEBUG #define NCR5380_dprint(flg, arg) \ @@ -320,17 +326,9 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance); static irqreturn_t NCR5380_intr(int irq, void *dev_id); #endif static void NCR5380_main(struct work_struct *work); -static void __maybe_unused NCR5380_print_options(struct Scsi_Host *instance); -static int NCR5380_abort(Scsi_Cmnd * cmd); -static int NCR5380_bus_reset(Scsi_Cmnd * cmd); -static int NCR5380_queue_command(struct Scsi_Host *, struct scsi_cmnd *); -static int __maybe_unused NCR5380_show_info(struct seq_file *, - struct Scsi_Host *); -static int __maybe_unused NCR5380_write_info(struct Scsi_Host *instance, - char *buffer, int length); - +static const char *NCR5380_info(struct Scsi_Host *instance); static void NCR5380_reselect(struct Scsi_Host *instance); -static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag); +static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd); #if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL) static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); #endif diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 681434e2dfe9..b32e77db0c48 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -2181,7 +2181,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) (fsa_dev_ptr[cid].sense_data.sense_key == NOT_READY)) { switch (scsicmd->cmnd[0]) { - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: if (!(dev->raw_io_interface) || !(dev->raw_io_64) || ((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16)) @@ -2309,7 +2309,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) scsi_sg_copy_from_buffer(scsicmd, &inq_data, sizeof(inq_data)); return aac_get_container_name(scsicmd); } - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: if (!(dev->raw_io_interface) || !(dev->raw_io_64) || ((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16)) diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index a759cb2d4b15..fdcdf9f781bc 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -462,9 +462,9 @@ static int aac_slave_configure(struct scsi_device *sdev) depth = 256; else if (depth < 2) depth = 2; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth); + scsi_change_queue_depth(sdev, depth); } else - scsi_adjust_queue_depth(sdev, 0, 1); + scsi_change_queue_depth(sdev, 1); return 0; } @@ -478,12 +478,8 @@ static int aac_slave_configure(struct scsi_device *sdev) * total capacity and the queue depth supported by the target device. */ -static int aac_change_queue_depth(struct scsi_device *sdev, int depth, - int reason) +static int aac_change_queue_depth(struct scsi_device *sdev, int depth) { - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (sdev->tagged_supported && (sdev->type == TYPE_DISK) && (sdev_channel(sdev) == CONTAINER_CHANNEL)) { struct scsi_device * dev; @@ -504,10 +500,10 @@ static int aac_change_queue_depth(struct scsi_device *sdev, int depth, depth = 256; else if (depth < 2) depth = 2; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth); - } else - scsi_adjust_queue_depth(sdev, 0, 1); - return sdev->queue_depth; + return scsi_change_queue_depth(sdev, depth); + } + + return scsi_change_queue_depth(sdev, 1); } static ssize_t aac_show_raid_level(struct device *dev, struct device_attribute *attr, char *buf) @@ -555,7 +551,7 @@ static int aac_eh_abort(struct scsi_cmnd* cmd) AAC_DRIVERNAME, host->host_no, sdev_channel(dev), sdev_id(dev), dev->lun); switch (cmd->cmnd[0]) { - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: if (!(aac->raw_io_interface) || !(aac->raw_io_64) || ((cmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16)) diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 43761c1c46f0..6719a3390ebd 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -7706,7 +7706,7 @@ advansys_narrow_slave_configure(struct scsi_device *sdev, ASC_DVC_VAR *asc_dvc) asc_dvc->cfg->can_tagged_qng |= tid_bit; asc_dvc->use_tagged_qng |= tid_bit; } - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, + scsi_change_queue_depth(sdev, asc_dvc->max_dvc_qng[sdev->id]); } } else { @@ -7714,7 +7714,6 @@ advansys_narrow_slave_configure(struct scsi_device *sdev, ASC_DVC_VAR *asc_dvc) asc_dvc->cfg->can_tagged_qng &= ~tid_bit; asc_dvc->use_tagged_qng &= ~tid_bit; } - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); } if ((sdev->lun == 0) && @@ -7848,12 +7847,8 @@ advansys_wide_slave_configure(struct scsi_device *sdev, ADV_DVC_VAR *adv_dvc) } } - if ((adv_dvc->tagqng_able & tidmask) && sdev->tagged_supported) { - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, - adv_dvc->max_dvc_qng); - } else { - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); - } + if ((adv_dvc->tagqng_able & tidmask) && sdev->tagged_supported) + scsi_change_queue_depth(sdev, adv_dvc->max_dvc_qng); } /* diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index e77b72f78006..2b960b326daf 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -230,7 +230,7 @@ * * ************************************************************************** - + see Documentation/scsi/aha152x.txt for configuration details **************************************************************************/ @@ -279,45 +279,11 @@ static LIST_HEAD(aha152x_host_list); #error define AUTOCONF or SETUP0 #endif -#if defined(AHA152X_DEBUG) -#define DEBUG_DEFAULT debug_eh - -#define DPRINTK(when,msgs...) \ - do { if(HOSTDATA(shpnt)->debug & (when)) printk(msgs); } while(0) - -#define DO_LOCK(flags) \ - do { \ - if(spin_is_locked(&QLOCK)) { \ - DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \ - } \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \ - spin_lock_irqsave(&QLOCK,flags); \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \ - QLOCKER=__func__; \ - QLOCKERL=__LINE__; \ - } while(0) - -#define DO_UNLOCK(flags) \ - do { \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \ - spin_unlock_irqrestore(&QLOCK,flags); \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \ - QLOCKER="(not locked)"; \ - QLOCKERL=0; \ - } while(0) - -#else -#define DPRINTK(when,msgs...) #define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags) #define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags) -#endif #define LEAD "(scsi%d:%d:%d) " -#define WARN_LEAD KERN_WARNING LEAD #define INFO_LEAD KERN_INFO LEAD -#define NOTE_LEAD KERN_NOTICE LEAD -#define ERR_LEAD KERN_ERR LEAD -#define DEBUG_LEAD KERN_DEBUG LEAD #define CMDINFO(cmd) \ (cmd) ? ((cmd)->device->host->host_no) : -1, \ (cmd) ? ((cmd)->device->id & 0x0f) : -1, \ @@ -345,10 +311,10 @@ CMD_INC_RESID(struct scsi_cmnd *cmd, int inc) enum { not_issued = 0x0001, /* command not yet issued */ - selecting = 0x0002, /* target is beeing selected */ + selecting = 0x0002, /* target is being selected */ identified = 0x0004, /* IDENTIFY was sent */ disconnected = 0x0008, /* target disconnected */ - completed = 0x0010, /* target sent COMMAND COMPLETE */ + completed = 0x0010, /* target sent COMMAND COMPLETE */ aborted = 0x0020, /* ABORT was sent */ resetted = 0x0040, /* BUS DEVICE RESET was sent */ spiordy = 0x0080, /* waiting for SPIORDY to raise */ @@ -396,7 +362,6 @@ static int exttrans[] = {0, 0}; module_param_array(exttrans, int, NULL, 0); MODULE_PARM_DESC(exttrans,"use extended translation"); -#if !defined(AHA152X_DEBUG) static int aha152x[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0}; module_param_array(aha152x, int, NULL, 0); MODULE_PARM_DESC(aha152x, "parameters for first controller"); @@ -404,19 +369,6 @@ MODULE_PARM_DESC(aha152x, "parameters for first controller"); static int aha152x1[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0}; module_param_array(aha152x1, int, NULL, 0); MODULE_PARM_DESC(aha152x1, "parameters for second controller"); -#else -static int debug[] = {DEBUG_DEFAULT, DEBUG_DEFAULT}; -module_param_array(debug, int, NULL, 0); -MODULE_PARM_DESC(debug, "flags for driver debugging"); - -static int aha152x[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT}; -module_param_array(aha152x, int, NULL, 0); -MODULE_PARM_DESC(aha152x, "parameters for first controller"); - -static int aha152x1[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT}; -module_param_array(aha152x1, int, NULL, 0); -MODULE_PARM_DESC(aha152x1, "parameters for second controller"); -#endif /* !defined(AHA152X_DEBUG) */ #endif /* MODULE */ #ifdef __ISAPNP__ @@ -446,7 +398,7 @@ static struct scsi_host_template aha152x_driver_template; /* * internal states of the host * - */ + */ enum aha152x_state { idle=0, unknown, @@ -485,24 +437,16 @@ struct aha152x_hostdata { spinlock_t lock; /* host lock */ -#if defined(AHA152X_DEBUG) - const char *locker; - /* which function has the lock */ - int lockerl; /* where did it get it */ - - int debug; /* current debugging setting */ -#endif - #if defined(AHA152X_STAT) - int total_commands; + int total_commands; int disconnections; int busfree_without_any_action; int busfree_without_old_command; int busfree_without_new_command; int busfree_without_done_command; int busfree_with_check_condition; - int count[maxstate]; - int count_trans[maxstate]; + int count[maxstate]; + int count_trans[maxstate]; unsigned long time[maxstate]; #endif @@ -514,7 +458,7 @@ struct aha152x_hostdata { int delay; /* reset out delay */ int ext_trans; /* extended translation enabled */ - int swint; /* software-interrupt was fired during detect() */ + int swint; /* software-interrupt was fired during detect() */ int service; /* bh needs to be run */ int in_intr; /* bh is running */ @@ -543,7 +487,7 @@ struct aha152x_hostdata { unsigned char msgi[256]; /* received message bytes */ - int msgo_i, msgo_len; + int msgo_i, msgo_len; /* number of sent bytes and length of current messages */ unsigned char msgo[256]; /* pending messages */ @@ -689,7 +633,6 @@ static void aha152x_error(struct Scsi_Host *shpnt, char *msg); static void done(struct Scsi_Host *shpnt, int error); /* diagnostics */ -static void disp_ports(struct Scsi_Host *shpnt); static void show_command(Scsi_Cmnd * ptr); static void show_queues(struct Scsi_Host *shpnt); static void disp_enintr(struct Scsi_Host *shpnt); @@ -812,10 +755,6 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup) DELAY = setup->delay; EXT_TRANS = setup->ext_trans; -#if defined(AHA152X_DEBUG) - HOSTDATA(shpnt)->debug = setup->debug; -#endif - SETPORT(SCSIID, setup->scsiid << 4); shpnt->this_id = setup->scsiid; @@ -941,31 +880,24 @@ void aha152x_release(struct Scsi_Host *shpnt) * setup controller to generate interrupts depending * on current state (lock has to be acquired) * - */ + */ static int setup_expected_interrupts(struct Scsi_Host *shpnt) { if(CURRENT_SC) { CURRENT_SC->SCp.phase |= 1 << 16; - + if(CURRENT_SC->SCp.phase & selecting) { - DPRINTK(debug_intr, DEBUG_LEAD "expecting: (seldo) (seltimo) (seldi)\n", CMDINFO(CURRENT_SC)); SETPORT(SSTAT1, SELTO); SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0)); SETPORT(SIMODE1, ENSELTIMO); } else { - DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (busfree) %s\n", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.phase & spiordy ? "(spiordy)" : ""); SETPORT(SIMODE0, (CURRENT_SC->SCp.phase & spiordy) ? ENSPIORDY : 0); - SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); + SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); } } else if(STATE==seldi) { - DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (identify)\n", CMDINFO(CURRENT_SC)); SETPORT(SIMODE0, 0); - SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); + SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); } else { - DPRINTK(debug_intr, DEBUG_LEAD "expecting: %s %s\n", - CMDINFO(CURRENT_SC), - DISCONNECTED_SC ? "(reselection)" : "", - ISSUE_SC ? "(busfree)" : ""); SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0); SETPORT(SIMODE1, ENSCSIRST | ( (ISSUE_SC||DONE_SC) ? ENBUSFREE : 0)); } @@ -977,7 +909,7 @@ static int setup_expected_interrupts(struct Scsi_Host *shpnt) } -/* +/* * Queue a command and setup interrupts for a free bus. */ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete, @@ -986,15 +918,6 @@ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete, struct Scsi_Host *shpnt = SCpnt->device->host; unsigned long flags; -#if defined(AHA152X_DEBUG) - if (HOSTDATA(shpnt)->debug & debug_queue) { - printk(INFO_LEAD "queue: %p; cmd_len=%d pieces=%d size=%u cmnd=", - CMDINFO(SCpnt), SCpnt, SCpnt->cmd_len, - scsi_sg_count(SCpnt), scsi_bufflen(SCpnt)); - __scsi_print_command(SCpnt->cmnd); - } -#endif - SCpnt->scsi_done = done; SCpnt->SCp.phase = not_issued | phase; SCpnt->SCp.Status = 0x1; /* Ilegal status by SCSI standard */ @@ -1004,13 +927,13 @@ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete, if(SCpnt->SCp.phase & (resetting|check_condition)) { if (!SCpnt->host_scribble || SCSEM(SCpnt) || SCNEXT(SCpnt)) { - printk(ERR_LEAD "cannot reuse command\n", CMDINFO(SCpnt)); + scmd_printk(KERN_ERR, SCpnt, "cannot reuse command\n"); return FAILED; } } else { SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC); if(!SCpnt->host_scribble) { - printk(ERR_LEAD "allocation failed\n", CMDINFO(SCpnt)); + scmd_printk(KERN_ERR, SCpnt, "allocation failed\n"); return FAILED; } } @@ -1066,15 +989,6 @@ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete, */ static int aha152x_queue_lck(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { -#if 0 - if(*SCpnt->cmnd == REQUEST_SENSE) { - SCpnt->result = 0; - done(SCpnt); - - return 0; - } -#endif - return aha152x_internal_queue(SCpnt, NULL, 0, done); } @@ -1082,15 +996,10 @@ static DEF_SCSI_QCMD(aha152x_queue) /* - * * */ static void reset_done(Scsi_Cmnd *SCpnt) { -#if 0 - struct Scsi_Host *shpnt = SCpnt->host; - DPRINTK(debug_eh, INFO_LEAD "reset_done called\n", CMDINFO(SCpnt)); -#endif if(SCSEM(SCpnt)) { complete(SCSEM(SCpnt)); } else { @@ -1108,20 +1017,11 @@ static int aha152x_abort(Scsi_Cmnd *SCpnt) Scsi_Cmnd *ptr; unsigned long flags; -#if defined(AHA152X_DEBUG) - if(HOSTDATA(shpnt)->debug & debug_eh) { - printk(DEBUG_LEAD "abort(%p)", CMDINFO(SCpnt), SCpnt); - show_queues(shpnt); - } -#endif - DO_LOCK(flags); ptr=remove_SC(&ISSUE_SC, SCpnt); if(ptr) { - DPRINTK(debug_eh, DEBUG_LEAD "not yet issued - SUCCESS\n", CMDINFO(SCpnt)); - HOSTDATA(shpnt)->commands--; if (!HOSTDATA(shpnt)->commands) SETPORT(PORTA, 0); @@ -1131,7 +1031,7 @@ static int aha152x_abort(Scsi_Cmnd *SCpnt) SCpnt->host_scribble=NULL; return SUCCESS; - } + } DO_UNLOCK(flags); @@ -1142,7 +1042,8 @@ static int aha152x_abort(Scsi_Cmnd *SCpnt) * */ - printk(ERR_LEAD "cannot abort running or disconnected command\n", CMDINFO(SCpnt)); + scmd_printk(KERN_ERR, SCpnt, + "cannot abort running or disconnected command\n"); return FAILED; } @@ -1160,15 +1061,8 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt) unsigned long flags; unsigned long timeleft; -#if defined(AHA152X_DEBUG) - if(HOSTDATA(shpnt)->debug & debug_eh) { - printk(INFO_LEAD "aha152x_device_reset(%p)", CMDINFO(SCpnt), SCpnt); - show_queues(shpnt); - } -#endif - if(CURRENT_SC==SCpnt) { - printk(ERR_LEAD "cannot reset current device\n", CMDINFO(SCpnt)); + scmd_printk(KERN_ERR, SCpnt, "cannot reset current device\n"); return FAILED; } @@ -1208,7 +1102,7 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt) } else if(disconnected) { append_SC(&DISCONNECTED_SC, SCpnt); } - + ret = FAILED; } @@ -1227,12 +1121,12 @@ static void free_hard_reset_SCs(struct Scsi_Host *shpnt, Scsi_Cmnd **SCs) if(SCDATA(ptr)) { next = SCNEXT(ptr); } else { - printk(DEBUG_LEAD "queue corrupted at %p\n", CMDINFO(ptr), ptr); + scmd_printk(KERN_DEBUG, ptr, + "queue corrupted at %p\n", ptr); next = NULL; } if (!ptr->device->soft_reset) { - DPRINTK(debug_eh, DEBUG_LEAD "disconnected command %p removed\n", CMDINFO(ptr), ptr); remove_SC(SCs, ptr); HOSTDATA(shpnt)->commands--; kfree(ptr->host_scribble); @@ -1253,25 +1147,14 @@ static int aha152x_bus_reset_host(struct Scsi_Host *shpnt) DO_LOCK(flags); -#if defined(AHA152X_DEBUG) - if(HOSTDATA(shpnt)->debug & debug_eh) { - printk(KERN_DEBUG "scsi%d: bus reset", shpnt->host_no); - show_queues(shpnt); - } -#endif - free_hard_reset_SCs(shpnt, &ISSUE_SC); free_hard_reset_SCs(shpnt, &DISCONNECTED_SC); - DPRINTK(debug_eh, KERN_DEBUG "scsi%d: resetting bus\n", shpnt->host_no); - SETPORT(SCSISEQ, SCSIRSTO); mdelay(256); SETPORT(SCSISEQ, 0); mdelay(DELAY); - DPRINTK(debug_eh, KERN_DEBUG "scsi%d: bus resetted\n", shpnt->host_no); - setup_expected_interrupts(shpnt); if(HOSTDATA(shpnt)->commands==0) SETPORT(PORTA, 0); @@ -1333,11 +1216,7 @@ static void reset_ports(struct Scsi_Host *shpnt) */ int aha152x_host_reset_host(struct Scsi_Host *shpnt) { - DPRINTK(debug_eh, KERN_DEBUG "scsi%d: host reset\n", shpnt->host_no); - aha152x_bus_reset_host(shpnt); - - DPRINTK(debug_eh, KERN_DEBUG "scsi%d: resetting ports\n", shpnt->host_no); reset_ports(shpnt); return SUCCESS; @@ -1345,7 +1224,7 @@ int aha152x_host_reset_host(struct Scsi_Host *shpnt) /* * Reset the host (bus and controller) - * + * */ static int aha152x_host_reset(Scsi_Cmnd *SCpnt) { @@ -1411,7 +1290,9 @@ static void done(struct Scsi_Host *shpnt, int error) { if (CURRENT_SC) { if(DONE_SC) - printk(ERR_LEAD "there's already a completed command %p - will cause abort\n", CMDINFO(CURRENT_SC), DONE_SC); + scmd_printk(KERN_ERR, CURRENT_SC, + "there's already a completed command %p " + "- will cause abort\n", DONE_SC); DONE_SC = CURRENT_SC; CURRENT_SC = NULL; @@ -1466,7 +1347,7 @@ static irqreturn_t intr(int irqno, void *dev_id) return IRQ_NONE; if( TESTLO(DMASTAT, INTSTAT) ) - return IRQ_NONE; + return IRQ_NONE; /* no more interrupts from the controller, while we're busy. INTEN is restored by the BH handler */ @@ -1501,7 +1382,7 @@ static void busfree_run(struct Scsi_Host *shpnt) SETPORT(SXFRCTL0, CH1); SETPORT(SSTAT1, CLRBUSFREE); - + if(CURRENT_SC) { #if defined(AHA152X_STAT) action++; @@ -1513,19 +1394,13 @@ static void busfree_run(struct Scsi_Host *shpnt) done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16)); } else if(CURRENT_SC->SCp.phase & aborted) { - DPRINTK(debug_eh, DEBUG_LEAD "ABORT sent\n", CMDINFO(CURRENT_SC)); done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_ABORT << 16)); } else if(CURRENT_SC->SCp.phase & resetted) { - DPRINTK(debug_eh, DEBUG_LEAD "BUS DEVICE RESET sent\n", CMDINFO(CURRENT_SC)); done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_RESET << 16)); } else if(CURRENT_SC->SCp.phase & disconnected) { /* target sent DISCONNECT */ - DPRINTK(debug_selection, DEBUG_LEAD "target disconnected at %d/%d\n", - CMDINFO(CURRENT_SC), - scsi_get_resid(CURRENT_SC), - scsi_bufflen(CURRENT_SC)); #if defined(AHA152X_STAT) HOSTDATA(shpnt)->disconnections++; #endif @@ -1553,13 +1428,6 @@ static void busfree_run(struct Scsi_Host *shpnt) struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC; struct aha152x_scdata *sc = SCDATA(cmd); -#if 0 - if(HOSTDATA(shpnt)->debug & debug_eh) { - printk(ERR_LEAD "received sense: ", CMDINFO(DONE_SC)); - scsi_print_sense("bh", DONE_SC); - } -#endif - scsi_eh_restore_cmnd(cmd, &sc->ses); cmd->SCp.Status = SAM_STAT_CHECK_CONDITION; @@ -1571,17 +1439,11 @@ static void busfree_run(struct Scsi_Host *shpnt) #if defined(AHA152X_STAT) HOSTDATA(shpnt)->busfree_with_check_condition++; #endif -#if 0 - DPRINTK(debug_eh, ERR_LEAD "CHECK CONDITION found\n", CMDINFO(DONE_SC)); -#endif if(!(DONE_SC->SCp.phase & not_issued)) { struct aha152x_scdata *sc; Scsi_Cmnd *ptr = DONE_SC; DONE_SC=NULL; -#if 0 - DPRINTK(debug_eh, ERR_LEAD "requesting sense\n", CMDINFO(ptr)); -#endif sc = SCDATA(ptr); /* It was allocated in aha152x_internal_queue? */ @@ -1591,19 +1453,10 @@ static void busfree_run(struct Scsi_Host *shpnt) DO_UNLOCK(flags); aha152x_internal_queue(ptr, NULL, check_condition, ptr->scsi_done); DO_LOCK(flags); -#if 0 - } else { - DPRINTK(debug_eh, ERR_LEAD "command not issued - CHECK CONDITION ignored\n", CMDINFO(DONE_SC)); -#endif } } if(DONE_SC && DONE_SC->scsi_done) { -#if defined(AHA152X_DEBUG) - int hostno=DONE_SC->device->host->host_no; - int id=DONE_SC->device->id & 0xf; - int lun=((u8)DONE_SC->device->lun) & 0x7; -#endif Scsi_Cmnd *ptr = DONE_SC; DONE_SC=NULL; @@ -1618,9 +1471,7 @@ static void busfree_run(struct Scsi_Host *shpnt) } DO_UNLOCK(flags); - DPRINTK(debug_done, DEBUG_LEAD "calling scsi_done(%p)\n", hostno, id, lun, ptr); - ptr->scsi_done(ptr); - DPRINTK(debug_done, DEBUG_LEAD "scsi_done(%p) returned\n", hostno, id, lun, ptr); + ptr->scsi_done(ptr); DO_LOCK(flags); } @@ -1640,9 +1491,7 @@ static void busfree_run(struct Scsi_Host *shpnt) #if defined(AHA152X_STAT) action++; #endif - CURRENT_SC->SCp.phase |= selecting; - - DPRINTK(debug_selection, DEBUG_LEAD "selecting target\n", CMDINFO(CURRENT_SC)); + CURRENT_SC->SCp.phase |= selecting; /* clear selection timeout */ SETPORT(SSTAT1, SELTO); @@ -1674,18 +1523,19 @@ static void seldo_run(struct Scsi_Host *shpnt) SETPORT(SSTAT1, CLRBUSFREE); SETPORT(SSTAT1, CLRPHASECHG); - CURRENT_SC->SCp.phase &= ~(selecting|not_issued); + CURRENT_SC->SCp.phase &= ~(selecting|not_issued); SETPORT(SCSISEQ, 0); if (TESTLO(SSTAT0, SELDO)) { - printk(ERR_LEAD "aha152x: passing bus free condition\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_ERR, CURRENT_SC, + "aha152x: passing bus free condition\n"); done(shpnt, DID_NO_CONNECT << 16); return; } SETPORT(SSTAT0, CLRSELDO); - + ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun)); if (CURRENT_SC->SCp.phase & aborting) { @@ -1693,7 +1543,7 @@ static void seldo_run(struct Scsi_Host *shpnt) } else if (CURRENT_SC->SCp.phase & resetting) { ADDMSGO(BUS_DEVICE_RESET); } else if (SYNCNEG==0 && SYNCHRONOUS) { - CURRENT_SC->SCp.phase |= syncneg; + CURRENT_SC->SCp.phase |= syncneg; MSGOLEN += spi_populate_sync_msg(&MSGO(MSGOLEN), 50, 8); SYNCNEG=1; /* negotiation in progress */ } @@ -1708,29 +1558,21 @@ static void seldo_run(struct Scsi_Host *shpnt) */ static void selto_run(struct Scsi_Host *shpnt) { - SETPORT(SCSISEQ, 0); + SETPORT(SCSISEQ, 0); SETPORT(SSTAT1, CLRSELTIMO); - DPRINTK(debug_selection, DEBUG_LEAD "selection timeout\n", CMDINFO(CURRENT_SC)); - - if(!CURRENT_SC) { - DPRINTK(debug_selection, DEBUG_LEAD "!CURRENT_SC\n", CMDINFO(CURRENT_SC)); + if (!CURRENT_SC) return; - } - CURRENT_SC->SCp.phase &= ~selecting; + CURRENT_SC->SCp.phase &= ~selecting; - if (CURRENT_SC->SCp.phase & aborted) { - DPRINTK(debug_selection, DEBUG_LEAD "aborted\n", CMDINFO(CURRENT_SC)); + if (CURRENT_SC->SCp.phase & aborted) done(shpnt, DID_ABORT << 16); - } else if (TESTLO(SSTAT0, SELINGO)) { - DPRINTK(debug_selection, DEBUG_LEAD "arbitration not won\n", CMDINFO(CURRENT_SC)); + else if (TESTLO(SSTAT0, SELINGO)) done(shpnt, DID_BUS_BUSY << 16); - } else { + else /* ARBITRATION won, but SELECTION failed */ - DPRINTK(debug_selection, DEBUG_LEAD "selection failed\n", CMDINFO(CURRENT_SC)); done(shpnt, DID_NO_CONNECT << 16); - } } /* @@ -1753,9 +1595,8 @@ static void seldi_run(struct Scsi_Host *shpnt) if(CURRENT_SC) { if(!(CURRENT_SC->SCp.phase & not_issued)) - printk(ERR_LEAD "command should not have been issued yet\n", CMDINFO(CURRENT_SC)); - - DPRINTK(debug_selection, ERR_LEAD "command requeued - reselection\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_ERR, CURRENT_SC, + "command should not have been issued yet\n"); DO_LOCK(flags); append_SC(&ISSUE_SC, CURRENT_SC); @@ -1764,17 +1605,16 @@ static void seldi_run(struct Scsi_Host *shpnt) CURRENT_SC = NULL; } - if(!DISCONNECTED_SC) { - DPRINTK(debug_selection, DEBUG_LEAD "unexpected SELDI ", CMDINFO(CURRENT_SC)); + if (!DISCONNECTED_SC) return; - } RECONN_TARGET=-1; selid = GETPORT(SELID) & ~(1 << shpnt->this_id); if (selid==0) { - printk("aha152x%d: target id unknown (%02x)\n", HOSTNO, selid); + shost_printk(KERN_INFO, shpnt, + "target id unknown (%02x)\n", selid); return; } @@ -1782,8 +1622,8 @@ static void seldi_run(struct Scsi_Host *shpnt) ; if(selid & ~(1 << target)) { - printk("aha152x%d: multiple targets reconnected (%02x)\n", - HOSTNO, selid); + shost_printk(KERN_INFO, shpnt, + "multiple targets reconnected (%02x)\n", selid); } @@ -1793,7 +1633,6 @@ static void seldi_run(struct Scsi_Host *shpnt) SETRATE(HOSTDATA(shpnt)->syncrate[target]); RECONN_TARGET=target; - DPRINTK(debug_selection, DEBUG_LEAD "target %d reselected (%02x).\n", CMDINFO(CURRENT_SC), target, selid); } /* @@ -1817,31 +1656,24 @@ static void msgi_run(struct Scsi_Host *shpnt) if(sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT)) return; - if(TESTLO(SSTAT0,SPIORDY)) { - DPRINTK(debug_msgi, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC)); + if (TESTLO(SSTAT0, SPIORDY)) return; - } ADDMSGI(GETPORT(SCSIDAT)); -#if defined(AHA152X_DEBUG) - if (HOSTDATA(shpnt)->debug & debug_msgi) { - printk(INFO_LEAD "inbound message %02x ", CMDINFO(CURRENT_SC), MSGI(0)); - spi_print_msg(&MSGI(0)); - printk("\n"); - } -#endif - if(!CURRENT_SC) { if(LASTSTATE!=seldi) { - printk(KERN_ERR "aha152x%d: message in w/o current command not after reselection\n", HOSTNO); + shost_printk(KERN_ERR, shpnt, + "message in w/o current command" + " not after reselection\n"); } /* - * Handle reselection - */ + * Handle reselection + */ if(!(MSGI(0) & IDENTIFY_BASE)) { - printk(KERN_ERR "aha152x%d: target didn't identify after reselection\n", HOSTNO); + shost_printk(KERN_ERR, shpnt, + "target didn't identify after reselection\n"); continue; } @@ -1849,12 +1681,13 @@ static void msgi_run(struct Scsi_Host *shpnt) if (!CURRENT_SC) { show_queues(shpnt); - printk(KERN_ERR "aha152x%d: no disconnected command for target %d/%d\n", HOSTNO, RECONN_TARGET, MSGI(0) & 0x3f); + shost_printk(KERN_ERR, shpnt, + "no disconnected command" + " for target %d/%d\n", + RECONN_TARGET, MSGI(0) & 0x3f); continue; } - DPRINTK(debug_msgi, DEBUG_LEAD "target reconnected\n", CMDINFO(CURRENT_SC)); - CURRENT_SC->SCp.Message = MSGI(0); CURRENT_SC->SCp.phase &= ~disconnected; @@ -1862,31 +1695,32 @@ static void msgi_run(struct Scsi_Host *shpnt) /* next message if any */ continue; - } + } CURRENT_SC->SCp.Message = MSGI(0); switch (MSGI(0)) { case DISCONNECT: if (!RECONNECT) - printk(WARN_LEAD "target was not allowed to disconnect\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_WARNING, CURRENT_SC, + "target was not allowed to disconnect\n"); CURRENT_SC->SCp.phase |= disconnected; break; case COMMAND_COMPLETE: - if(CURRENT_SC->SCp.phase & completed) - DPRINTK(debug_msgi, DEBUG_LEAD "again COMMAND COMPLETE\n", CMDINFO(CURRENT_SC)); - CURRENT_SC->SCp.phase |= completed; break; case MESSAGE_REJECT: if (SYNCNEG==1) { - printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_INFO, CURRENT_SC, + "Synchronous Data Transfer Request" + " was rejected\n"); SYNCNEG=2; /* negotiation completed */ } else - printk(INFO_LEAD "inbound message (MESSAGE REJECT)\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_INFO, CURRENT_SC, + "inbound message (MESSAGE REJECT)\n"); break; case SAVE_POINTERS: @@ -1907,7 +1741,8 @@ static void msgi_run(struct Scsi_Host *shpnt) long ticks; if (MSGI(1) != 3) { - printk(ERR_LEAD "SDTR message length!=3\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_ERR, CURRENT_SC, + "SDTR message length!=3\n"); break; } @@ -1924,10 +1759,12 @@ static void msgi_run(struct Scsi_Host *shpnt) /* negotiation in progress */ if (ticks > 9 || MSGI(4) < 1 || MSGI(4) > 8) { ADDMSGO(MESSAGE_REJECT); - printk(INFO_LEAD "received Synchronous Data Transfer Request invalid - rejected\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_INFO, + CURRENT_SC, + "received Synchronous Data Transfer Request invalid - rejected\n"); break; } - + SYNCRATE |= ((ticks - 2) << 4) + MSGI(4); } else if (ticks <= 9 && MSGI(4) >= 1) { ADDMSGO(EXTENDED_MESSAGE); @@ -1947,11 +1784,14 @@ static void msgi_run(struct Scsi_Host *shpnt) SYNCRATE |= ((ticks - 2) << 4) + MSGI(4); } else { /* requested SDTR is too slow, do it asynchronously */ - printk(INFO_LEAD "Synchronous Data Transfer Request too slow - Rejecting\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_INFO, + CURRENT_SC, + "Synchronous Data Transfer Request too slow - Rejecting\n"); ADDMSGO(MESSAGE_REJECT); } - SYNCNEG=2; /* negotiation completed */ + /* negotiation completed */ + SYNCNEG=2; SETRATE(SYNCRATE); } break; @@ -1985,12 +1825,12 @@ static void msgi_run(struct Scsi_Host *shpnt) static void msgi_end(struct Scsi_Host *shpnt) { if(MSGILEN>0) - printk(WARN_LEAD "target left before message completed (%d)\n", CMDINFO(CURRENT_SC), MSGILEN); + scmd_printk(KERN_WARNING, CURRENT_SC, + "target left before message completed (%d)\n", + MSGILEN); - if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE)) { - DPRINTK(debug_msgi, DEBUG_LEAD "msgo pending\n", CMDINFO(CURRENT_SC)); + if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE)) SETPORT(SCSISIG, P_MSGI | SIG_ATNO); - } } /* @@ -2003,21 +1843,12 @@ static void msgo_init(struct Scsi_Host *shpnt) if((CURRENT_SC->SCp.phase & syncneg) && SYNCNEG==2 && SYNCRATE==0) { ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun)); } else { - printk(INFO_LEAD "unexpected MESSAGE OUT phase; rejecting\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_INFO, CURRENT_SC, + "unexpected MESSAGE OUT phase; rejecting\n"); ADDMSGO(MESSAGE_REJECT); } } -#if defined(AHA152X_DEBUG) - if(HOSTDATA(shpnt)->debug & debug_msgo) { - int i; - - printk(DEBUG_LEAD "messages( ", CMDINFO(CURRENT_SC)); - for (i=0; i<MSGOLEN; i+=spi_print_msg(&MSGO(i)), printk(" ")) - ; - printk(")\n"); - } -#endif } /* @@ -2026,16 +1857,9 @@ static void msgo_init(struct Scsi_Host *shpnt) */ static void msgo_run(struct Scsi_Host *shpnt) { - if(MSGO_I==MSGOLEN) - DPRINTK(debug_msgo, DEBUG_LEAD "messages all sent (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN); - while(MSGO_I<MSGOLEN) { - DPRINTK(debug_msgo, DEBUG_LEAD "message byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO(MSGO_I), MSGO_I, MSGOLEN); - - if(TESTLO(SSTAT0, SPIORDY)) { - DPRINTK(debug_msgo, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC)); + if (TESTLO(SSTAT0, SPIORDY)) return; - } if (MSGO_I==MSGOLEN-1) { /* Leave MESSAGE OUT after transfer */ @@ -2059,36 +1883,33 @@ static void msgo_run(struct Scsi_Host *shpnt) static void msgo_end(struct Scsi_Host *shpnt) { if(MSGO_I<MSGOLEN) { - printk(ERR_LEAD "message sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN); + scmd_printk(KERN_ERR, CURRENT_SC, + "message sent incompletely (%d/%d)\n", + MSGO_I, MSGOLEN); if(SYNCNEG==1) { - printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_INFO, CURRENT_SC, + "Synchronous Data Transfer Request was rejected\n"); SYNCNEG=2; } } - + MSGO_I = 0; MSGOLEN = 0; } -/* +/* * command phase * */ static void cmd_init(struct Scsi_Host *shpnt) { if (CURRENT_SC->SCp.sent_command) { - printk(ERR_LEAD "command already sent\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_ERR, CURRENT_SC, + "command already sent\n"); done(shpnt, DID_ERROR << 16); return; } -#if defined(AHA152X_DEBUG) - if (HOSTDATA(shpnt)->debug & debug_cmd) { - printk(DEBUG_LEAD "cmd_init: ", CMDINFO(CURRENT_SC)); - __scsi_print_command(CURRENT_SC->cmnd); - } -#endif - CMD_I=0; } @@ -2098,18 +1919,9 @@ static void cmd_init(struct Scsi_Host *shpnt) */ static void cmd_run(struct Scsi_Host *shpnt) { - if(CMD_I==CURRENT_SC->cmd_len) { - DPRINTK(debug_cmd, DEBUG_LEAD "command already completely sent (%d/%d)", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len); - disp_ports(shpnt); - } - while(CMD_I<CURRENT_SC->cmd_len) { - DPRINTK(debug_cmd, DEBUG_LEAD "command byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), CURRENT_SC->cmnd[CMD_I], CMD_I, CURRENT_SC->cmd_len); - - if(TESTLO(SSTAT0, SPIORDY)) { - DPRINTK(debug_cmd, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC)); + if (TESTLO(SSTAT0, SPIORDY)) return; - } SETPORT(SCSIDAT, CURRENT_SC->cmnd[CMD_I++]); } @@ -2118,7 +1930,9 @@ static void cmd_run(struct Scsi_Host *shpnt) static void cmd_end(struct Scsi_Host *shpnt) { if(CMD_I<CURRENT_SC->cmd_len) - printk(ERR_LEAD "command sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len); + scmd_printk(KERN_ERR, CURRENT_SC, + "command sent incompletely (%d/%d)\n", + CMD_I, CURRENT_SC->cmd_len); else CURRENT_SC->SCp.sent_command++; } @@ -2129,20 +1943,11 @@ static void cmd_end(struct Scsi_Host *shpnt) */ static void status_run(struct Scsi_Host *shpnt) { - if(TESTLO(SSTAT0,SPIORDY)) { - DPRINTK(debug_status, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC)); + if (TESTLO(SSTAT0, SPIORDY)) return; - } CURRENT_SC->SCp.Status = GETPORT(SCSIDAT); -#if defined(AHA152X_DEBUG) - if (HOSTDATA(shpnt)->debug & debug_status) { - printk(DEBUG_LEAD "inbound status %02x ", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.Status); - scsi_print_status(CURRENT_SC->SCp.Status); - printk("\n"); - } -#endif } /* @@ -2161,10 +1966,6 @@ static void datai_init(struct Scsi_Host *shpnt) SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE); DATA_LEN=0; - DPRINTK(debug_datai, - DEBUG_LEAD "datai_init: request_bufflen=%d resid=%d\n", - CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC), - scsi_get_resid(CURRENT_SC)); } static void datai_run(struct Scsi_Host *shpnt) @@ -2186,8 +1987,7 @@ static void datai_run(struct Scsi_Host *shpnt) barrier(); if(TESTLO(DMASTAT, DFIFOFULL|INTSTAT)) { - printk(ERR_LEAD "datai timeout", CMDINFO(CURRENT_SC)); - disp_ports(shpnt); + scmd_printk(KERN_ERR, CURRENT_SC, "datai timeout\n"); break; } @@ -2199,8 +1999,8 @@ static void datai_run(struct Scsi_Host *shpnt) barrier(); if(TESTLO(SSTAT2, SEMPTY)) { - printk(ERR_LEAD "datai sempty timeout", CMDINFO(CURRENT_SC)); - disp_ports(shpnt); + scmd_printk(KERN_ERR, CURRENT_SC, + "datai sempty timeout"); break; } @@ -2209,48 +2009,49 @@ static void datai_run(struct Scsi_Host *shpnt) if(CURRENT_SC->SCp.this_residual>0) { while(fifodata>0 && CURRENT_SC->SCp.this_residual>0) { - data_count = fifodata>CURRENT_SC->SCp.this_residual ? + data_count = fifodata > CURRENT_SC->SCp.this_residual ? CURRENT_SC->SCp.this_residual : fifodata; fifodata -= data_count; - if(data_count & 1) { - DPRINTK(debug_datai, DEBUG_LEAD "8bit\n", CMDINFO(CURRENT_SC)); - SETPORT(DMACNTRL0, ENDMA|_8BIT); - *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT); - CURRENT_SC->SCp.this_residual--; - DATA_LEN++; - SETPORT(DMACNTRL0, ENDMA); - } - - if(data_count > 1) { - DPRINTK(debug_datai, DEBUG_LEAD "16bit(%d)\n", CMDINFO(CURRENT_SC), data_count); - data_count >>= 1; - insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count); - CURRENT_SC->SCp.ptr += 2 * data_count; - CURRENT_SC->SCp.this_residual -= 2 * data_count; - DATA_LEN += 2 * data_count; - } - - if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) { - /* advance to next buffer */ - CURRENT_SC->SCp.buffers_residual--; - CURRENT_SC->SCp.buffer++; - CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer); - CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length; - } - } - } else if(fifodata>0) { - printk(ERR_LEAD "no buffers left for %d(%d) bytes (data overrun!?)\n", CMDINFO(CURRENT_SC), fifodata, GETPORT(FIFOSTAT)); - SETPORT(DMACNTRL0, ENDMA|_8BIT); + if (data_count & 1) { + SETPORT(DMACNTRL0, ENDMA|_8BIT); + *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT); + CURRENT_SC->SCp.this_residual--; + DATA_LEN++; + SETPORT(DMACNTRL0, ENDMA); + } + + if (data_count > 1) { + data_count >>= 1; + insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count); + CURRENT_SC->SCp.ptr += 2 * data_count; + CURRENT_SC->SCp.this_residual -= 2 * data_count; + DATA_LEN += 2 * data_count; + } + + if (CURRENT_SC->SCp.this_residual == 0 && + CURRENT_SC->SCp.buffers_residual > 0) { + /* advance to next buffer */ + CURRENT_SC->SCp.buffers_residual--; + CURRENT_SC->SCp.buffer++; + CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer); + CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length; + } + } + } else if (fifodata > 0) { + scmd_printk(KERN_ERR, CURRENT_SC, + "no buffers left for %d(%d) bytes" + " (data overrun!?)\n", + fifodata, GETPORT(FIFOSTAT)); + SETPORT(DMACNTRL0, ENDMA|_8BIT); while(fifodata>0) { int data; data=GETPORT(DATAPORT); - DPRINTK(debug_datai, DEBUG_LEAD "data=%02x\n", CMDINFO(CURRENT_SC), data); fifodata--; DATA_LEN++; } - SETPORT(DMACNTRL0, ENDMA|_8BIT); + SETPORT(DMACNTRL0, ENDMA|_8BIT); } } @@ -2258,19 +2059,20 @@ static void datai_run(struct Scsi_Host *shpnt) TESTLO(DMASTAT, DFIFOEMP) || TESTLO(SSTAT2, SEMPTY) || GETPORT(FIFOSTAT)>0) { - /* + /* * something went wrong, if there's something left in the fifos * or the phase didn't change */ - printk(ERR_LEAD "fifos should be empty and phase should have changed\n", CMDINFO(CURRENT_SC)); - disp_ports(shpnt); + scmd_printk(KERN_ERR, CURRENT_SC, + "fifos should be empty and phase should have changed\n"); } if(DATA_LEN!=GETSTCNT()) { - printk(ERR_LEAD - "manual transfer count differs from automatic (count=%d;stcnt=%d;diff=%d;fifostat=%d)", - CMDINFO(CURRENT_SC), DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN, GETPORT(FIFOSTAT)); - disp_ports(shpnt); + scmd_printk(KERN_ERR, CURRENT_SC, + "manual transfer count differs from automatic " + "(count=%d;stcnt=%d;diff=%d;fifostat=%d)", + DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN, + GETPORT(FIFOSTAT)); mdelay(10000); } } @@ -2279,11 +2081,6 @@ static void datai_end(struct Scsi_Host *shpnt) { CMD_INC_RESID(CURRENT_SC, -GETSTCNT()); - DPRINTK(debug_datai, - DEBUG_LEAD "datai_end: request_bufflen=%d resid=%d stcnt=%d\n", - CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC), - scsi_get_resid(CURRENT_SC), GETSTCNT()); - SETPORT(SXFRCTL0, CH1|CLRSTCNT); SETPORT(DMACNTRL0, 0); } @@ -2304,11 +2101,6 @@ static void datao_init(struct Scsi_Host *shpnt) SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE ); DATA_LEN = scsi_get_resid(CURRENT_SC); - - DPRINTK(debug_datao, - DEBUG_LEAD "datao_init: request_bufflen=%d; resid=%d\n", - CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC), - scsi_get_resid(CURRENT_SC)); } static void datao_run(struct Scsi_Host *shpnt) @@ -2323,8 +2115,9 @@ static void datao_run(struct Scsi_Host *shpnt) data_count=CURRENT_SC->SCp.this_residual; if(TESTLO(DMASTAT, DFIFOEMP)) { - printk(ERR_LEAD "datao fifo not empty (%d)", CMDINFO(CURRENT_SC), GETPORT(FIFOSTAT)); - disp_ports(shpnt); + scmd_printk(KERN_ERR, CURRENT_SC, + "datao fifo not empty (%d)", + GETPORT(FIFOSTAT)); break; } @@ -2342,7 +2135,7 @@ static void datao_run(struct Scsi_Host *shpnt) CURRENT_SC->SCp.ptr += 2 * data_count; CURRENT_SC->SCp.this_residual -= 2 * data_count; CMD_INC_RESID(CURRENT_SC, -2 * data_count); - } + } if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) { /* advance to next buffer */ @@ -2357,8 +2150,7 @@ static void datao_run(struct Scsi_Host *shpnt) barrier(); if(TESTLO(DMASTAT, DFIFOEMP|INTSTAT)) { - printk(ERR_LEAD "dataout timeout", CMDINFO(CURRENT_SC)); - disp_ports(shpnt); + scmd_printk(KERN_ERR, CURRENT_SC, "dataout timeout\n"); break; } } @@ -2368,35 +2160,23 @@ static void datao_end(struct Scsi_Host *shpnt) { if(TESTLO(DMASTAT, DFIFOEMP)) { int data_count = (DATA_LEN - scsi_get_resid(CURRENT_SC)) - - GETSTCNT(); - - DPRINTK(debug_datao, DEBUG_LEAD "datao: %d bytes to resend (%d written, %d transferred)\n", - CMDINFO(CURRENT_SC), - data_count, - DATA_LEN - scsi_get_resid(CURRENT_SC), - GETSTCNT()); + GETSTCNT(); CMD_INC_RESID(CURRENT_SC, data_count); data_count -= CURRENT_SC->SCp.ptr - - SG_ADDRESS(CURRENT_SC->SCp.buffer); + SG_ADDRESS(CURRENT_SC->SCp.buffer); while(data_count>0) { CURRENT_SC->SCp.buffer--; CURRENT_SC->SCp.buffers_residual++; data_count -= CURRENT_SC->SCp.buffer->length; } CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer) - - data_count; + data_count; CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length + - data_count; + data_count; } - DPRINTK(debug_datao, DEBUG_LEAD "datao_end: request_bufflen=%d; resid=%d; stcnt=%d\n", - CMDINFO(CURRENT_SC), - scsi_bufflen(CURRENT_SC), - scsi_get_resid(CURRENT_SC), - GETSTCNT()); - SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT); SETPORT(SXFRCTL0, CH1); @@ -2420,7 +2200,7 @@ static int update_state(struct Scsi_Host *shpnt) STATE=rsti; SETPORT(SCSISEQ,0); SETPORT(SSTAT1,SCSIRSTI); - } else if(stat0 & SELDI && PREVSTATE==busfree) { + } else if (stat0 & SELDI && PREVSTATE == busfree) { STATE=seldi; } else if(stat0 & SELDO && CURRENT_SC && (CURRENT_SC->SCp.phase & selecting)) { STATE=seldo; @@ -2445,8 +2225,7 @@ static int update_state(struct Scsi_Host *shpnt) } if((stat0 & SELDI) && STATE!=seldi && !dataphase) { - printk(INFO_LEAD "reselection missed?", CMDINFO(CURRENT_SC)); - disp_ports(shpnt); + scmd_printk(KERN_INFO, CURRENT_SC, "reselection missed?"); } if(STATE!=PREVSTATE) { @@ -2464,7 +2243,7 @@ static int update_state(struct Scsi_Host *shpnt) */ static void parerr_run(struct Scsi_Host *shpnt) { - printk(ERR_LEAD "parity error\n", CMDINFO(CURRENT_SC)); + scmd_printk(KERN_ERR, CURRENT_SC, "parity error\n"); done(shpnt, DID_PARITY << 16); } @@ -2476,8 +2255,8 @@ static void rsti_run(struct Scsi_Host *shpnt) { Scsi_Cmnd *ptr; - printk(KERN_NOTICE "aha152x%d: scsi reset in\n", HOSTNO); - + shost_printk(KERN_NOTICE, shpnt, "scsi reset in\n"); + ptr=DISCONNECTED_SC; while(ptr) { Scsi_Cmnd *next = SCNEXT(ptr); @@ -2539,8 +2318,6 @@ static void is_complete(struct Scsi_Host *shpnt) dataphase=update_state(shpnt); - DPRINTK(debug_phases, LEAD "start %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name); - /* * end previous state * @@ -2567,9 +2344,9 @@ static void is_complete(struct Scsi_Host *shpnt) if(dataphase) { SETPORT(SSTAT0, REQINIT); SETPORT(SCSISIG, GETPORT(SCSISIG) & P_MASK); - SETPORT(SSTAT1, PHASECHG); + SETPORT(SSTAT1, PHASECHG); } - + /* * enable SPIO mode if previous didn't use it * and this one does @@ -2581,14 +2358,14 @@ static void is_complete(struct Scsi_Host *shpnt) if(CURRENT_SC) CURRENT_SC->SCp.phase |= spiordy; } - + /* * initialize for new state * */ if(PREVSTATE!=STATE && states[STATE].init) states[STATE].init(shpnt); - + /* * handle current state * @@ -2596,8 +2373,9 @@ static void is_complete(struct Scsi_Host *shpnt) if(states[STATE].run) states[STATE].run(shpnt); else - printk(ERR_LEAD "unexpected state (%x)\n", CMDINFO(CURRENT_SC), STATE); - + scmd_printk(KERN_ERR, CURRENT_SC, + "unexpected state (%x)\n", STATE); + /* * setup controller to interrupt on * the next expected condition and @@ -2613,7 +2391,6 @@ static void is_complete(struct Scsi_Host *shpnt) HOSTDATA(shpnt)->time[STATE] += jiffies-start; #endif - DPRINTK(debug_phases, LEAD "end %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name); } while(pending); /* @@ -2626,289 +2403,42 @@ static void is_complete(struct Scsi_Host *shpnt) } -/* +/* * Dump the current driver status and panic */ static void aha152x_error(struct Scsi_Host *shpnt, char *msg) { - printk(KERN_EMERG "\naha152x%d: %s\n", HOSTNO, msg); + shost_printk(KERN_EMERG, shpnt, "%s\n", msg); show_queues(shpnt); panic("aha152x panic\n"); } /* - * Display registers of AIC-6260 - */ -static void disp_ports(struct Scsi_Host *shpnt) -{ -#if defined(AHA152X_DEBUG) - int s; - - printk("\n%s: %s(%s) ", - CURRENT_SC ? "busy" : "waiting", - states[STATE].name, - states[PREVSTATE].name); - - s = GETPORT(SCSISEQ); - printk("SCSISEQ( "); - if (s & TEMODEO) - printk("TARGET MODE "); - if (s & ENSELO) - printk("SELO "); - if (s & ENSELI) - printk("SELI "); - if (s & ENRESELI) - printk("RESELI "); - if (s & ENAUTOATNO) - printk("AUTOATNO "); - if (s & ENAUTOATNI) - printk("AUTOATNI "); - if (s & ENAUTOATNP) - printk("AUTOATNP "); - if (s & SCSIRSTO) - printk("SCSIRSTO "); - printk(");"); - - printk(" SCSISIG("); - s = GETPORT(SCSISIG); - switch (s & P_MASK) { - case P_DATAO: - printk("DATA OUT"); - break; - case P_DATAI: - printk("DATA IN"); - break; - case P_CMD: - printk("COMMAND"); - break; - case P_STATUS: - printk("STATUS"); - break; - case P_MSGO: - printk("MESSAGE OUT"); - break; - case P_MSGI: - printk("MESSAGE IN"); - break; - default: - printk("*invalid*"); - break; - } - - printk("); "); - - printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo"); - - printk("SSTAT( "); - s = GETPORT(SSTAT0); - if (s & TARGET) - printk("TARGET "); - if (s & SELDO) - printk("SELDO "); - if (s & SELDI) - printk("SELDI "); - if (s & SELINGO) - printk("SELINGO "); - if (s & SWRAP) - printk("SWRAP "); - if (s & SDONE) - printk("SDONE "); - if (s & SPIORDY) - printk("SPIORDY "); - if (s & DMADONE) - printk("DMADONE "); - - s = GETPORT(SSTAT1); - if (s & SELTO) - printk("SELTO "); - if (s & ATNTARG) - printk("ATNTARG "); - if (s & SCSIRSTI) - printk("SCSIRSTI "); - if (s & PHASEMIS) - printk("PHASEMIS "); - if (s & BUSFREE) - printk("BUSFREE "); - if (s & SCSIPERR) - printk("SCSIPERR "); - if (s & PHASECHG) - printk("PHASECHG "); - if (s & REQINIT) - printk("REQINIT "); - printk("); "); - - - printk("SSTAT( "); - - s = GETPORT(SSTAT0) & GETPORT(SIMODE0); - - if (s & TARGET) - printk("TARGET "); - if (s & SELDO) - printk("SELDO "); - if (s & SELDI) - printk("SELDI "); - if (s & SELINGO) - printk("SELINGO "); - if (s & SWRAP) - printk("SWRAP "); - if (s & SDONE) - printk("SDONE "); - if (s & SPIORDY) - printk("SPIORDY "); - if (s & DMADONE) - printk("DMADONE "); - - s = GETPORT(SSTAT1) & GETPORT(SIMODE1); - - if (s & SELTO) - printk("SELTO "); - if (s & ATNTARG) - printk("ATNTARG "); - if (s & SCSIRSTI) - printk("SCSIRSTI "); - if (s & PHASEMIS) - printk("PHASEMIS "); - if (s & BUSFREE) - printk("BUSFREE "); - if (s & SCSIPERR) - printk("SCSIPERR "); - if (s & PHASECHG) - printk("PHASECHG "); - if (s & REQINIT) - printk("REQINIT "); - printk("); "); - - printk("SXFRCTL0( "); - - s = GETPORT(SXFRCTL0); - if (s & SCSIEN) - printk("SCSIEN "); - if (s & DMAEN) - printk("DMAEN "); - if (s & CH1) - printk("CH1 "); - if (s & CLRSTCNT) - printk("CLRSTCNT "); - if (s & SPIOEN) - printk("SPIOEN "); - if (s & CLRCH1) - printk("CLRCH1 "); - printk("); "); - - printk("SIGNAL( "); - - s = GETPORT(SCSISIG); - if (s & SIG_ATNI) - printk("ATNI "); - if (s & SIG_SELI) - printk("SELI "); - if (s & SIG_BSYI) - printk("BSYI "); - if (s & SIG_REQI) - printk("REQI "); - if (s & SIG_ACKI) - printk("ACKI "); - printk("); "); - - printk("SELID (%02x), ", GETPORT(SELID)); - - printk("STCNT (%d), ", GETSTCNT()); - - printk("SSTAT2( "); - - s = GETPORT(SSTAT2); - if (s & SOFFSET) - printk("SOFFSET "); - if (s & SEMPTY) - printk("SEMPTY "); - if (s & SFULL) - printk("SFULL "); - printk("); SFCNT (%d); ", s & (SFULL | SFCNT)); - - s = GETPORT(SSTAT3); - printk("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f); - - printk("SSTAT4( "); - s = GETPORT(SSTAT4); - if (s & SYNCERR) - printk("SYNCERR "); - if (s & FWERR) - printk("FWERR "); - if (s & FRERR) - printk("FRERR "); - printk("); "); - - printk("DMACNTRL0( "); - s = GETPORT(DMACNTRL0); - printk("%s ", s & _8BIT ? "8BIT" : "16BIT"); - printk("%s ", s & DMA ? "DMA" : "PIO"); - printk("%s ", s & WRITE_READ ? "WRITE" : "READ"); - if (s & ENDMA) - printk("ENDMA "); - if (s & INTEN) - printk("INTEN "); - if (s & RSTFIFO) - printk("RSTFIFO "); - if (s & SWINT) - printk("SWINT "); - printk("); "); - - printk("DMASTAT( "); - s = GETPORT(DMASTAT); - if (s & ATDONE) - printk("ATDONE "); - if (s & WORDRDY) - printk("WORDRDY "); - if (s & DFIFOFULL) - printk("DFIFOFULL "); - if (s & DFIFOEMP) - printk("DFIFOEMP "); - printk(")\n"); -#endif -} - -/* * display enabled interrupts */ static void disp_enintr(struct Scsi_Host *shpnt) { - int s; - - printk(KERN_DEBUG "enabled interrupts ( "); - - s = GETPORT(SIMODE0); - if (s & ENSELDO) - printk("ENSELDO "); - if (s & ENSELDI) - printk("ENSELDI "); - if (s & ENSELINGO) - printk("ENSELINGO "); - if (s & ENSWRAP) - printk("ENSWRAP "); - if (s & ENSDONE) - printk("ENSDONE "); - if (s & ENSPIORDY) - printk("ENSPIORDY "); - if (s & ENDMADONE) - printk("ENDMADONE "); - - s = GETPORT(SIMODE1); - if (s & ENSELTIMO) - printk("ENSELTIMO "); - if (s & ENATNTARG) - printk("ENATNTARG "); - if (s & ENPHASEMIS) - printk("ENPHASEMIS "); - if (s & ENBUSFREE) - printk("ENBUSFREE "); - if (s & ENSCSIPERR) - printk("ENSCSIPERR "); - if (s & ENPHASECHG) - printk("ENPHASECHG "); - if (s & ENREQINIT) - printk("ENREQINIT "); - printk(")\n"); + int s0, s1; + + s0 = GETPORT(SIMODE0); + s1 = GETPORT(SIMODE1); + + shost_printk(KERN_DEBUG, shpnt, + "enabled interrupts (%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", + (s0 & ENSELDO) ? "ENSELDO " : "", + (s0 & ENSELDI) ? "ENSELDI " : "", + (s0 & ENSELINGO) ? "ENSELINGO " : "", + (s0 & ENSWRAP) ? "ENSWRAP " : "", + (s0 & ENSDONE) ? "ENSDONE " : "", + (s0 & ENSPIORDY) ? "ENSPIORDY " : "", + (s0 & ENDMADONE) ? "ENDMADONE " : "", + (s1 & ENSELTIMO) ? "ENSELTIMO " : "", + (s1 & ENATNTARG) ? "ENATNTARG " : "", + (s1 & ENPHASEMIS) ? "ENPHASEMIS " : "", + (s1 & ENBUSFREE) ? "ENBUSFREE " : "", + (s1 & ENSCSIPERR) ? "ENSCSIPERR " : "", + (s1 & ENPHASECHG) ? "ENPHASECHG " : "", + (s1 & ENREQINIT) ? "ENREQINIT " : ""); } /* @@ -2916,36 +2446,21 @@ static void disp_enintr(struct Scsi_Host *shpnt) */ static void show_command(Scsi_Cmnd *ptr) { - scmd_printk(KERN_DEBUG, ptr, "%p: cmnd=(", ptr); - - __scsi_print_command(ptr->cmnd); - - printk(KERN_DEBUG "); request_bufflen=%d; resid=%d; phase |", - scsi_bufflen(ptr), scsi_get_resid(ptr)); - - if (ptr->SCp.phase & not_issued) - printk("not issued|"); - if (ptr->SCp.phase & selecting) - printk("selecting|"); - if (ptr->SCp.phase & identified) - printk("identified|"); - if (ptr->SCp.phase & disconnected) - printk("disconnected|"); - if (ptr->SCp.phase & completed) - printk("completed|"); - if (ptr->SCp.phase & spiordy) - printk("spiordy|"); - if (ptr->SCp.phase & syncneg) - printk("syncneg|"); - if (ptr->SCp.phase & aborted) - printk("aborted|"); - if (ptr->SCp.phase & resetted) - printk("resetted|"); - if( SCDATA(ptr) ) { - printk("; next=0x%p\n", SCNEXT(ptr)); - } else { - printk("; next=(host scribble NULL)\n"); - } + scsi_print_command(ptr); + scmd_printk(KERN_DEBUG, ptr, + "request_bufflen=%d; resid=%d; " + "phase |%s%s%s%s%s%s%s%s%s; next=0x%p", + scsi_bufflen(ptr), scsi_get_resid(ptr), + (ptr->SCp.phase & not_issued) ? "not issued|" : "", + (ptr->SCp.phase & selecting) ? "selecting|" : "", + (ptr->SCp.phase & identified) ? "identified|" : "", + (ptr->SCp.phase & disconnected) ? "disconnected|" : "", + (ptr->SCp.phase & completed) ? "completed|" : "", + (ptr->SCp.phase & spiordy) ? "spiordy|" : "", + (ptr->SCp.phase & syncneg) ? "syncneg|" : "", + (ptr->SCp.phase & aborted) ? "aborted|" : "", + (ptr->SCp.phase & resetted) ? "resetted|" : "", + (SCDATA(ptr)) ? SCNEXT(ptr) : NULL); } /* @@ -2972,7 +2487,6 @@ static void show_queues(struct Scsi_Host *shpnt) for (ptr = DISCONNECTED_SC; ptr; ptr = SCDATA(ptr) ? SCNEXT(ptr) : NULL) show_command(ptr); - disp_ports(shpnt); disp_enintr(shpnt); } @@ -3276,15 +2790,6 @@ static int aha152x_set_info(struct Scsi_Host *shpnt, char *buffer, int length) if(!shpnt || !buffer || length<8 || strncmp("aha152x ", buffer, 8)!=0) return -EINVAL; -#if defined(AHA152X_DEBUG) - if(length>14 && strncmp("debug ", buffer+8, 6)==0) { - int debug = HOSTDATA(shpnt)->debug; - - HOSTDATA(shpnt)->debug = simple_strtoul(buffer+14, NULL, 0); - - printk(KERN_INFO "aha152x%d: debugging options set to 0x%04x (were 0x%04x)\n", HOSTNO, HOSTDATA(shpnt)->debug, debug); - } else -#endif #if defined(AHA152X_STAT) if(length>13 && strncmp("reset", buffer+8, 5)==0) { int i; @@ -3302,7 +2807,7 @@ static int aha152x_set_info(struct Scsi_Host *shpnt, char *buffer, int length) HOSTDATA(shpnt)->time[i]=0; } - printk(KERN_INFO "aha152x%d: stats reseted.\n", HOSTNO); + shost_printk(KERN_INFO, shpnt, "aha152x: stats reset.\n"); } else #endif @@ -3343,29 +2848,6 @@ static int aha152x_show_info(struct seq_file *m, struct Scsi_Host *shpnt) (((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2) * 50, HOSTDATA(shpnt)->syncrate[i] & 0x0f); } -#if defined(AHA152X_DEBUG) -#define PDEBUG(flags,txt) \ - if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt); - - SPRINTF("enabled debugging options: "); - - PDEBUG(debug_procinfo, "procinfo"); - PDEBUG(debug_queue, "queue"); - PDEBUG(debug_intr, "interrupt"); - PDEBUG(debug_selection, "selection"); - PDEBUG(debug_msgo, "message out"); - PDEBUG(debug_msgi, "message in"); - PDEBUG(debug_status, "status"); - PDEBUG(debug_cmd, "command"); - PDEBUG(debug_datai, "data in"); - PDEBUG(debug_datao, "data out"); - PDEBUG(debug_eh, "eh"); - PDEBUG(debug_locking, "locks"); - PDEBUG(debug_phases, "phases"); - - SPRINTF("\n"); -#endif - SPRINTF("\nqueue status:\n"); DO_LOCK(flags); if (ISSUE_SC) { @@ -3393,8 +2875,8 @@ static int aha152x_show_info(struct seq_file *m, struct Scsi_Host *shpnt) #if defined(AHA152X_STAT) SPRINTF("statistics:\n" - "total commands: %d\n" - "disconnections: %d\n" + "total commands: %d\n" + "disconnections: %d\n" "busfree with check condition: %d\n" "busfree without old command: %d\n" "busfree without new command: %d\n" @@ -3413,7 +2895,7 @@ static int aha152x_show_info(struct seq_file *m, struct Scsi_Host *shpnt) HOSTDATA(shpnt)->busfree_without_any_action); for(i=0; i<maxstate; i++) { SPRINTF("%-10s %-12d %-12d %-12ld\n", - states[i].name, + states[i].name, HOSTDATA(shpnt)->count_trans[i], HOSTDATA(shpnt)->count[i], HOSTDATA(shpnt)->time[i]); @@ -3671,25 +3153,19 @@ static int __init aha152x_init(void) setup[setup_count].synchronous = aha152x[5]; setup[setup_count].delay = aha152x[6]; setup[setup_count].ext_trans = aha152x[7]; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = aha152x[8]; -#endif - } else if(io[0]!=0 || irq[0]!=0) { + } else if (io[0] != 0 || irq[0] != 0) { if(io[0]!=0) setup[setup_count].io_port = io[0]; if(irq[0]!=0) setup[setup_count].irq = irq[0]; - setup[setup_count].scsiid = scsiid[0]; - setup[setup_count].reconnect = reconnect[0]; - setup[setup_count].parity = parity[0]; - setup[setup_count].synchronous = sync[0]; - setup[setup_count].delay = delay[0]; - setup[setup_count].ext_trans = exttrans[0]; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = debug[0]; -#endif + setup[setup_count].scsiid = scsiid[0]; + setup[setup_count].reconnect = reconnect[0]; + setup[setup_count].parity = parity[0]; + setup[setup_count].synchronous = sync[0]; + setup[setup_count].delay = delay[0]; + setup[setup_count].ext_trans = exttrans[0]; } - if (checksetup(&setup[setup_count])) + if (checksetup(&setup[setup_count])) setup_count++; else printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n", @@ -3714,22 +3190,16 @@ static int __init aha152x_init(void) setup[setup_count].synchronous = aha152x1[5]; setup[setup_count].delay = aha152x1[6]; setup[setup_count].ext_trans = aha152x1[7]; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = aha152x1[8]; -#endif - } else if(io[1]!=0 || irq[1]!=0) { + } else if (io[1] != 0 || irq[1] != 0) { if(io[1]!=0) setup[setup_count].io_port = io[1]; if(irq[1]!=0) setup[setup_count].irq = irq[1]; - setup[setup_count].scsiid = scsiid[1]; - setup[setup_count].reconnect = reconnect[1]; - setup[setup_count].parity = parity[1]; - setup[setup_count].synchronous = sync[1]; - setup[setup_count].delay = delay[1]; - setup[setup_count].ext_trans = exttrans[1]; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = debug[1]; -#endif + setup[setup_count].scsiid = scsiid[1]; + setup[setup_count].reconnect = reconnect[1]; + setup[setup_count].parity = parity[1]; + setup[setup_count].synchronous = sync[1]; + setup[setup_count].delay = delay[1]; + setup[setup_count].ext_trans = exttrans[1]; } if (checksetup(&setup[setup_count])) setup_count++; @@ -3776,9 +3246,6 @@ static int __init aha152x_init(void) setup[setup_count].synchronous = 1; setup[setup_count].delay = DELAY_DEFAULT; setup[setup_count].ext_trans = 0; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = DEBUG_DEFAULT; -#endif #if defined(__ISAPNP__) pnpdev[setup_count] = dev; #endif @@ -3847,9 +3314,6 @@ static int __init aha152x_init(void) setup[setup_count].synchronous = conf.cf_syncneg; setup[setup_count].delay = DELAY_DEFAULT; setup[setup_count].ext_trans = 0; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = DEBUG_DEFAULT; -#endif setup_count++; } @@ -3903,11 +3367,8 @@ module_exit(aha152x_exit); #if !defined(MODULE) static int __init aha152x_setup(char *str) { -#if defined(AHA152X_DEBUG) - int ints[11]; -#else int ints[10]; -#endif + get_options(str, ARRAY_SIZE(ints), ints); if(setup_count>=ARRAY_SIZE(setup)) { @@ -3924,16 +3385,9 @@ static int __init aha152x_setup(char *str) setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 1; setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT; setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0; -#if defined(AHA152X_DEBUG) - setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT; - if (ints[0] > 9) { - printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>" - "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n"); -#else if (ints[0] > 8) { /*}*/ printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>" "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n"); -#endif } else { setup_count++; return 0; diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c index 5f3101797c93..31ace4bef8fe 100644 --- a/drivers/scsi/aha1740.c +++ b/drivers/scsi/aha1740.c @@ -531,7 +531,7 @@ static int aha1740_eh_abort_handler (Scsi_Cmnd *dummy) * quiet as possible... */ - return 0; + return SUCCESS; } static struct scsi_host_template aha1740_template = { diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index ed333669a7dc..d5c7b193d8d3 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -925,6 +925,7 @@ struct scsi_host_template aic79xx_driver_template = { .slave_configure = ahd_linux_slave_configure, .target_alloc = ahd_linux_target_alloc, .target_destroy = ahd_linux_target_destroy, + .use_blk_tags = 1, }; /******************************** Bus DMA *************************************/ @@ -1468,12 +1469,9 @@ ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev, switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) { case AHD_DEV_Q_BASIC: - scsi_set_tag_type(sdev, MSG_SIMPLE_TASK); - scsi_activate_tcq(sdev, dev->openings + dev->active); - break; case AHD_DEV_Q_TAGGED: - scsi_set_tag_type(sdev, MSG_ORDERED_TASK); - scsi_activate_tcq(sdev, dev->openings + dev->active); + scsi_change_queue_depth(sdev, + dev->openings + dev->active); break; default: /* @@ -1482,7 +1480,7 @@ ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev, * serially on the controller/device. This should * remove some latency. */ - scsi_deactivate_tcq(sdev, 1); + scsi_change_queue_depth(sdev, 1); break; } } @@ -1619,15 +1617,6 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev, } if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) { - int msg_bytes; - uint8_t tag_msgs[2]; - - msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs); - if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) { - hscb->control |= tag_msgs[0]; - if (tag_msgs[0] == MSG_ORDERED_TASK) - dev->commands_since_idle_or_otag = 0; - } else if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH && (dev->flags & AHD_DEV_Q_TAGGED) != 0) { hscb->control |= MSG_ORDERED_TASK; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index d2c9bf39033d..88360116dbcb 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -812,6 +812,7 @@ struct scsi_host_template aic7xxx_driver_template = { .slave_configure = ahc_linux_slave_configure, .target_alloc = ahc_linux_target_alloc, .target_destroy = ahc_linux_target_destroy, + .use_blk_tags = 1, }; /**************************** Tasklet Handler *********************************/ @@ -1334,13 +1335,9 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev, } switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) { case AHC_DEV_Q_BASIC: - scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); - scsi_activate_tcq(sdev, dev->openings + dev->active); - break; case AHC_DEV_Q_TAGGED: - scsi_set_tag_type(sdev, MSG_ORDERED_TAG); - scsi_activate_tcq(sdev, dev->openings + dev->active); - break; + scsi_change_queue_depth(sdev, + dev->openings + dev->active); default: /* * We allow the OS to queue 2 untagged transactions to @@ -1348,7 +1345,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev, * serially on the controller/device. This should * remove some latency. */ - scsi_deactivate_tcq(sdev, 2); + scsi_change_queue_depth(sdev, 2); break; } } @@ -1447,7 +1444,7 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev, * we are storing a full busy target *lun* * table in SCB space. */ - if (!blk_rq_tagged(cmd->request) + if (!(cmd->flags & SCMD_TAGGED) && (ahc->features & AHC_SCB_BTT) == 0) { int target_offset; @@ -1501,15 +1498,7 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev, } if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) { - int msg_bytes; - uint8_t tag_msgs[2]; - - msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs); - if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) { - hscb->control |= tag_msgs[0]; - if (tag_msgs[0] == MSG_ORDERED_TASK) - dev->commands_since_idle_or_otag = 0; - } else if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH + if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH && (dev->flags & AHC_DEV_Q_TAGGED) != 0) { hscb->control |= MSG_ORDERED_TASK; dev->commands_since_idle_or_otag = 0; diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 66cda669b417..26d4ad9ede2e 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -78,7 +78,7 @@ void asd_dev_gone(struct domain_device *dev); void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id); -int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags); +int asd_execute_task(struct sas_task *task, gfp_t gfp_flags); void asd_set_dmamode(struct domain_device *dev); diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 4df867e07b20..9f636a34d595 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -1200,8 +1200,7 @@ static void asd_start_scb_timers(struct list_head *list) * Case A: we can send the whole batch at once. Increment "pending" * in the beginning of this function, when it is checked, in order to * eliminate races when this function is called by multiple processes. - * Case B: should never happen if the managing layer considers - * lldd_queue_size. + * Case B: should never happen. */ int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, int num) diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index c56741fc4b99..14fc018436c2 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -49,14 +49,6 @@ MODULE_PARM_DESC(use_msi, "\n" "\tEnable(1) or disable(0) using PCI MSI.\n" "\tDefault: 0"); -static int lldd_max_execute_num = 0; -module_param_named(collector, lldd_max_execute_num, int, S_IRUGO); -MODULE_PARM_DESC(collector, "\n" - "\tIf greater than one, tells the SAS Layer to run in Task Collector\n" - "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n" - "\tThe aic94xx SAS LLDD supports both modes.\n" - "\tDefault: 0 (Direct Mode).\n"); - static struct scsi_transport_template *aic94xx_transport_template; static int asd_scan_finished(struct Scsi_Host *, unsigned long); static void asd_scan_start(struct Scsi_Host *); @@ -83,6 +75,8 @@ static struct scsi_host_template aic94xx_sht = { .eh_bus_reset_handler = sas_eh_bus_reset_handler, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static int asd_map_memio(struct asd_ha_struct *asd_ha) @@ -709,9 +703,6 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha) asd_ha->sas_ha.sas_port= sas_ports; asd_ha->sas_ha.num_phys= ASD_MAX_PHYS; - asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue; - asd_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num; - return sas_register_ha(&asd_ha->sas_ha); } diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 59b86e260ce9..5ff1ce7ba1f4 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -543,8 +543,7 @@ static int asd_can_queue(struct asd_ha_struct *asd_ha, int num) return res; } -int asd_execute_task(struct sas_task *task, const int num, - gfp_t gfp_flags) +int asd_execute_task(struct sas_task *task, gfp_t gfp_flags) { int res = 0; LIST_HEAD(alist); @@ -553,11 +552,11 @@ int asd_execute_task(struct sas_task *task, const int num, struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; unsigned long flags; - res = asd_can_queue(asd_ha, num); + res = asd_can_queue(asd_ha, 1); if (res) return res; - res = num; + res = 1; ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags); if (res) { res = -ENOMEM; @@ -568,7 +567,7 @@ int asd_execute_task(struct sas_task *task, const int num, list_for_each_entry(a, &alist, list) { a->uldd_task = t; t->lldd_task = a; - t = list_entry(t->list.next, struct sas_task, list); + break; } list_for_each_entry(a, &alist, list) { t = a->uldd_task; @@ -601,7 +600,7 @@ int asd_execute_task(struct sas_task *task, const int num, } list_del_init(&alist); - res = asd_post_ascb_list(asd_ha, ascb, num); + res = asd_post_ascb_list(asd_ha, ascb, 1); if (unlikely(res)) { a = NULL; __list_add(&alist, ascb->list.prev, &ascb->list); @@ -639,6 +638,6 @@ out_err_unmap: out_err: if (ascb) asd_ascb_free_list(ascb); - asd_can_dequeue(asd_ha, num); + asd_can_dequeue(asd_ha, 1); return res; } diff --git a/drivers/scsi/am53c974.c b/drivers/scsi/am53c974.c new file mode 100644 index 000000000000..aa3e2c7cd83c --- /dev/null +++ b/drivers/scsi/am53c974.c @@ -0,0 +1,586 @@ +/* + * AMD am53c974 driver. + * Copyright (c) 2014 Hannes Reinecke, SUSE Linux GmbH + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/interrupt.h> + +#include <scsi/scsi_host.h> + +#include "esp_scsi.h" + +#define DRV_MODULE_NAME "am53c974" +#define DRV_MODULE_VERSION "1.00" + +static bool am53c974_debug; +static bool am53c974_fenab = true; + +#define esp_dma_log(f, a...) \ + do { \ + if (am53c974_debug) \ + shost_printk(KERN_DEBUG, esp->host, f, ##a); \ + } while (0) + +#define ESP_DMA_CMD 0x10 +#define ESP_DMA_STC 0x11 +#define ESP_DMA_SPA 0x12 +#define ESP_DMA_WBC 0x13 +#define ESP_DMA_WAC 0x14 +#define ESP_DMA_STATUS 0x15 +#define ESP_DMA_SMDLA 0x16 +#define ESP_DMA_WMAC 0x17 + +#define ESP_DMA_CMD_IDLE 0x00 +#define ESP_DMA_CMD_BLAST 0x01 +#define ESP_DMA_CMD_ABORT 0x02 +#define ESP_DMA_CMD_START 0x03 +#define ESP_DMA_CMD_MASK 0x03 +#define ESP_DMA_CMD_DIAG 0x04 +#define ESP_DMA_CMD_MDL 0x10 +#define ESP_DMA_CMD_INTE_P 0x20 +#define ESP_DMA_CMD_INTE_D 0x40 +#define ESP_DMA_CMD_DIR 0x80 + +#define ESP_DMA_STAT_PWDN 0x01 +#define ESP_DMA_STAT_ERROR 0x02 +#define ESP_DMA_STAT_ABORT 0x04 +#define ESP_DMA_STAT_DONE 0x08 +#define ESP_DMA_STAT_SCSIINT 0x10 +#define ESP_DMA_STAT_BCMPLT 0x20 + +/* EEPROM is accessed with 16-bit values */ +#define DC390_EEPROM_READ 0x80 +#define DC390_EEPROM_LEN 0x40 + +/* + * DC390 EEPROM + * + * 8 * 4 bytes of per-device options + * followed by HBA specific options + */ + +/* Per-device options */ +#define DC390_EE_MODE1 0x00 +#define DC390_EE_SPEED 0x01 + +/* HBA-specific options */ +#define DC390_EE_ADAPT_SCSI_ID 0x40 +#define DC390_EE_MODE2 0x41 +#define DC390_EE_DELAY 0x42 +#define DC390_EE_TAG_CMD_NUM 0x43 + +#define DC390_EE_MODE1_PARITY_CHK 0x01 +#define DC390_EE_MODE1_SYNC_NEGO 0x02 +#define DC390_EE_MODE1_EN_DISC 0x04 +#define DC390_EE_MODE1_SEND_START 0x08 +#define DC390_EE_MODE1_TCQ 0x10 + +#define DC390_EE_MODE2_MORE_2DRV 0x01 +#define DC390_EE_MODE2_GREATER_1G 0x02 +#define DC390_EE_MODE2_RST_SCSI_BUS 0x04 +#define DC390_EE_MODE2_ACTIVE_NEGATION 0x08 +#define DC390_EE_MODE2_NO_SEEK 0x10 +#define DC390_EE_MODE2_LUN_CHECK 0x20 + +struct pci_esp_priv { + struct esp *esp; + u8 dma_status; +}; + +static void pci_esp_dma_drain(struct esp *esp); + +static inline struct pci_esp_priv *pci_esp_get_priv(struct esp *esp) +{ + struct pci_dev *pdev = esp->dev; + + return pci_get_drvdata(pdev); +} + +static void pci_esp_write8(struct esp *esp, u8 val, unsigned long reg) +{ + iowrite8(val, esp->regs + (reg * 4UL)); +} + +static u8 pci_esp_read8(struct esp *esp, unsigned long reg) +{ + return ioread8(esp->regs + (reg * 4UL)); +} + +static void pci_esp_write32(struct esp *esp, u32 val, unsigned long reg) +{ + return iowrite32(val, esp->regs + (reg * 4UL)); +} + +static dma_addr_t pci_esp_map_single(struct esp *esp, void *buf, + size_t sz, int dir) +{ + return pci_map_single(esp->dev, buf, sz, dir); +} + +static int pci_esp_map_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + return pci_map_sg(esp->dev, sg, num_sg, dir); +} + +static void pci_esp_unmap_single(struct esp *esp, dma_addr_t addr, + size_t sz, int dir) +{ + pci_unmap_single(esp->dev, addr, sz, dir); +} + +static void pci_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + pci_unmap_sg(esp->dev, sg, num_sg, dir); +} + +static int pci_esp_irq_pending(struct esp *esp) +{ + struct pci_esp_priv *pep = pci_esp_get_priv(esp); + + pep->dma_status = pci_esp_read8(esp, ESP_DMA_STATUS); + esp_dma_log("dma intr dreg[%02x]\n", pep->dma_status); + + if (pep->dma_status & (ESP_DMA_STAT_ERROR | + ESP_DMA_STAT_ABORT | + ESP_DMA_STAT_DONE | + ESP_DMA_STAT_SCSIINT)) + return 1; + + return 0; +} + +static void pci_esp_reset_dma(struct esp *esp) +{ + /* Nothing to do ? */ +} + +static void pci_esp_dma_drain(struct esp *esp) +{ + u8 resid; + int lim = 1000; + + + if ((esp->sreg & ESP_STAT_PMASK) == ESP_DOP || + (esp->sreg & ESP_STAT_PMASK) == ESP_DIP) + /* Data-In or Data-Out, nothing to be done */ + return; + + while (--lim > 0) { + resid = pci_esp_read8(esp, ESP_FFLAGS) & ESP_FF_FBYTES; + if (resid <= 1) + break; + cpu_relax(); + } + if (resid > 1) { + /* FIFO not cleared */ + shost_printk(KERN_INFO, esp->host, + "FIFO not cleared, %d bytes left\n", + resid); + } + + /* + * When there is a residual BCMPLT will never be set + * (obviously). But we still have to issue the BLAST + * command, otherwise the data will not being transferred. + * But we'll never know when the BLAST operation is + * finished. So check for some time and give up eventually. + */ + lim = 1000; + pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_BLAST, ESP_DMA_CMD); + while (pci_esp_read8(esp, ESP_DMA_STATUS) & ESP_DMA_STAT_BCMPLT) { + if (--lim == 0) + break; + cpu_relax(); + } + pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_IDLE, ESP_DMA_CMD); + esp_dma_log("DMA blast done (%d tries, %d bytes left)\n", lim, resid); + /* BLAST residual handling is currently untested */ + if (WARN_ON_ONCE(resid == 1)) { + struct esp_cmd_entry *ent = esp->active_cmd; + + ent->flags |= ESP_CMD_FLAG_RESIDUAL; + } +} + +static void pci_esp_dma_invalidate(struct esp *esp) +{ + struct pci_esp_priv *pep = pci_esp_get_priv(esp); + + esp_dma_log("invalidate DMA\n"); + + pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD); + pep->dma_status = 0; +} + +static int pci_esp_dma_error(struct esp *esp) +{ + struct pci_esp_priv *pep = pci_esp_get_priv(esp); + + if (pep->dma_status & ESP_DMA_STAT_ERROR) { + u8 dma_cmd = pci_esp_read8(esp, ESP_DMA_CMD); + + if ((dma_cmd & ESP_DMA_CMD_MASK) == ESP_DMA_CMD_START) + pci_esp_write8(esp, ESP_DMA_CMD_ABORT, ESP_DMA_CMD); + + return 1; + } + if (pep->dma_status & ESP_DMA_STAT_ABORT) { + pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD); + pep->dma_status = pci_esp_read8(esp, ESP_DMA_CMD); + return 1; + } + return 0; +} + +static void pci_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, + u32 dma_count, int write, u8 cmd) +{ + struct pci_esp_priv *pep = pci_esp_get_priv(esp); + u32 val = 0; + + BUG_ON(!(cmd & ESP_CMD_DMA)); + + pep->dma_status = 0; + + /* Set DMA engine to IDLE */ + if (write) + /* DMA write direction logic is inverted */ + val |= ESP_DMA_CMD_DIR; + pci_esp_write8(esp, ESP_DMA_CMD_IDLE | val, ESP_DMA_CMD); + + pci_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + pci_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + if (esp->config2 & ESP_CONFIG2_FENAB) + pci_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + pci_esp_write32(esp, esp_count, ESP_DMA_STC); + pci_esp_write32(esp, addr, ESP_DMA_SPA); + + esp_dma_log("start dma addr[%x] count[%d:%d]\n", + addr, esp_count, dma_count); + + scsi_esp_cmd(esp, cmd); + /* Send DMA Start command */ + pci_esp_write8(esp, ESP_DMA_CMD_START | val, ESP_DMA_CMD); +} + +static u32 pci_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len) +{ + int dma_limit = 16; + u32 base, end; + + /* + * If CONFIG2_FENAB is set we can + * handle up to 24 bit addresses + */ + if (esp->config2 & ESP_CONFIG2_FENAB) + dma_limit = 24; + + if (dma_len > (1U << dma_limit)) + dma_len = (1U << dma_limit); + + /* + * Prevent crossing a 24-bit address boundary. + */ + base = dma_addr & ((1U << 24) - 1U); + end = base + dma_len; + if (end > (1U << 24)) + end = (1U <<24); + dma_len = end - base; + + return dma_len; +} + +static const struct esp_driver_ops pci_esp_ops = { + .esp_write8 = pci_esp_write8, + .esp_read8 = pci_esp_read8, + .map_single = pci_esp_map_single, + .map_sg = pci_esp_map_sg, + .unmap_single = pci_esp_unmap_single, + .unmap_sg = pci_esp_unmap_sg, + .irq_pending = pci_esp_irq_pending, + .reset_dma = pci_esp_reset_dma, + .dma_drain = pci_esp_dma_drain, + .dma_invalidate = pci_esp_dma_invalidate, + .send_dma_cmd = pci_esp_send_dma_cmd, + .dma_error = pci_esp_dma_error, + .dma_length_limit = pci_esp_dma_length_limit, +}; + +/* + * Read DC-390 eeprom + */ +static void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd) +{ + u8 carry_flag = 1, j = 0x80, bval; + int i; + + for (i = 0; i < 9; i++) { + if (carry_flag) { + pci_write_config_byte(pdev, 0x80, 0x40); + bval = 0xc0; + } else + bval = 0x80; + + udelay(160); + pci_write_config_byte(pdev, 0x80, bval); + udelay(160); + pci_write_config_byte(pdev, 0x80, 0); + udelay(160); + + carry_flag = (cmd & j) ? 1 : 0; + j >>= 1; + } +} + +static u16 dc390_eeprom_get_data(struct pci_dev *pdev) +{ + int i; + u16 wval = 0; + u8 bval; + + for (i = 0; i < 16; i++) { + wval <<= 1; + + pci_write_config_byte(pdev, 0x80, 0x80); + udelay(160); + pci_write_config_byte(pdev, 0x80, 0x40); + udelay(160); + pci_read_config_byte(pdev, 0x00, &bval); + + if (bval == 0x22) + wval |= 1; + } + + return wval; +} + +static void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr) +{ + u8 cmd = DC390_EEPROM_READ, i; + + for (i = 0; i < DC390_EEPROM_LEN; i++) { + pci_write_config_byte(pdev, 0xc0, 0); + udelay(160); + + dc390_eeprom_prepare_read(pdev, cmd++); + *ptr++ = dc390_eeprom_get_data(pdev); + + pci_write_config_byte(pdev, 0x80, 0); + pci_write_config_byte(pdev, 0x80, 0); + udelay(160); + } +} + +static void dc390_check_eeprom(struct esp *esp) +{ + u8 EEbuf[128]; + u16 *ptr = (u16 *)EEbuf, wval = 0; + int i; + + dc390_read_eeprom((struct pci_dev *)esp->dev, ptr); + + for (i = 0; i < DC390_EEPROM_LEN; i++, ptr++) + wval += *ptr; + + /* no Tekram EEprom found */ + if (wval != 0x1234) { + struct pci_dev *pdev = esp->dev; + dev_printk(KERN_INFO, &pdev->dev, + "No valid Tekram EEprom found\n"); + return; + } + esp->scsi_id = EEbuf[DC390_EE_ADAPT_SCSI_ID]; + esp->num_tags = 2 << EEbuf[DC390_EE_TAG_CMD_NUM]; + if (EEbuf[DC390_EE_MODE2] & DC390_EE_MODE2_ACTIVE_NEGATION) + esp->config4 |= ESP_CONFIG4_RADE | ESP_CONFIG4_RAE; +} + +static int pci_esp_probe_one(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct scsi_host_template *hostt = &scsi_esp_template; + int err = -ENODEV; + struct Scsi_Host *shost; + struct esp *esp; + struct pci_esp_priv *pep; + + if (pci_enable_device(pdev)) { + dev_printk(KERN_INFO, &pdev->dev, "cannot enable device\n"); + return -ENODEV; + } + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + dev_printk(KERN_INFO, &pdev->dev, + "failed to set 32bit DMA mask\n"); + goto fail_disable_device; + } + + shost = scsi_host_alloc(hostt, sizeof(struct esp)); + if (!shost) { + dev_printk(KERN_INFO, &pdev->dev, + "failed to allocate scsi host\n"); + err = -ENOMEM; + goto fail_disable_device; + } + + pep = kzalloc(sizeof(struct pci_esp_priv), GFP_KERNEL); + if (!pep) { + dev_printk(KERN_INFO, &pdev->dev, + "failed to allocate esp_priv\n"); + err = -ENOMEM; + goto fail_host_alloc; + } + + esp = shost_priv(shost); + esp->host = shost; + esp->dev = pdev; + esp->ops = &pci_esp_ops; + /* + * The am53c974 HBA has a design flaw of generating + * spurious DMA completion interrupts when using + * DMA for command submission. + */ + esp->flags |= ESP_FLAG_USE_FIFO; + /* + * Enable CONFIG2_FENAB to allow for large DMA transfers + */ + if (am53c974_fenab) + esp->config2 |= ESP_CONFIG2_FENAB; + + pep->esp = esp; + + if (pci_request_regions(pdev, DRV_MODULE_NAME)) { + dev_printk(KERN_ERR, &pdev->dev, + "pci memory selection failed\n"); + goto fail_priv_alloc; + } + + esp->regs = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!esp->regs) { + dev_printk(KERN_ERR, &pdev->dev, "pci I/O map failed\n"); + err = -EINVAL; + goto fail_release_regions; + } + esp->dma_regs = esp->regs; + + pci_set_master(pdev); + + esp->command_block = pci_alloc_consistent(pdev, 16, + &esp->command_block_dma); + if (!esp->command_block) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to allocate command block\n"); + err = -ENOMEM; + goto fail_unmap_regs; + } + + err = request_irq(pdev->irq, scsi_esp_intr, IRQF_SHARED, + DRV_MODULE_NAME, esp); + if (err < 0) { + dev_printk(KERN_ERR, &pdev->dev, "failed to register IRQ\n"); + goto fail_unmap_command_block; + } + + esp->scsi_id = 7; + dc390_check_eeprom(esp); + + shost->this_id = esp->scsi_id; + shost->max_id = 8; + shost->irq = pdev->irq; + shost->io_port = pci_resource_start(pdev, 0); + shost->n_io_port = pci_resource_len(pdev, 0); + shost->unique_id = shost->io_port; + esp->scsi_id_mask = (1 << esp->scsi_id); + /* Assume 40MHz clock */ + esp->cfreq = 40000000; + + pci_set_drvdata(pdev, pep); + + err = scsi_esp_register(esp, &pdev->dev); + if (err) + goto fail_free_irq; + + return 0; + +fail_free_irq: + free_irq(pdev->irq, esp); +fail_unmap_command_block: + pci_free_consistent(pdev, 16, esp->command_block, + esp->command_block_dma); +fail_unmap_regs: + pci_iounmap(pdev, esp->regs); +fail_release_regions: + pci_release_regions(pdev); +fail_priv_alloc: + kfree(pep); +fail_host_alloc: + scsi_host_put(shost); +fail_disable_device: + pci_disable_device(pdev); + + return err; +} + +static void pci_esp_remove_one(struct pci_dev *pdev) +{ + struct pci_esp_priv *pep = pci_get_drvdata(pdev); + struct esp *esp = pep->esp; + + scsi_esp_unregister(esp); + free_irq(pdev->irq, esp); + pci_free_consistent(pdev, 16, esp->command_block, + esp->command_block_dma); + pci_iounmap(pdev, esp->regs); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(pep); + + scsi_host_put(esp->host); +} + +static struct pci_device_id am53c974_pci_tbl[] = { + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { } +}; +MODULE_DEVICE_TABLE(pci, am53c974_pci_tbl); + +static struct pci_driver am53c974_driver = { + .name = DRV_MODULE_NAME, + .id_table = am53c974_pci_tbl, + .probe = pci_esp_probe_one, + .remove = pci_esp_remove_one, +}; + +static int __init am53c974_module_init(void) +{ + return pci_register_driver(&am53c974_driver); +} + +static void __exit am53c974_module_exit(void) +{ + pci_unregister_driver(&am53c974_driver); +} + +MODULE_DESCRIPTION("AM53C974 SCSI driver"); +MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); +MODULE_ALIAS("tmscsim"); + +module_param(am53c974_debug, bool, 0644); +MODULE_PARM_DESC(am53c974_debug, "Enable debugging"); + +module_param(am53c974_fenab, bool, 0444); +MODULE_PARM_DESC(am53c974_fenab, "Enable 24-bit DMA transfer sizes"); + +module_init(am53c974_module_init); +module_exit(am53c974_module_exit); diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 0b44fb5ee485..914c39f9f388 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -114,16 +114,11 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb); static const char *arcmsr_info(struct Scsi_Host *); static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb); static void arcmsr_free_irq(struct pci_dev *, struct AdapterControlBlock *); -static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, - int queue_depth, int reason) +static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth) { - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (queue_depth > ARCMSR_MAX_CMD_PERLUN) queue_depth = ARCMSR_MAX_CMD_PERLUN; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); - return queue_depth; + return scsi_change_queue_depth(sdev, queue_depth); } static struct scsi_host_template arcmsr_scsi_host_template = { diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c index d89b9b4deb3c..deaaf84989cd 100644 --- a/drivers/scsi/arm/acornscsi.c +++ b/drivers/scsi/arm/acornscsi.c @@ -850,13 +850,13 @@ static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, break; default: - printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", - host->host->host_no, SCpnt->result); - __scsi_print_command(SCpnt->cmnd); + scmd_printk(KERN_ERR, SCpnt, + "incomplete data transfer detected: " + "result=%08X", SCpnt->result); + scsi_print_command(SCpnt); acornscsi_dumpdma(host, "done"); - acornscsi_dumplog(host, SCpnt->device->id); - SCpnt->result &= 0xffff; - SCpnt->result |= DID_ERROR << 16; + acornscsi_dumplog(host, SCpnt->device->id); + set_host_byte(SCpnt, DID_ERROR); } } } diff --git a/drivers/scsi/arm/cumana_1.c b/drivers/scsi/arm/cumana_1.c index 8ef810a4476e..d28d6c0f18c0 100644 --- a/drivers/scsi/arm/cumana_1.c +++ b/drivers/scsi/arm/cumana_1.c @@ -13,16 +13,12 @@ #include <asm/ecard.h> #include <asm/io.h> -#include "../scsi.h" #include <scsi/scsi_host.h> #include <scsi/scsicam.h> -#define AUTOSENSE #define PSEUDO_DMA -#define CUMANASCSI_PUBLIC_RELEASE 1 - #define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata) #define NCR5380_local_declare() struct Scsi_Host *_instance #define NCR5380_setup(instance) _instance = instance @@ -30,6 +26,7 @@ #define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value) #define NCR5380_intr cumanascsi_intr #define NCR5380_queue_command cumanascsi_queue_command +#define NCR5380_info cumanascsi_info #define NCR5380_implementation_fields \ unsigned ctrl; \ @@ -42,11 +39,6 @@ void cumanascsi_setup(char *str, int *ints) { } -const char *cumanascsi_info(struct Scsi_Host *spnt) -{ - return ""; -} - #define CTRL 0x16fc #define STAT 0x2004 #define L(v) (((v)<<16)|((v) & 0x0000ffff)) @@ -267,14 +259,6 @@ static int cumanascsi1_probe(struct expansion_card *ec, goto out_unmap; } - printk("scsi%d: at port 0x%08lx irq %d", - host->host_no, host->io_port, host->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - ret = scsi_add_host(host, &ec->dev); if (ret) goto out_free_irq; diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index 71cfb1e504c4..e64c3af7c1a0 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -308,8 +308,7 @@ static void fas216_log_command(FAS216_Info *info, int level, fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); va_end(args); - printk(" CDB: "); - __scsi_print_command(SCpnt->cmnd); + scsi_print_command(SCpnt); } static void @@ -2079,14 +2078,12 @@ fas216_std_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, unsigned int result) break; default: - printk(KERN_ERR "scsi%d.%c: incomplete data transfer " - "detected: res=%08X ptr=%p len=%X CDB: ", - info->host->host_no, '0' + SCpnt->device->id, - SCpnt->result, info->scsi.SCp.ptr, - info->scsi.SCp.this_residual); - __scsi_print_command(SCpnt->cmnd); - SCpnt->result &= ~(255 << 16); - SCpnt->result |= DID_BAD_TARGET << 16; + scmd_printk(KERN_ERR, SCpnt, + "incomplete data transfer detected: res=%08X ptr=%p len=%X\n", + SCpnt->result, info->scsi.SCp.ptr, + info->scsi.SCp.this_residual); + scsi_print_command(SCpnt); + set_host_byte(SCpnt, DID_ERROR); goto request_sense; } } @@ -2158,12 +2155,11 @@ static void fas216_done(FAS216_Info *info, unsigned int result) * to transfer, we should not have a valid pointer. */ if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { - printk("scsi%d.%c: zero bytes left to transfer, but " - "buffer pointer still valid: ptr=%p len=%08x CDB: ", - info->host->host_no, '0' + SCpnt->device->id, - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + scmd_printk(KERN_INFO, SCpnt, + "zero bytes left to transfer, but buffer pointer still valid: ptr=%p len=%08x\n", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); info->scsi.SCp.ptr = NULL; - __scsi_print_command(SCpnt->cmnd); + scsi_print_command(SCpnt); } /* @@ -2427,14 +2423,11 @@ int fas216_eh_abort(struct scsi_cmnd *SCpnt) info->stats.aborts += 1; - printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no); - __scsi_print_command(SCpnt->cmnd); + scmd_printk(KERN_WARNING, SCpnt, "abort command\n"); print_debug_list(); fas216_dumpstate(info); - printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt); - switch (fas216_find_command(info, SCpnt)) { /* * We found the command, and cleared it out. Either @@ -2442,7 +2435,7 @@ int fas216_eh_abort(struct scsi_cmnd *SCpnt) * target, or the busylun bit is not set. */ case res_success: - printk("success\n"); + scmd_printk(KERN_WARNING, SCpnt, "abort %p success\n", SCpnt); result = SUCCESS; break; @@ -2452,14 +2445,13 @@ int fas216_eh_abort(struct scsi_cmnd *SCpnt) * if the bus is free. */ case res_hw_abort: - /* * We are unable to abort the command for some reason. */ default: case res_failed: - printk("failed\n"); + scmd_printk(KERN_WARNING, SCpnt, "abort %p failed\n", SCpnt); break; } @@ -2664,8 +2656,7 @@ int fas216_eh_host_reset(struct scsi_cmnd *SCpnt) fas216_checkmagic(info); - printk("scsi%d.%c: %s: resetting host\n", - info->host->host_no, '0' + SCpnt->device->id, __func__); + fas216_log(info, LOG_ERROR, "resetting host"); /* * Reset the SCSI chip. diff --git a/drivers/scsi/arm/oak.c b/drivers/scsi/arm/oak.c index 188e734c7ff0..7c6fa1479c9c 100644 --- a/drivers/scsi/arm/oak.c +++ b/drivers/scsi/arm/oak.c @@ -14,13 +14,9 @@ #include <asm/ecard.h> #include <asm/io.h> -#include "../scsi.h" #include <scsi/scsi_host.h> -#define AUTOSENSE /*#define PSEUDO_DMA*/ - -#define OAKSCSI_PUBLIC_RELEASE 1 #define DONT_USE_INTR #define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata) @@ -29,10 +25,9 @@ #define NCR5380_read(reg) readb(_base + ((reg) << 2)) #define NCR5380_write(reg, value) writeb(value, _base + ((reg) << 2)) -#define NCR5380_intr oakscsi_intr #define NCR5380_queue_command oakscsi_queue_command +#define NCR5380_info oakscsi_info #define NCR5380_show_info oakscsi_show_info -#define NCR5380_write_info oakscsi_write_info #define NCR5380_implementation_fields \ void __iomem *base @@ -42,11 +37,6 @@ #undef START_DMA_INITIATOR_RECEIVE_REG #define START_DMA_INITIATOR_RECEIVE_REG (128 + 7) -const char * oakscsi_info (struct Scsi_Host *spnt) -{ - return ""; -} - #define STAT ((128 + 16) << 2) #define DATA ((128 + 8) << 2) @@ -114,7 +104,6 @@ printk("reading %p len %d\n", addr, len); static struct scsi_host_template oakscsi_template = { .module = THIS_MODULE, .show_info = oakscsi_show_info, - .write_info = oakscsi_write_info, .name = "Oak 16-bit SCSI", .info = oakscsi_info, .queuecommand = oakscsi_queue_command, @@ -150,19 +139,11 @@ static int oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) goto unreg; } - host->irq = IRQ_NONE; + host->irq = NO_IRQ; host->n_io_port = 255; NCR5380_init(host, 0); - printk("scsi%d: at port 0x%08lx irqs disabled", - host->host_no, host->io_port); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - ret = scsi_add_host(host, &ec->dev); if (ret) goto out_unmap; diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c index 79e6f045c2a9..6daed6b386d4 100644 --- a/drivers/scsi/atari_NCR5380.c +++ b/drivers/scsi/atari_NCR5380.c @@ -11,8 +11,6 @@ * drew@colorado.edu * +1 (303) 666-5836 * - * DISTRIBUTION RELEASE 6. - * * For more information, please consult * * NCR 5380 Family @@ -73,6 +71,9 @@ * 1. Test linked command handling code after Eric is ready with * the high level code. */ + +/* Adapted for the sun3 by Sam Creasey. */ + #include <scsi/scsi_dbg.h> #include <scsi/scsi_transport_spi.h> @@ -103,27 +104,7 @@ /* * Design - * Issues : - * - * The other Linux SCSI drivers were written when Linux was Intel PC-only, - * and specifically for each board rather than each chip. This makes their - * adaptation to platforms like the Mac (Some of which use NCR5380's) - * more difficult than it has to be. * - * Also, many of the SCSI drivers were written before the command queuing - * routines were implemented, meaning their implementations of queued - * commands were hacked on rather than designed in from the start. - * - * When I designed the Linux SCSI drivers I figured that - * while having two different SCSI boards in a system might be useful - * for debugging things, two of the same type wouldn't be used. - * Well, I was wrong and a number of users have mailed me about running - * multiple high-performance SCSI boards in a server. - * - * Finally, when I get questions from users, I have no idea what - * revision of my driver they are running. - * - * This driver attempts to address these problems : * This is a generic 5380 driver. To use it on a different platform, * one simply writes appropriate system specific macros (ie, data * transfer - some PC's will use the I/O bus, 68K's must use @@ -138,17 +119,6 @@ * allowing multiple commands to propagate all the way to a SCSI-II device * while a command is already executing. * - * To solve the multiple-boards-in-the-same-system problem, - * there is a separate instance structure for each instance - * of a 5380 in the system. So, multiple NCR5380 drivers will - * be able to coexist with appropriate changes to the high level - * SCSI code. - * - * A NCR5380_PUBLIC_REVISION macro is provided, with the release - * number (updated for each public release) printed by the - * NCR5380_print_options command, which should be called from the - * wrapper detect function, so that I know what release of the driver - * users are using. * * Issues specific to the NCR5380 : * @@ -173,19 +143,17 @@ * Architecture : * * At the heart of the design is a coroutine, NCR5380_main, - * which is started when not running by the interrupt handler, - * timer, and queue command function. It attempts to establish - * I_T_L or I_T_L_Q nexuses by removing the commands from the - * issue queue and calling NCR5380_select() if a nexus - * is not established. + * which is started from a workqueue for each NCR5380 host in the + * system. It attempts to establish I_T_L or I_T_L_Q nexuses by + * removing the commands from the issue queue and calling + * NCR5380_select() if a nexus is not established. * * Once a nexus is established, the NCR5380_information_transfer() * phase goes through the various phases as instructed by the target. * if the target goes into MSG IN and sends a DISCONNECT message, * the command structure is placed into the per instance disconnected - * queue, and NCR5380_main tries to find more work. If USLEEP - * was defined, and the target is idle for too long, the system - * will try to sleep. + * queue, and NCR5380_main tries to find more work. If the target is + * idle for too long, the system will try to sleep. * * If a command has disconnected, eventually an interrupt will trigger, * calling NCR5380_intr() which will in turn call NCR5380_reselect @@ -211,6 +179,9 @@ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically * for commands that return with a CHECK CONDITION status. * + * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential + * transceivers. + * * LINKED - if defined, linked commands are supported. * * REAL_DMA - if defined, REAL DMA is used during the data transfer phases. @@ -223,6 +194,9 @@ * * NCR5380_write(register, value) - write to the specific register * + * NCR5380_implementation_fields - additional fields needed for this + * specific implementation of the NCR5380 + * * Either real DMA *or* pseudo DMA may be implemented * REAL functions : * NCR5380_REAL_DMA should be defined if real DMA is to be used. @@ -241,40 +215,21 @@ * NCR5380_pwrite(instance, src, count) * NCR5380_pread(instance, dst, count); * - * If nothing specific to this implementation needs doing (ie, with external - * hardware), you must also define - * - * NCR5380_queue_command - * NCR5380_reset - * NCR5380_abort - * NCR5380_proc_info - * - * to be the global entry points into the specific driver, ie - * #define NCR5380_queue_command t128_queue_command. - * - * If this is not done, the routines will be defined as static functions - * with the NCR5380* names and the user must provide a globally - * accessible wrapper function. - * * The generic driver is initialized by calling NCR5380_init(instance), * after setting the appropriate host specific fields and ID. If the * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance, - * possible) function may be used. Before the specific driver initialization - * code finishes, NCR5380_print_options should be called. + * possible) function may be used. */ -static struct Scsi_Host *first_instance = NULL; -static struct scsi_host_template *the_template = NULL; - /* Macros ease life... :-) */ #define SETUP_HOSTDATA(in) \ struct NCR5380_hostdata *hostdata = \ (struct NCR5380_hostdata *)(in)->hostdata #define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata) -#define NEXT(cmd) ((Scsi_Cmnd *)(cmd)->host_scribble) +#define NEXT(cmd) ((struct scsi_cmnd *)(cmd)->host_scribble) #define SET_NEXT(cmd,next) ((cmd)->host_scribble = (void *)(next)) -#define NEXTADDR(cmd) ((Scsi_Cmnd **)&(cmd)->host_scribble) +#define NEXTADDR(cmd) ((struct scsi_cmnd **)&(cmd)->host_scribble) #define HOSTNO instance->host_no #define H_NO(cmd) (cmd)->device->host->host_no @@ -316,30 +271,17 @@ static struct scsi_host_template *the_template = NULL; * important: the tag bit must be cleared before 'nr_allocated' is decreased. */ -/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */ -#undef TAG_NONE -#define TAG_NONE 0xff - -typedef struct { - DECLARE_BITMAP(allocated, MAX_TAGS); - int nr_allocated; - int queue_size; -} TAG_ALLOC; - -static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */ - - -static void __init init_tags(void) +static void __init init_tags(struct NCR5380_hostdata *hostdata) { int target, lun; - TAG_ALLOC *ta; + struct tag_alloc *ta; - if (!setup_use_tagged_queuing) + if (!(hostdata->flags & FLAG_TAGGED_QUEUING)) return; for (target = 0; target < 8; ++target) { for (lun = 0; lun < 8; ++lun) { - ta = &TagAlloc[target][lun]; + ta = &hostdata->TagAlloc[target][lun]; bitmap_zero(ta->allocated, MAX_TAGS); ta->nr_allocated = 0; /* At the beginning, assume the maximum queue size we could @@ -359,7 +301,7 @@ static void __init init_tags(void) * conditions. */ -static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged) +static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged) { u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); @@ -367,10 +309,11 @@ static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged) if (hostdata->busy[cmd->device->id] & (1 << lun)) return 1; if (!should_be_tagged || - !setup_use_tagged_queuing || !cmd->device->tagged_supported) + !(hostdata->flags & FLAG_TAGGED_QUEUING) || + !cmd->device->tagged_supported) return 0; - if (TagAlloc[cmd->device->id][lun].nr_allocated >= - TagAlloc[cmd->device->id][lun].queue_size) { + if (hostdata->TagAlloc[scmd_id(cmd)][lun].nr_allocated >= + hostdata->TagAlloc[scmd_id(cmd)][lun].queue_size) { dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n", H_NO(cmd), cmd->device->id, lun); return 1; @@ -384,7 +327,7 @@ static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged) * untagged. */ -static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) +static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged) { u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); @@ -393,13 +336,14 @@ static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) * an untagged command. */ if (!should_be_tagged || - !setup_use_tagged_queuing || !cmd->device->tagged_supported) { + !(hostdata->flags & FLAG_TAGGED_QUEUING) || + !cmd->device->tagged_supported) { cmd->tag = TAG_NONE; hostdata->busy[cmd->device->id] |= (1 << lun); dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged " "command\n", H_NO(cmd), cmd->device->id, lun); } else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; + struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][lun]; cmd->tag = find_first_zero_bit(ta->allocated, MAX_TAGS); set_bit(cmd->tag, ta->allocated); @@ -416,7 +360,7 @@ static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) * unlock the LUN. */ -static void cmd_free_tag(Scsi_Cmnd *cmd) +static void cmd_free_tag(struct scsi_cmnd *cmd) { u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); @@ -429,7 +373,7 @@ static void cmd_free_tag(Scsi_Cmnd *cmd) printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n", H_NO(cmd), cmd->tag); } else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; + struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][lun]; clear_bit(cmd->tag, ta->allocated); ta->nr_allocated--; dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n", @@ -438,17 +382,17 @@ static void cmd_free_tag(Scsi_Cmnd *cmd) } -static void free_all_tags(void) +static void free_all_tags(struct NCR5380_hostdata *hostdata) { int target, lun; - TAG_ALLOC *ta; + struct tag_alloc *ta; - if (!setup_use_tagged_queuing) + if (!(hostdata->flags & FLAG_TAGGED_QUEUING)) return; for (target = 0; target < 8; ++target) { for (lun = 0; lun < 8; ++lun) { - ta = &TagAlloc[target][lun]; + ta = &hostdata->TagAlloc[target][lun]; bitmap_zero(ta->allocated, MAX_TAGS); ta->nr_allocated = 0; } @@ -459,19 +403,20 @@ static void free_all_tags(void) /* - * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd ) + * Function: void merge_contiguous_buffers( struct scsi_cmnd *cmd ) * * Purpose: Try to merge several scatter-gather requests into one DMA * transfer. This is possible if the scatter buffers lie on * physical contiguous addresses. * - * Parameters: Scsi_Cmnd *cmd + * Parameters: struct scsi_cmnd *cmd * The command to work on. The first scatter buffer's data are * assumed to be already transferred into ptr/this_residual. */ -static void merge_contiguous_buffers(Scsi_Cmnd *cmd) +static void merge_contiguous_buffers(struct scsi_cmnd *cmd) { +#if !defined(CONFIG_SUN3) unsigned long endaddr; #if (NDEBUG & NDEBUG_MERGING) unsigned long oldlen = cmd->SCp.this_residual; @@ -496,18 +441,17 @@ static void merge_contiguous_buffers(Scsi_Cmnd *cmd) dprintk(NDEBUG_MERGING, "merged %d buffers from %p, new length %08x\n", cnt, cmd->SCp.ptr, cmd->SCp.this_residual); #endif +#endif /* !defined(CONFIG_SUN3) */ } -/* - * Function : void initialize_SCp(Scsi_Cmnd *cmd) +/** + * initialize_SCp - init the scsi pointer field + * @cmd: command block to set up * - * Purpose : initialize the saved data pointers for cmd to point to the - * start of the buffer. - * - * Inputs : cmd - Scsi_Cmnd structure to have pointers reset. + * Set up the internal fields in the SCSI command. */ -static inline void initialize_SCp(Scsi_Cmnd *cmd) +static inline void initialize_SCp(struct scsi_cmnd *cmd) { /* * Initialize the Scsi Pointer field so that all of the commands in the @@ -557,12 +501,11 @@ static struct { {0, NULL} }; -/* - * Function : void NCR5380_print(struct Scsi_Host *instance) +/** + * NCR5380_print - print scsi bus signals + * @instance: adapter state to dump * - * Purpose : print the SCSI bus signals for debugging purposes - * - * Input : instance - which NCR5380 + * Print the SCSI bus signals for debugging purposes */ static void NCR5380_print(struct Scsi_Host *instance) @@ -605,12 +548,13 @@ static struct { {PHASE_UNKNOWN, "UNKNOWN"} }; -/* - * Function : void NCR5380_print_phase(struct Scsi_Host *instance) +/** + * NCR5380_print_phase - show SCSI phase + * @instance: adapter to dump * - * Purpose : print the current SCSI phase for debugging purposes + * Print the current SCSI phase for debugging purposes * - * Input : instance - which NCR5380 + * Locks: none */ static void NCR5380_print_phase(struct Scsi_Host *instance) @@ -648,71 +592,75 @@ static void NCR5380_print_phase(struct Scsi_Host *instance) #include <linux/workqueue.h> #include <linux/interrupt.h> -static volatile int main_running; -static DECLARE_WORK(NCR5380_tqueue, NCR5380_main); - -static inline void queue_main(void) +static inline void queue_main(struct NCR5380_hostdata *hostdata) { - if (!main_running) { + if (!hostdata->main_running) { /* If in interrupt and NCR5380_main() not already running, queue it on the 'immediate' task queue, to be processed immediately after the current interrupt processing has finished. */ - schedule_work(&NCR5380_tqueue); + schedule_work(&hostdata->main_task); } /* else: nothing to do: the running NCR5380_main() will pick up any newly queued command. */ } - -static inline void NCR5380_all_init(void) -{ - static int done = 0; - if (!done) { - dprintk(NDEBUG_INIT, "scsi : NCR5380_all_init()\n"); - done = 1; - } -} - - -/* - * Function : void NCR58380_print_options (struct Scsi_Host *instance) +/** + * NCR58380_info - report driver and host information + * @instance: relevant scsi host instance * - * Purpose : called by probe code indicating the NCR5380 driver - * options that were selected. + * For use as the host template info() handler. * - * Inputs : instance, pointer to this instance. Unused. + * Locks: none */ -static void __init NCR5380_print_options(struct Scsi_Host *instance) +static const char *NCR5380_info(struct Scsi_Host *instance) +{ + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + return hostdata->info; +} + +static void prepare_info(struct Scsi_Host *instance) { - printk(" generic options" -#ifdef AUTOSENSE - " AUTOSENSE" + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + snprintf(hostdata->info, sizeof(hostdata->info), + "%s, io_port 0x%lx, n_io_port %d, " + "base 0x%lx, irq %d, " + "can_queue %d, cmd_per_lun %d, " + "sg_tablesize %d, this_id %d, " + "flags { %s}, " + "options { %s} ", + instance->hostt->name, instance->io_port, instance->n_io_port, + instance->base, instance->irq, + instance->can_queue, instance->cmd_per_lun, + instance->sg_tablesize, instance->this_id, + hostdata->flags & FLAG_TAGGED_QUEUING ? "TAGGED_QUEUING " : "", +#ifdef DIFFERENTIAL + "DIFFERENTIAL " #endif #ifdef REAL_DMA - " REAL DMA" + "REAL_DMA " #endif #ifdef PARITY - " PARITY" + "PARITY " #endif #ifdef SUPPORT_TAGS - " SCSI-2 TAGGED QUEUING" + "SUPPORT_TAGS " #endif - ); - printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); + ""); } -/* - * Function : void NCR5380_print_status (struct Scsi_Host *instance) +/** + * NCR5380_print_status - dump controller info + * @instance: controller to dump * - * Purpose : print commands in the various queues, called from - * NCR5380_abort and NCR5380_debug to aid debugging. - * - * Inputs : instance, pointer to this instance. + * Print commands in the various queues, called from NCR5380_abort + * to aid debugging. */ -static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd) +static void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd) { int i, s; unsigned char *command; @@ -729,7 +677,7 @@ static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd) static void NCR5380_print_status(struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata; - Scsi_Cmnd *ptr; + struct scsi_cmnd *ptr; unsigned long flags; NCR5380_dprint(NDEBUG_ANY, instance); @@ -737,20 +685,19 @@ static void NCR5380_print_status(struct Scsi_Host *instance) hostdata = (struct NCR5380_hostdata *)instance->hostdata; - printk("\nNCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE); local_irq_save(flags); printk("NCR5380: coroutine is%s running.\n", - main_running ? "" : "n't"); + hostdata->main_running ? "" : "n't"); if (!hostdata->connected) printk("scsi%d: no currently connected command\n", HOSTNO); else - lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected); + lprint_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected); printk("scsi%d: issue_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) + for (ptr = (struct scsi_cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) lprint_Scsi_Cmnd(ptr); printk("scsi%d: disconnected_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; + for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr; ptr = NEXT(ptr)) lprint_Scsi_Cmnd(ptr); @@ -758,7 +705,7 @@ static void NCR5380_print_status(struct Scsi_Host *instance) printk("\n"); } -static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m) +static void show_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m) { int i, s; unsigned char *command; @@ -772,28 +719,28 @@ static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m) seq_printf(m, "\n"); } -static int NCR5380_show_info(struct seq_file *m, struct Scsi_Host *instance) +static int __maybe_unused NCR5380_show_info(struct seq_file *m, + struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata; - Scsi_Cmnd *ptr; + struct scsi_cmnd *ptr; unsigned long flags; hostdata = (struct NCR5380_hostdata *)instance->hostdata; - seq_printf(m, "NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE); local_irq_save(flags); seq_printf(m, "NCR5380: coroutine is%s running.\n", - main_running ? "" : "n't"); + hostdata->main_running ? "" : "n't"); if (!hostdata->connected) seq_printf(m, "scsi%d: no currently connected command\n", HOSTNO); else - show_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, m); + show_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected, m); seq_printf(m, "scsi%d: issue_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) + for (ptr = (struct scsi_cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) show_Scsi_Cmnd(ptr, m); seq_printf(m, "scsi%d: disconnected_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; + for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr; ptr = NEXT(ptr)) show_Scsi_Cmnd(ptr, m); @@ -801,16 +748,18 @@ static int NCR5380_show_info(struct seq_file *m, struct Scsi_Host *instance) return 0; } -/* - * Function : void NCR5380_init (struct Scsi_Host *instance) +/** + * NCR5380_init - initialise an NCR5380 + * @instance: adapter to configure + * @flags: control flags * - * Purpose : initializes *instance and corresponding 5380 chip. - * - * Inputs : instance - instantiation of the 5380 driver. + * Initializes *instance and corresponding 5380 chip, + * with flags OR'd into the initial flags value. * * Notes : I assume that the host, hostno, and id bits have been - * set correctly. I don't care about the irq and other fields. + * set correctly. I don't care about the irq and other fields. * + * Returns 0 for success */ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) @@ -818,8 +767,7 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) int i; SETUP_HOSTDATA(instance); - NCR5380_all_init(); - + hostdata->host = instance; hostdata->aborted = 0; hostdata->id_mask = 1 << instance->this_id; hostdata->id_higher_mask = 0; @@ -829,7 +777,7 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) for (i = 0; i < 8; ++i) hostdata->busy[i] = 0; #ifdef SUPPORT_TAGS - init_tags(); + init_tags(hostdata); #endif #if defined (REAL_DMA) hostdata->dma_len = 0; @@ -838,19 +786,11 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) hostdata->connected = NULL; hostdata->issue_queue = NULL; hostdata->disconnected_queue = NULL; - hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT; + hostdata->flags = flags; - if (!the_template) { - the_template = instance->hostt; - first_instance = instance; - } + INIT_WORK(&hostdata->main_task, NCR5380_main); -#ifndef AUTOSENSE - if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1)) - printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n" - " without AUTOSENSE option, contingent allegiance conditions may\n" - " be incorrectly cleared.\n", HOSTNO); -#endif /* def AUTOSENSE */ + prepare_info(instance); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_write(MODE_REG, MR_BASE); @@ -860,33 +800,35 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) return 0; } +/** + * NCR5380_exit - remove an NCR5380 + * @instance: adapter to remove + * + * Assumes that no more work can be queued (e.g. by NCR5380_intr). + */ + static void NCR5380_exit(struct Scsi_Host *instance) { - /* Empty, as we didn't schedule any delayed work */ + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + cancel_work_sync(&hostdata->main_task); } -/* - * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, - * void (*done)(Scsi_Cmnd *)) - * - * Purpose : enqueues a SCSI command - * - * Inputs : cmd - SCSI command, done - function called on completion, with - * a pointer to the command descriptor. - * - * Returns : 0 - * - * Side effects : - * cmd is added to the per instance issue_queue, with minor - * twiddling done to the host specific fields of cmd. If the - * main coroutine is not running, it is restarted. +/** + * NCR5380_queue_command - queue a command + * @instance: the relevant SCSI adapter + * @cmd: SCSI command * + * cmd is added to the per instance issue_queue, with minor + * twiddling done to the host specific fields of cmd. If the + * main coroutine is not running, it is restarted. */ -static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) +static int NCR5380_queue_command(struct Scsi_Host *instance, + struct scsi_cmnd *cmd) { - SETUP_HOSTDATA(cmd->device->host); - Scsi_Cmnd *tmp; + struct NCR5380_hostdata *hostdata = shost_priv(instance); + struct scsi_cmnd *tmp; unsigned long flags; #if (NDEBUG & NDEBUG_NO_WRITE) @@ -896,47 +838,17 @@ static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", H_NO(cmd)); cmd->result = (DID_ERROR << 16); - done(cmd); + cmd->scsi_done(cmd); return 0; } #endif /* (NDEBUG & NDEBUG_NO_WRITE) */ -#ifdef NCR5380_STATS -# if 0 - if (!hostdata->connected && !hostdata->issue_queue && - !hostdata->disconnected_queue) { - hostdata->timebase = jiffies; - } -# endif -# ifdef NCR5380_STAT_LIMIT - if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT) -# endif - switch (cmd->cmnd[0]) { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase); - hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd); - hostdata->pendingw++; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase); - hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd); - hostdata->pendingr++; - break; - } -#endif - /* * We use the host_scribble field as a pointer to the next command * in a queue */ SET_NEXT(cmd, NULL); - cmd->scsi_done = done; - cmd->result = 0; /* @@ -946,7 +858,6 @@ static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) * sense data is only guaranteed to be valid while the condition exists. */ - local_irq_save(flags); /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA. * Otherwise a running NCR5380_main may steal the lock. * Lock before actually inserting due to fairness reasons explained in @@ -959,17 +870,24 @@ static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) * because also a timer int can trigger an abort or reset, which would * alter queues and touch the lock. */ - if (!IS_A_TT()) { - /* perhaps stop command timer here */ - falcon_get_lock(); - /* perhaps restart command timer here */ - } + if (!NCR5380_acquire_dma_irq(instance)) + return SCSI_MLQUEUE_HOST_BUSY; + + local_irq_save(flags); + + /* + * Insert the cmd into the issue queue. Note that REQUEST SENSE + * commands are added to the head of the queue since any command will + * clear the contingent allegiance condition that exists and the + * sense data is only guaranteed to be valid while the condition exists. + */ + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { LIST(cmd, hostdata->issue_queue); SET_NEXT(cmd, hostdata->issue_queue); hostdata->issue_queue = cmd; } else { - for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; + for (tmp = (struct scsi_cmnd *)hostdata->issue_queue; NEXT(tmp); tmp = NEXT(tmp)) ; LIST(cmd, tmp); @@ -987,32 +905,42 @@ static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) * If we're not in an interrupt, we can call NCR5380_main() * unconditionally, because it cannot be already running. */ - if (in_interrupt() || ((flags >> 8) & 7) >= 6) - queue_main(); + if (in_interrupt() || irqs_disabled()) + queue_main(hostdata); else - NCR5380_main(NULL); + NCR5380_main(&hostdata->main_task); return 0; } -static DEF_SCSI_QCMD(NCR5380_queue_command) +static inline void maybe_release_dma_irq(struct Scsi_Host *instance) +{ + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + /* Caller does the locking needed to set & test these data atomically */ + if (!hostdata->disconnected_queue && + !hostdata->issue_queue && + !hostdata->connected && + !hostdata->retain_dma_intr) + NCR5380_release_dma_irq(instance); +} -/* - * Function : NCR5380_main (void) +/** + * NCR5380_main - NCR state machines * - * Purpose : NCR5380_main is a coroutine that runs as long as more work can - * be done on the NCR5380 host adapters in a system. Both - * NCR5380_queue_command() and NCR5380_intr() will try to start it - * in case it is not running. + * NCR5380_main is a coroutine that runs as long as more work can + * be done on the NCR5380 host adapters in a system. Both + * NCR5380_queue_command() and NCR5380_intr() will try to start it + * in case it is not running. * - * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should - * reenable them. This prevents reentrancy and kernel stack overflow. + * Locks: called as its own thread with no locks held. */ static void NCR5380_main(struct work_struct *work) { - Scsi_Cmnd *tmp, *prev; - struct Scsi_Host *instance = first_instance; - struct NCR5380_hostdata *hostdata = HOSTDATA(instance); + struct NCR5380_hostdata *hostdata = + container_of(work, struct NCR5380_hostdata, main_task); + struct Scsi_Host *instance = hostdata->host; + struct scsi_cmnd *tmp, *prev; int done; unsigned long flags; @@ -1037,9 +965,9 @@ static void NCR5380_main(struct work_struct *work) 'main_running' is set here, and queues/executes main via the task queue, it doesn't do any harm, just this instance of main won't find any work left to do. */ - if (main_running) + if (hostdata->main_running) return; - main_running = 1; + hostdata->main_running = 1; local_save_flags(flags); do { @@ -1053,7 +981,7 @@ static void NCR5380_main(struct work_struct *work) * for a target that's not busy. */ #if (NDEBUG & NDEBUG_LISTS) - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; + for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL; tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp)) ; /*printk("%p ", tmp);*/ @@ -1061,16 +989,14 @@ static void NCR5380_main(struct work_struct *work) printk(" LOOP\n"); /* else printk("\n"); */ #endif - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, + for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) { u8 lun = tmp->device->lun; -#if (NDEBUG & NDEBUG_LISTS) - if (prev != tmp) - printk("MAIN tmp=%p target=%d busy=%d lun=%llu\n", - tmp, tmp->device->id, hostdata->busy[tmp->device->id], - lun); -#endif + dprintk(NDEBUG_LISTS, + "MAIN tmp=%p target=%d busy=%d lun=%d\n", + tmp, scmd_id(tmp), hostdata->busy[scmd_id(tmp)], + lun); /* When we find one, remove it from the issue queue. */ /* ++guenther: possible race with Falcon locking */ if ( @@ -1090,7 +1016,7 @@ static void NCR5380_main(struct work_struct *work) hostdata->issue_queue = NEXT(tmp); } SET_NEXT(tmp, NULL); - falcon_dont_release++; + hostdata->retain_dma_intr++; /* reenable interrupts after finding one */ local_irq_restore(flags); @@ -1117,12 +1043,12 @@ static void NCR5380_main(struct work_struct *work) #ifdef SUPPORT_TAGS cmd_get_tag(tmp, tmp->cmnd[0] != REQUEST_SENSE); #endif - if (!NCR5380_select(instance, tmp, - (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : - TAG_NEXT)) { - falcon_dont_release--; + if (!NCR5380_select(instance, tmp)) { + local_irq_disable(); + hostdata->retain_dma_intr--; /* release if target did not response! */ - falcon_release_lock_if_possible(hostdata); + maybe_release_dma_irq(instance); + local_irq_restore(flags); break; } else { local_irq_disable(); @@ -1132,7 +1058,7 @@ static void NCR5380_main(struct work_struct *work) #ifdef SUPPORT_TAGS cmd_free_tag(tmp); #endif - falcon_dont_release--; + hostdata->retain_dma_intr--; local_irq_restore(flags); dprintk(NDEBUG_MAIN, "scsi%d: main(): select() failed, " "returned to issue_queue\n", HOSTNO); @@ -1160,7 +1086,7 @@ static void NCR5380_main(struct work_struct *work) /* Better allow ints _after_ 'main_running' has been cleared, else an interrupt could believe we'll pick up the work it left for us, but we won't see it anymore here... */ - main_running = 0; + hostdata->main_running = 0; local_irq_restore(flags); } @@ -1179,9 +1105,11 @@ static void NCR5380_main(struct work_struct *work) static void NCR5380_dma_complete(struct Scsi_Host *instance) { SETUP_HOSTDATA(instance); - int transfered, saved_data = 0, overrun = 0, cnt, toPIO; - unsigned char **data, p; + int transferred; + unsigned char **data; volatile int *count; + int saved_data = 0, overrun = 0; + unsigned char p; if (!hostdata->connected) { printk(KERN_WARNING "scsi%d: received end of DMA interrupt with " @@ -1189,7 +1117,7 @@ static void NCR5380_dma_complete(struct Scsi_Host *instance) return; } - if (atari_read_overruns) { + if (hostdata->read_overruns) { p = hostdata->connected->SCp.phase; if (p & SR_IO) { udelay(10); @@ -1207,21 +1135,41 @@ static void NCR5380_dma_complete(struct Scsi_Host *instance) HOSTNO, NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)); +#if defined(CONFIG_SUN3) + if ((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) { + pr_err("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", + instance->host_no); + BUG(); + } + + /* make sure we're not stuck in a data phase */ + if ((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == + (BASR_PHASE_MATCH | BASR_ACK)) { + pr_err("scsi%d: BASR %02x\n", instance->host_no, + NCR5380_read(BUS_AND_STATUS_REG)); + pr_err("scsi%d: bus stuck in data phase -- probably a single byte overrun!\n", + instance->host_no); + BUG(); + } +#endif + (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - transfered = hostdata->dma_len - NCR5380_dma_residual(instance); + transferred = hostdata->dma_len - NCR5380_dma_residual(instance); hostdata->dma_len = 0; data = (unsigned char **)&hostdata->connected->SCp.ptr; count = &hostdata->connected->SCp.this_residual; - *data += transfered; - *count -= transfered; + *data += transferred; + *count -= transferred; + + if (hostdata->read_overruns) { + int cnt, toPIO; - if (atari_read_overruns) { if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) { - cnt = toPIO = atari_read_overruns; + cnt = toPIO = hostdata->read_overruns; if (overrun) { dprintk(NDEBUG_DMA, "Got an input overrun, using saved byte\n"); *(*data)++ = saved_data; @@ -1238,20 +1186,19 @@ static void NCR5380_dma_complete(struct Scsi_Host *instance) #endif /* REAL_DMA */ -/* - * Function : void NCR5380_intr (int irq) - * - * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses - * from the disconnected queue, and restarting NCR5380_main() - * as required. - * - * Inputs : int irq, irq that caused this interrupt. +/** + * NCR5380_intr - generic NCR5380 irq handler + * @irq: interrupt number + * @dev_id: device info * + * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses + * from the disconnected queue, and restarting NCR5380_main() + * as required. */ static irqreturn_t NCR5380_intr(int irq, void *dev_id) { - struct Scsi_Host *instance = first_instance; + struct Scsi_Host *instance = dev_id; int done = 1, handled = 0; unsigned char basr; @@ -1265,7 +1212,6 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id) NCR5380_dprint(NDEBUG_INTR, instance); if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) { done = 0; - ENABLE_IRQ(); dprintk(NDEBUG_INTR, "scsi%d: SEL interrupt\n", HOSTNO); NCR5380_reselect(instance); (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); @@ -1295,17 +1241,19 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id) dprintk(NDEBUG_INTR, "scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO); NCR5380_dma_complete( instance ); done = 0; - ENABLE_IRQ(); } else #endif /* REAL_DMA */ { /* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */ if (basr & BASR_PHASE_MATCH) - printk(KERN_NOTICE "scsi%d: unknown interrupt, " + dprintk(NDEBUG_INTR, "scsi%d: unknown interrupt, " "BASR 0x%x, MR 0x%x, SR 0x%x\n", HOSTNO, basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)); (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_DMA_ENABLE; +#endif } } /* if !(SELECTION || PARITY) */ handled = 1; @@ -1314,53 +1262,29 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id) "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)); (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_DMA_ENABLE; +#endif } if (!done) { dprintk(NDEBUG_INTR, "scsi%d: in int routine, calling main\n", HOSTNO); /* Put a call to NCR5380_main() on the queue... */ - queue_main(); + queue_main(shost_priv(instance)); } return IRQ_RETVAL(handled); } -#ifdef NCR5380_STATS -static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd *cmd) -{ -# ifdef NCR5380_STAT_LIMIT - if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT) -# endif - switch (cmd->cmnd[0]) { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase); - /*hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);*/ - hostdata->pendingw--; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase); - /*hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);*/ - hostdata->pendingr--; - break; - } -} -#endif - /* - * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, - * int tag); + * Function : int NCR5380_select(struct Scsi_Host *instance, + * struct scsi_cmnd *cmd) * * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command, * including ARBITRATION, SELECTION, and initial message out for * IDENTIFY and queue messages. * * Inputs : instance - instantiation of the 5380 driver on which this - * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for - * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for - * the command that is presently connected. + * target lives, cmd - SCSI command to execute. * * Returns : -1 if selection could not execute for some reason, * 0 if selection succeeded or failed because the target @@ -1380,7 +1304,7 @@ static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd *cmd) * cmd->result host byte set to DID_BAD_TARGET. */ -static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) +static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd) { SETUP_HOSTDATA(instance); unsigned char tmp[3], phase; @@ -1562,7 +1486,7 @@ static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) * selection. */ - timeout = jiffies + 25; + timeout = jiffies + (250 * HZ / 1000); /* * XXX very interesting - we're seeing a bounce where the BSY we @@ -1616,9 +1540,6 @@ static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) return -1; } cmd->result = DID_BAD_TARGET << 16; -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif #ifdef SUPPORT_TAGS cmd_free_tag(cmd); #endif @@ -1676,6 +1597,9 @@ static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) #ifndef SUPPORT_TAGS hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); #endif +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_INTR; +#endif initialize_SCp(cmd); @@ -1826,7 +1750,7 @@ static int NCR5380_transfer_pio(struct Scsi_Host *instance, * Returns : 0 on success, -1 on failure. */ -static int do_abort(struct Scsi_Host *host) +static int do_abort(struct Scsi_Host *instance) { unsigned char tmp, *msgptr, phase; int len; @@ -1861,7 +1785,7 @@ static int do_abort(struct Scsi_Host *host) msgptr = &tmp; len = 1; phase = PHASE_MSGOUT; - NCR5380_transfer_pio(host, &phase, &len, &msgptr); + NCR5380_transfer_pio(instance, &phase, &len, &msgptr); /* * If we got here, and the command completed successfully, @@ -1899,17 +1823,62 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, SETUP_HOSTDATA(instance); register int c = *count; register unsigned char p = *phase; + unsigned long flags; + +#if defined(CONFIG_SUN3) + /* sanity check */ + if (!sun3_dma_setup_done) { + pr_err("scsi%d: transfer_dma without setup!\n", + instance->host_no); + BUG(); + } + hostdata->dma_len = c; + + dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n", + instance->host_no, (p & SR_IO) ? "reading" : "writing", + c, (p & SR_IO) ? "to" : "from", *data); + + /* netbsd turns off ints here, why not be safe and do it too */ + local_irq_save(flags); + + /* send start chain */ + sun3scsi_dma_start(c, *data); + + if (p & SR_IO) { + NCR5380_write(TARGET_COMMAND_REG, 1); + NCR5380_read(RESET_PARITY_INTERRUPT_REG); + NCR5380_write(INITIATOR_COMMAND_REG, 0); + NCR5380_write(MODE_REG, + (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR)); + NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0); + } else { + NCR5380_write(TARGET_COMMAND_REG, 0); + NCR5380_read(RESET_PARITY_INTERRUPT_REG); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA); + NCR5380_write(MODE_REG, + (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR)); + NCR5380_write(START_DMA_SEND_REG, 0); + } + +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_DMA_ENABLE; +#endif + + local_irq_restore(flags); + + sun3_dma_active = 1; + +#else /* !defined(CONFIG_SUN3) */ register unsigned char *d = *data; unsigned char tmp; - unsigned long flags; if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) { *phase = tmp; return -1; } - if (atari_read_overruns && (p & SR_IO)) - c -= atari_read_overruns; + if (hostdata->read_overruns && (p & SR_IO)) + c -= hostdata->read_overruns; dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n", HOSTNO, (p & SR_IO) ? "reading" : "writing", @@ -1921,7 +1890,7 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY); #endif /* def REAL_DMA */ - if (IS_A_TT()) { + if (!(hostdata->flags & FLAG_LATE_DMA_SETUP)) { /* On the Medusa, it is a must to initialize the DMA before * starting the NCR. This is also the cleaner way for the TT. */ @@ -1939,7 +1908,7 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, NCR5380_write(START_DMA_SEND_REG, 0); } - if (!IS_A_TT()) { + if (hostdata->flags & FLAG_LATE_DMA_SETUP) { /* On the Falcon, the DMA setup must be done after the last */ /* NCR access, else the DMA setup gets trashed! */ @@ -1949,6 +1918,8 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, NCR5380_dma_write_setup(instance, d, c); local_irq_restore(flags); } +#endif /* !defined(CONFIG_SUN3) */ + return 0; } #endif /* defined(REAL_DMA) */ @@ -1982,7 +1953,11 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) #endif unsigned char *data; unsigned char phase, tmp, extended_msg[10], old_phase = 0xff; - Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected; + struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected; + +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_INTR; +#endif while (1) { tmp = NCR5380_read(STATUS_REG); @@ -1993,6 +1968,33 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) old_phase = phase; NCR5380_dprint_phase(NDEBUG_INFORMATION, instance); } +#if defined(CONFIG_SUN3) + if (phase == PHASE_CMDOUT) { +#if defined(REAL_DMA) + void *d; + unsigned long count; + + if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { + count = cmd->SCp.buffer->length; + d = sg_virt(cmd->SCp.buffer); + } else { + count = cmd->SCp.this_residual; + d = cmd->SCp.ptr; + } + /* this command setup for dma yet? */ + if ((count >= DMA_MIN_SIZE) && (sun3_dma_setup_done != cmd)) { + if (cmd->request->cmd_type == REQ_TYPE_FS) { + sun3scsi_dma_setup(d, count, + rq_data_dir(cmd->request)); + sun3_dma_setup_done = cmd; + } + } +#endif +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_INTR; +#endif + } +#endif /* CONFIG_SUN3 */ if (sink && (phase != PHASE_MSGOUT)) { NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); @@ -2054,8 +2056,11 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) */ #if defined(REAL_DMA) - if (!cmd->device->borken && - (transfersize = NCR5380_dma_xfer_len(instance,cmd,phase)) > 31) { + if ( +#if !defined(CONFIG_SUN3) + !cmd->device->borken && +#endif + (transfersize = NCR5380_dma_xfer_len(instance, cmd, phase)) >= DMA_MIN_SIZE) { len = transfersize; cmd->SCp.phase = phase; if (NCR5380_transfer_dma(instance, &phase, @@ -2064,9 +2069,8 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) * If the watchdog timer fires, all future * accesses to this device will use the * polled-IO. */ - printk(KERN_NOTICE "scsi%d: switching target %d " - "lun %llu to slow handshake\n", HOSTNO, - cmd->device->id, cmd->device->lun); + scmd_printk(KERN_INFO, cmd, + "switching to slow handshake\n"); cmd->device->borken = 1; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); @@ -2092,6 +2096,11 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) NCR5380_transfer_pio(instance, &phase, (int *)&cmd->SCp.this_residual, (unsigned char **)&cmd->SCp.ptr); +#if defined(CONFIG_SUN3) && defined(REAL_DMA) + /* if we had intended to dma that command clear it */ + if (sun3_dma_setup_done == cmd) + sun3_dma_setup_done = NULL; +#endif break; case PHASE_MSGIN: len = 1; @@ -2145,9 +2154,6 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked request " "done, calling scsi_done().\n", HOSTNO, cmd->device->id, cmd->device->lun); -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif cmd->scsi_done(cmd); cmd = hostdata->connected; break; @@ -2156,11 +2162,12 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) case COMMAND_COMPLETE: /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - /* ++guenther: possible race with Falcon locking */ - falcon_dont_release++; - hostdata->connected = NULL; dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu " "completed\n", HOSTNO, cmd->device->id, cmd->device->lun); + + local_irq_save(flags); + hostdata->retain_dma_intr++; + hostdata->connected = NULL; #ifdef SUPPORT_TAGS cmd_free_tag(cmd); if (status_byte(cmd->SCp.Status) == QUEUE_FULL) { @@ -2172,7 +2179,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) */ /* ++Andreas: the mid level code knows about QUEUE_FULL now. */ - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; + struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][cmd->device->lun]; dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu returned " "QUEUE_FULL after %d commands\n", HOSTNO, cmd->device->id, cmd->device->lun, @@ -2207,7 +2214,6 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) else if (status_byte(cmd->SCp.Status) != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); -#ifdef AUTOSENSE if ((cmd->cmnd[0] == REQUEST_SENSE) && hostdata->ses.cmd_len) { scsi_eh_restore_cmnd(cmd, &hostdata->ses); @@ -2220,22 +2226,17 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) dprintk(NDEBUG_AUTOSENSE, "scsi%d: performing request sense\n", HOSTNO); - local_irq_save(flags); LIST(cmd,hostdata->issue_queue); SET_NEXT(cmd, hostdata->issue_queue); - hostdata->issue_queue = (Scsi_Cmnd *) cmd; - local_irq_restore(flags); + hostdata->issue_queue = (struct scsi_cmnd *) cmd; dprintk(NDEBUG_QUEUES, "scsi%d: REQUEST SENSE added to head of " "issue queue\n", H_NO(cmd)); - } else -#endif /* def AUTOSENSE */ - { -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif + } else { cmd->scsi_done(cmd); } + local_irq_restore(flags); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); /* * Restore phase bits to 0 so an interrupted selection, @@ -2246,12 +2247,14 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) barrier(); - falcon_dont_release--; + local_irq_save(flags); + hostdata->retain_dma_intr--; /* ++roman: For Falcon SCSI, release the lock on the * ST-DMA here if no other commands are waiting on the * disconnected queue. */ - falcon_release_lock_if_possible(hostdata); + maybe_release_dma_irq(instance); + local_irq_restore(flags); return; case MESSAGE_REJECT: /* Accept message by clearing ACK */ @@ -2303,6 +2306,9 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) /* Wait for bus free to avoid nasty timeouts */ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) barrier(); +#ifdef SUN3_SCSI_VME + dregs->csr |= CSR_DMA_ENABLE; +#endif return; /* * The SCSI data pointer is *IMPLICITLY* saved on a disconnect @@ -2384,20 +2390,18 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) */ default: if (!tmp) { - printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO); + printk(KERN_INFO "scsi%d: rejecting message ", + instance->host_no); spi_print_msg(extended_msg); printk("\n"); } else if (tmp != EXTENDED_MESSAGE) - printk(KERN_DEBUG "scsi%d: rejecting unknown " - "message %02x from target %d, lun %llu\n", - HOSTNO, tmp, cmd->device->id, cmd->device->lun); + scmd_printk(KERN_INFO, cmd, + "rejecting unknown message %02x\n", + tmp); else - printk(KERN_DEBUG "scsi%d: rejecting unknown " - "extended message " - "code %02x, length %d from target %d, lun %llu\n", - HOSTNO, extended_msg[1], extended_msg[0], - cmd->device->id, cmd->device->lun); - + scmd_printk(KERN_INFO, cmd, + "rejecting unknown extended message code %02x, length %d\n", + extended_msg[1], extended_msg[0]); msgout = MESSAGE_REJECT; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); @@ -2410,6 +2414,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) hostdata->last_message = msgout; NCR5380_transfer_pio(instance, &phase, &len, &data); if (msgout == ABORT) { + local_irq_save(flags); #ifdef SUPPORT_TAGS cmd_free_tag(cmd); #else @@ -2417,12 +2422,10 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) #endif hostdata->connected = NULL; cmd->result = DID_ERROR << 16; -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - falcon_release_lock_if_possible(hostdata); + maybe_release_dma_irq(instance); + local_irq_restore(flags); + cmd->scsi_done(cmd); return; } msgout = NOP; @@ -2455,7 +2458,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) * Function : void NCR5380_reselect (struct Scsi_Host *instance) * * Purpose : does reselection, initializing the instance->connected - * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q + * field to point to the scsi_cmnd for which the I_T_L or I_T_L_Q * nexus has been reestablished, * * Inputs : instance - this instance of the NCR5380. @@ -2463,19 +2466,21 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) */ +/* it might eventually prove necessary to do a dma setup on + reselection, but it doesn't seem to be needed now -- sam */ + static void NCR5380_reselect(struct Scsi_Host *instance) { SETUP_HOSTDATA(instance); unsigned char target_mask; - unsigned char lun, phase; - int len; + unsigned char lun; #ifdef SUPPORT_TAGS unsigned char tag; #endif unsigned char msg[3]; - unsigned char *data; - Scsi_Cmnd *tmp = NULL, *prev; -/* unsigned long flags; */ + int __maybe_unused len; + unsigned char __maybe_unused *data, __maybe_unused phase; + struct scsi_cmnd *tmp = NULL, *prev; /* * Disable arbitration, etc. since the host adapter obviously @@ -2511,10 +2516,18 @@ static void NCR5380_reselect(struct Scsi_Host *instance) while (!(NCR5380_read(STATUS_REG) & SR_REQ)) ; +#if defined(CONFIG_SUN3) && defined(REAL_DMA) + /* acknowledge toggle to MSGIN */ + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN)); + + /* peek at the byte without really hitting the bus */ + msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG); +#else len = 1; data = msg; phase = PHASE_MSGIN; NCR5380_transfer_pio(instance, &phase, &len, &data); +#endif if (!(msg[0] & 0x80)) { printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO); @@ -2524,13 +2537,13 @@ static void NCR5380_reselect(struct Scsi_Host *instance) } lun = (msg[0] & 0x07); -#ifdef SUPPORT_TAGS +#if defined(SUPPORT_TAGS) && !defined(CONFIG_SUN3) /* If the phase is still MSGIN, the target wants to send some more * messages. In case it supports tagged queuing, this is probably a * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus. */ tag = TAG_NONE; - if (phase == PHASE_MSGIN && setup_use_tagged_queuing) { + if (phase == PHASE_MSGIN && (hostdata->flags & FLAG_TAGGED_QUEUING)) { /* Accept previous IDENTIFY message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); len = 2; @@ -2548,15 +2561,13 @@ static void NCR5380_reselect(struct Scsi_Host *instance) * just reestablished, and remove it from the disconnected queue. */ - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; + for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) { if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun) #ifdef SUPPORT_TAGS && (tag == tmp->tag) #endif ) { - /* ++guenther: prevent race with falcon_release_lock */ - falcon_dont_release++; if (prev) { REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); SET_NEXT(prev, NEXT(tmp)); @@ -2588,26 +2599,67 @@ static void NCR5380_reselect(struct Scsi_Host *instance) return; } +#if defined(CONFIG_SUN3) && defined(REAL_DMA) + /* engage dma setup for the command we just saw */ + { + void *d; + unsigned long count; + + if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) { + count = tmp->SCp.buffer->length; + d = sg_virt(tmp->SCp.buffer); + } else { + count = tmp->SCp.this_residual; + d = tmp->SCp.ptr; + } + /* setup this command for dma if not already */ + if ((count >= DMA_MIN_SIZE) && (sun3_dma_setup_done != tmp)) { + sun3scsi_dma_setup(d, count, rq_data_dir(tmp->request)); + sun3_dma_setup_done = tmp; + } + } + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); +#endif + /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); +#if defined(SUPPORT_TAGS) && defined(CONFIG_SUN3) + /* If the phase is still MSGIN, the target wants to send some more + * messages. In case it supports tagged queuing, this is probably a + * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus. + */ + tag = TAG_NONE; + if (phase == PHASE_MSGIN && setup_use_tagged_queuing) { + /* Accept previous IDENTIFY message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + len = 2; + data = msg + 1; + if (!NCR5380_transfer_pio(instance, &phase, &len, &data) && + msg[1] == SIMPLE_QUEUE_TAG) + tag = msg[2]; + dprintk(NDEBUG_TAGS, "scsi%d: target mask %02x, lun %d sent tag %d at reselection\n" + HOSTNO, target_mask, lun, tag); + } +#endif + hostdata->connected = tmp; dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n", HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag); - falcon_dont_release--; } /* - * Function : int NCR5380_abort (Scsi_Cmnd *cmd) + * Function : int NCR5380_abort (struct scsi_cmnd *cmd) * * Purpose : abort a command * - * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the + * Inputs : cmd - the scsi_cmnd to abort, code - code to set the * host byte of the result field to, if zero DID_ABORTED is * used. * - * Returns : 0 - success, -1 on failure. + * Returns : SUCCESS - success, FAILED on failure. * * XXX - there is no way to abort the command that is currently * connected, you have to wait for it to complete. If this is @@ -2616,24 +2668,19 @@ static void NCR5380_reselect(struct Scsi_Host *instance) */ static -int NCR5380_abort(Scsi_Cmnd *cmd) +int NCR5380_abort(struct scsi_cmnd *cmd) { struct Scsi_Host *instance = cmd->device->host; SETUP_HOSTDATA(instance); - Scsi_Cmnd *tmp, **prev; + struct scsi_cmnd *tmp, **prev; unsigned long flags; - printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO); - scsi_print_command(cmd); + scmd_printk(KERN_NOTICE, cmd, "aborting command\n"); NCR5380_print_status(instance); local_irq_save(flags); - if (!IS_A_TT() && !falcon_got_lock) - printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_abort\n", - HOSTNO); - dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)); @@ -2674,12 +2721,12 @@ int NCR5380_abort(Scsi_Cmnd *cmd) #else hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); #endif + maybe_release_dma_irq(instance); local_irq_restore(flags); cmd->scsi_done(cmd); - falcon_release_lock_if_possible(hostdata); return SUCCESS; } else { -/* local_irq_restore(flags); */ + local_irq_restore(flags); printk("scsi%d: abort of connected command failed!\n", HOSTNO); return FAILED; } @@ -2690,21 +2737,21 @@ int NCR5380_abort(Scsi_Cmnd *cmd) * Case 2 : If the command hasn't been issued yet, we simply remove it * from the issue queue. */ - for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue), - tmp = (Scsi_Cmnd *)hostdata->issue_queue; + for (prev = (struct scsi_cmnd **)&(hostdata->issue_queue), + tmp = (struct scsi_cmnd *)hostdata->issue_queue; tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) { if (cmd == tmp) { REMOVE(5, *prev, tmp, NEXT(tmp)); (*prev) = NEXT(tmp); SET_NEXT(tmp, NULL); tmp->result = DID_ABORT << 16; + maybe_release_dma_irq(instance); local_irq_restore(flags); dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n", HOSTNO); /* Tagged queuing note: no tag to free here, hasn't been assigned * yet... */ tmp->scsi_done(tmp); - falcon_release_lock_if_possible(hostdata); return SUCCESS; } } @@ -2751,13 +2798,13 @@ int NCR5380_abort(Scsi_Cmnd *cmd) * it from the disconnected queue. */ - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; + for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; tmp = NEXT(tmp)) { if (cmd == tmp) { local_irq_restore(flags); dprintk(NDEBUG_ABORT, "scsi%d: aborting disconnected command.\n", HOSTNO); - if (NCR5380_select(instance, cmd, (int)cmd->tag)) + if (NCR5380_select(instance, cmd)) return FAILED; dprintk(NDEBUG_ABORT, "scsi%d: nexus reestablished.\n", HOSTNO); @@ -2765,8 +2812,8 @@ int NCR5380_abort(Scsi_Cmnd *cmd) do_abort(instance); local_irq_save(flags); - for (prev = (Scsi_Cmnd **)&(hostdata->disconnected_queue), - tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; + for (prev = (struct scsi_cmnd **)&(hostdata->disconnected_queue), + tmp = (struct scsi_cmnd *)hostdata->disconnected_queue; tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) { if (cmd == tmp) { REMOVE(5, *prev, tmp, NEXT(tmp)); @@ -2782,15 +2829,22 @@ int NCR5380_abort(Scsi_Cmnd *cmd) #else hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); #endif + maybe_release_dma_irq(instance); local_irq_restore(flags); tmp->scsi_done(tmp); - falcon_release_lock_if_possible(hostdata); return SUCCESS; } } } } + /* Maybe it is sufficient just to release the ST-DMA lock... (if + * possible at all) At least, we should check if the lock could be + * released after the abort, in case it is kept due to some bug. + */ + maybe_release_dma_irq(instance); + local_irq_restore(flags); + /* * Case 5 : If we reached this point, the command was not found in any of * the queues. @@ -2801,21 +2855,14 @@ int NCR5380_abort(Scsi_Cmnd *cmd) * broke. */ - local_irq_restore(flags); printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO); - /* Maybe it is sufficient just to release the ST-DMA lock... (if - * possible at all) At least, we should check if the lock could be - * released after the abort, in case it is kept due to some bug. - */ - falcon_release_lock_if_possible(hostdata); - return FAILED; } /* - * Function : int NCR5380_reset (Scsi_Cmnd *cmd) + * Function : int NCR5380_reset (struct scsi_cmnd *cmd) * * Purpose : reset the SCSI bus. * @@ -2823,20 +2870,14 @@ int NCR5380_abort(Scsi_Cmnd *cmd) * */ -static int NCR5380_bus_reset(Scsi_Cmnd *cmd) +static int NCR5380_bus_reset(struct scsi_cmnd *cmd) { - SETUP_HOSTDATA(cmd->device->host); + struct Scsi_Host *instance = cmd->device->host; + struct NCR5380_hostdata *hostdata = shost_priv(instance); int i; unsigned long flags; -#if defined(RESET_RUN_DONE) - Scsi_Cmnd *connected, *disconnected_queue; -#endif - - if (!IS_A_TT() && !falcon_got_lock) - printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_reset\n", - H_NO(cmd)); - NCR5380_print_status(cmd->device->host); + NCR5380_print_status(instance); /* get in phase */ NCR5380_write(TARGET_COMMAND_REG, @@ -2853,89 +2894,6 @@ static int NCR5380_bus_reset(Scsi_Cmnd *cmd) * through anymore ... */ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); - /* MSch 20140115 - looking at the generic NCR5380 driver, all of this - * should go. - * Catch-22: if we don't clear all queues, the SCSI driver lock will - * not be reset by atari_scsi_reset()! - */ - -#if defined(RESET_RUN_DONE) - /* XXX Should now be done by midlevel code, but it's broken XXX */ - /* XXX see below XXX */ - - /* MSch: old-style reset: actually abort all command processing here */ - - /* After the reset, there are no more connected or disconnected commands - * and no busy units; to avoid problems with re-inserting the commands - * into the issue_queue (via scsi_done()), the aborted commands are - * remembered in local variables first. - */ - local_irq_save(flags); - connected = (Scsi_Cmnd *)hostdata->connected; - hostdata->connected = NULL; - disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue; - hostdata->disconnected_queue = NULL; -#ifdef SUPPORT_TAGS - free_all_tags(); -#endif - for (i = 0; i < 8; ++i) - hostdata->busy[i] = 0; -#ifdef REAL_DMA - hostdata->dma_len = 0; -#endif - local_irq_restore(flags); - - /* In order to tell the mid-level code which commands were aborted, - * set the command status to DID_RESET and call scsi_done() !!! - * This ultimately aborts processing of these commands in the mid-level. - */ - - if ((cmd = connected)) { - dprintk(NDEBUG_ABORT, "scsi%d: reset aborted a connected command\n", H_NO(cmd)); - cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); - cmd->scsi_done(cmd); - } - - for (i = 0; (cmd = disconnected_queue); ++i) { - disconnected_queue = NEXT(cmd); - SET_NEXT(cmd, NULL); - cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); - cmd->scsi_done(cmd); - } - if (i > 0) - dprintk(NDEBUG_ABORT, "scsi: reset aborted %d disconnected command(s)\n", i); - - /* The Falcon lock should be released after a reset... - */ - /* ++guenther: moved to atari_scsi_reset(), to prevent a race between - * unlocking and enabling dma interrupt. - */ -/* falcon_release_lock_if_possible( hostdata );*/ - - /* since all commands have been explicitly terminated, we need to tell - * the midlevel code that the reset was SUCCESSFUL, and there is no - * need to 'wake up' the commands by a request_sense - */ - return SUCCESS; -#else /* 1 */ - - /* MSch: new-style reset handling: let the mid-level do what it can */ - - /* ++guenther: MID-LEVEL IS STILL BROKEN. - * Mid-level is supposed to requeue all commands that were active on the - * various low-level queues. In fact it does this, but that's not enough - * because all these commands are subject to timeout. And if a timeout - * happens for any removed command, *_abort() is called but all queues - * are now empty. Abort then gives up the falcon lock, which is fatal, - * since the mid-level will queue more commands and must have the lock - * (it's all happening inside timer interrupt handler!!). - * Even worse, abort will return NOT_RUNNING for all those commands not - * on any queue, so they won't be retried ... - * - * Conclusion: either scsi.c disables timeout for all resetted commands - * immediately, or we lose! As of linux-2.0.20 it doesn't. - */ - /* After the reset, there are no more connected or disconnected commands * and no busy units; so clear the low-level status here to avoid * conflicts when the mid-level code tries to wake up the affected @@ -2954,16 +2912,16 @@ static int NCR5380_bus_reset(Scsi_Cmnd *cmd) hostdata->connected = NULL; hostdata->disconnected_queue = NULL; #ifdef SUPPORT_TAGS - free_all_tags(); + free_all_tags(hostdata); #endif for (i = 0; i < 8; ++i) hostdata->busy[i] = 0; #ifdef REAL_DMA hostdata->dma_len = 0; #endif + + maybe_release_dma_irq(instance); local_irq_restore(flags); - /* we did no complete reset of all commands, so a wakeup is required */ return SUCCESS; -#endif /* 1 */ } diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index b522134528d6..d1c37a386947 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -64,45 +64,57 @@ /**************************************************************************/ - #include <linux/module.h> - -#define AUTOSENSE -/* For the Atari version, use only polled IO or REAL_DMA */ -#define REAL_DMA -/* Support tagged queuing? (on devices that are able to... :-) */ -#define SUPPORT_TAGS -#define MAX_TAGS 32 - #include <linux/types.h> -#include <linux/stddef.h> -#include <linux/ctype.h> #include <linux/delay.h> -#include <linux/mm.h> #include <linux/blkdev.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/nvram.h> #include <linux/bitops.h> #include <linux/wait.h> +#include <linux/platform_device.h> #include <asm/setup.h> #include <asm/atarihw.h> #include <asm/atariints.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/irq.h> -#include <asm/traps.h> - -#include "scsi.h" -#include <scsi/scsi_host.h> -#include "atari_scsi.h" -#include "NCR5380.h" #include <asm/atari_stdma.h> #include <asm/atari_stram.h> #include <asm/io.h> -#include <linux/stat.h> +#include <scsi/scsi_host.h> + +/* Definitions for the core NCR5380 driver. */ + +#define REAL_DMA +#define SUPPORT_TAGS +#define MAX_TAGS 32 +#define DMA_MIN_SIZE 32 + +#define NCR5380_implementation_fields /* none */ + +#define NCR5380_read(reg) atari_scsi_reg_read(reg) +#define NCR5380_write(reg, value) atari_scsi_reg_write(reg, value) + +#define NCR5380_queue_command atari_scsi_queue_command +#define NCR5380_abort atari_scsi_abort +#define NCR5380_show_info atari_scsi_show_info +#define NCR5380_info atari_scsi_info + +#define NCR5380_dma_read_setup(instance, data, count) \ + atari_scsi_dma_setup(instance, data, count, 0) +#define NCR5380_dma_write_setup(instance, data, count) \ + atari_scsi_dma_setup(instance, data, count, 1) +#define NCR5380_dma_residual(instance) \ + atari_scsi_dma_residual(instance) +#define NCR5380_dma_xfer_len(instance, cmd, phase) \ + atari_dma_xfer_len(cmd->SCp.this_residual, cmd, !((phase) & SR_IO)) + +#define NCR5380_acquire_dma_irq(instance) falcon_get_lock(instance) +#define NCR5380_release_dma_irq(instance) falcon_release_lock() + +#include "NCR5380.h" + #define IS_A_TT() ATARIHW_PRESENT(TT_SCSI) @@ -149,23 +161,6 @@ static inline unsigned long SCSI_DMA_GETADR(void) return adr; } -static inline void ENABLE_IRQ(void) -{ - if (IS_A_TT()) - atari_enable_irq(IRQ_TT_MFP_SCSI); - else - atari_enable_irq(IRQ_MFP_FSCSI); -} - -static inline void DISABLE_IRQ(void) -{ - if (IS_A_TT()) - atari_disable_irq(IRQ_TT_MFP_SCSI); - else - atari_disable_irq(IRQ_MFP_FSCSI); -} - - #define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \ (atari_scsi_host->hostdata))->dma_len) @@ -178,30 +173,9 @@ static inline void DISABLE_IRQ(void) #define AFTER_RESET_DELAY (5*HZ/2) #endif -/***************************** Prototypes *****************************/ - #ifdef REAL_DMA -static int scsi_dma_is_ignored_buserr(unsigned char dma_stat); static void atari_scsi_fetch_restbytes(void); -static long atari_scsi_dma_residual(struct Scsi_Host *instance); -static int falcon_classify_cmd(Scsi_Cmnd *cmd); -static unsigned long atari_dma_xfer_len(unsigned long wanted_len, - Scsi_Cmnd *cmd, int write_flag); -#endif -static irqreturn_t scsi_tt_intr(int irq, void *dummy); -static irqreturn_t scsi_falcon_intr(int irq, void *dummy); -static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata); -static void falcon_get_lock(void); -#ifdef CONFIG_ATARI_SCSI_RESET_BOOT -static void atari_scsi_reset_boot(void); #endif -static unsigned char atari_scsi_tt_reg_read(unsigned char reg); -static void atari_scsi_tt_reg_write(unsigned char reg, unsigned char value); -static unsigned char atari_scsi_falcon_reg_read(unsigned char reg); -static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value); - -/************************* End of Prototypes **************************/ - static struct Scsi_Host *atari_scsi_host; static unsigned char (*atari_scsi_reg_read)(unsigned char reg); @@ -226,8 +200,6 @@ static char *atari_dma_orig_addr; /* mask for address bits that can't be used with the ST-DMA */ static unsigned long atari_dma_stram_mask; #define STRAM_ADDR(a) (((a) & atari_dma_stram_mask) == 0) -/* number of bytes to cut from a transfer to handle NCR overruns */ -static int atari_read_overruns; #endif static int setup_can_queue = -1; @@ -386,10 +358,6 @@ static irqreturn_t scsi_tt_intr(int irq, void *dummy) NCR5380_intr(irq, dummy); -#if 0 - /* To be sure the int is not masked */ - atari_enable_irq(IRQ_TT_MFP_SCSI); -#endif return IRQ_HANDLED; } @@ -480,257 +448,35 @@ static void atari_scsi_fetch_restbytes(void) #endif /* REAL_DMA */ -static int falcon_got_lock = 0; -static DECLARE_WAIT_QUEUE_HEAD(falcon_fairness_wait); -static int falcon_trying_lock = 0; -static DECLARE_WAIT_QUEUE_HEAD(falcon_try_wait); -static int falcon_dont_release = 0; - /* This function releases the lock on the DMA chip if there is no - * connected command and the disconnected queue is empty. On - * releasing, instances of falcon_get_lock are awoken, that put - * themselves to sleep for fairness. They can now try to get the lock - * again (but others waiting longer more probably will win). + * connected command and the disconnected queue is empty. */ -static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata) +static void falcon_release_lock(void) { - unsigned long flags; - if (IS_A_TT()) return; - local_irq_save(flags); - - if (falcon_got_lock && !hostdata->disconnected_queue && - !hostdata->issue_queue && !hostdata->connected) { - - if (falcon_dont_release) { -#if 0 - printk("WARNING: Lock release not allowed. Ignored\n"); -#endif - local_irq_restore(flags); - return; - } - falcon_got_lock = 0; + if (stdma_is_locked_by(scsi_falcon_intr)) stdma_release(); - wake_up(&falcon_fairness_wait); - } - - local_irq_restore(flags); } /* This function manages the locking of the ST-DMA. * If the DMA isn't locked already for SCSI, it tries to lock it by * calling stdma_lock(). But if the DMA is locked by the SCSI code and * there are other drivers waiting for the chip, we do not issue the - * command immediately but wait on 'falcon_fairness_queue'. We will be - * waked up when the DMA is unlocked by some SCSI interrupt. After that - * we try to get the lock again. - * But we must be prepared that more than one instance of - * falcon_get_lock() is waiting on the fairness queue. They should not - * try all at once to call stdma_lock(), one is enough! For that, the - * first one sets 'falcon_trying_lock', others that see that variable - * set wait on the queue 'falcon_try_wait'. - * Complicated, complicated.... Sigh... + * command immediately but tell the SCSI mid-layer to defer. */ -static void falcon_get_lock(void) +static int falcon_get_lock(struct Scsi_Host *instance) { - unsigned long flags; - if (IS_A_TT()) - return; - - local_irq_save(flags); - - wait_event_cmd(falcon_fairness_wait, - in_interrupt() || !falcon_got_lock || !stdma_others_waiting(), - local_irq_restore(flags), - local_irq_save(flags)); - - while (!falcon_got_lock) { - if (in_irq()) - panic("Falcon SCSI hasn't ST-DMA lock in interrupt"); - if (!falcon_trying_lock) { - falcon_trying_lock = 1; - stdma_lock(scsi_falcon_intr, NULL); - falcon_got_lock = 1; - falcon_trying_lock = 0; - wake_up(&falcon_try_wait); - } else { - wait_event_cmd(falcon_try_wait, - falcon_got_lock && !falcon_trying_lock, - local_irq_restore(flags), - local_irq_save(flags)); - } - } - - local_irq_restore(flags); - if (!falcon_got_lock) - panic("Falcon SCSI: someone stole the lock :-(\n"); -} - - -static int __init atari_scsi_detect(struct scsi_host_template *host) -{ - static int called = 0; - struct Scsi_Host *instance; - - if (!MACH_IS_ATARI || - (!ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(TT_SCSI)) || - called) - return 0; - - host->proc_name = "Atari"; - - atari_scsi_reg_read = IS_A_TT() ? atari_scsi_tt_reg_read : - atari_scsi_falcon_reg_read; - atari_scsi_reg_write = IS_A_TT() ? atari_scsi_tt_reg_write : - atari_scsi_falcon_reg_write; - - /* setup variables */ - host->can_queue = - (setup_can_queue > 0) ? setup_can_queue : - IS_A_TT() ? ATARI_TT_CAN_QUEUE : ATARI_FALCON_CAN_QUEUE; - host->cmd_per_lun = - (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : - IS_A_TT() ? ATARI_TT_CMD_PER_LUN : ATARI_FALCON_CMD_PER_LUN; - /* Force sg_tablesize to 0 on a Falcon! */ - host->sg_tablesize = - !IS_A_TT() ? ATARI_FALCON_SG_TABLESIZE : - (setup_sg_tablesize >= 0) ? setup_sg_tablesize : ATARI_TT_SG_TABLESIZE; - - if (setup_hostid >= 0) - host->this_id = setup_hostid; - else { - /* use 7 as default */ - host->this_id = 7; - /* Test if a host id is set in the NVRam */ - if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) { - unsigned char b = nvram_read_byte( 14 ); - /* Arbitration enabled? (for TOS) If yes, use configured host ID */ - if (b & 0x80) - host->this_id = b & 7; - } - } + return 1; -#ifdef SUPPORT_TAGS - if (setup_use_tagged_queuing < 0) - setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING; -#endif -#ifdef REAL_DMA - /* If running on a Falcon and if there's TT-Ram (i.e., more than one - * memory block, since there's always ST-Ram in a Falcon), then allocate a - * STRAM_BUFFER_SIZE byte dribble buffer for transfers from/to alternative - * Ram. - */ - if (MACH_IS_ATARI && ATARIHW_PRESENT(ST_SCSI) && - !ATARIHW_PRESENT(EXTD_DMA) && m68k_num_memory > 1) { - atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI"); - if (!atari_dma_buffer) { - printk(KERN_ERR "atari_scsi_detect: can't allocate ST-RAM " - "double buffer\n"); - return 0; - } - atari_dma_phys_buffer = atari_stram_to_phys(atari_dma_buffer); - atari_dma_orig_addr = 0; - } -#endif - instance = scsi_register(host, sizeof(struct NCR5380_hostdata)); - if (instance == NULL) { - atari_stram_free(atari_dma_buffer); - atari_dma_buffer = 0; - return 0; - } - atari_scsi_host = instance; - /* - * Set irq to 0, to avoid that the mid-level code disables our interrupt - * during queue_command calls. This is completely unnecessary, and even - * worse causes bad problems on the Falcon, where the int is shared with - * IDE and floppy! - */ - instance->irq = 0; - -#ifdef CONFIG_ATARI_SCSI_RESET_BOOT - atari_scsi_reset_boot(); -#endif - NCR5380_init(instance, 0); - - if (IS_A_TT()) { - - /* This int is actually "pseudo-slow", i.e. it acts like a slow - * interrupt after having cleared the pending flag for the DMA - * interrupt. */ - if (request_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr, IRQ_TYPE_SLOW, - "SCSI NCR5380", instance)) { - printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting",IRQ_TT_MFP_SCSI); - scsi_unregister(atari_scsi_host); - atari_stram_free(atari_dma_buffer); - atari_dma_buffer = 0; - return 0; - } - tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */ -#ifdef REAL_DMA - tt_scsi_dma.dma_ctrl = 0; - atari_dma_residual = 0; - - if (MACH_IS_MEDUSA) { - /* While the read overruns (described by Drew Eckhardt in - * NCR5380.c) never happened on TTs, they do in fact on the Medusa - * (This was the cause why SCSI didn't work right for so long - * there.) Since handling the overruns slows down a bit, I turned - * the #ifdef's into a runtime condition. - * - * In principle it should be sufficient to do max. 1 byte with - * PIO, but there is another problem on the Medusa with the DMA - * rest data register. So 'atari_read_overruns' is currently set - * to 4 to avoid having transfers that aren't a multiple of 4. If - * the rest data bug is fixed, this can be lowered to 1. - */ - atari_read_overruns = 4; - } -#endif /*REAL_DMA*/ - } else { /* ! IS_A_TT */ - - /* Nothing to do for the interrupt: the ST-DMA is initialized - * already by atari_init_INTS() - */ - -#ifdef REAL_DMA - atari_dma_residual = 0; - atari_dma_active = 0; - atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 - : 0xff000000); -#endif - } + if (in_interrupt()) + return stdma_try_lock(scsi_falcon_intr, instance); - printk(KERN_INFO "scsi%d: options CAN_QUEUE=%d CMD_PER_LUN=%d SCAT-GAT=%d " -#ifdef SUPPORT_TAGS - "TAGGED-QUEUING=%s " -#endif - "HOSTID=%d", - instance->host_no, instance->hostt->can_queue, - instance->hostt->cmd_per_lun, - instance->hostt->sg_tablesize, -#ifdef SUPPORT_TAGS - setup_use_tagged_queuing ? "yes" : "no", -#endif - instance->hostt->this_id ); - NCR5380_print_options(instance); - printk("\n"); - - called = 1; - return 1; -} - -static int atari_scsi_release(struct Scsi_Host *sh) -{ - if (IS_A_TT()) - free_irq(IRQ_TT_MFP_SCSI, sh); - if (atari_dma_buffer) - atari_stram_free(atari_dma_buffer); - NCR5380_exit(sh); + stdma_lock(scsi_falcon_intr, instance); return 1; } @@ -739,7 +485,7 @@ static int __init atari_scsi_setup(char *str) { /* Format of atascsi parameter is: * atascsi=<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags> - * Defaults depend on TT or Falcon, hostid determined at run time. + * Defaults depend on TT or Falcon, determined at run time. * Negative values mean don't change. */ int ints[6]; @@ -750,36 +496,17 @@ static int __init atari_scsi_setup(char *str) printk("atari_scsi_setup: no arguments!\n"); return 0; } - - if (ints[0] >= 1) { - if (ints[1] > 0) - /* no limits on this, just > 0 */ - setup_can_queue = ints[1]; - } - if (ints[0] >= 2) { - if (ints[2] > 0) - setup_cmd_per_lun = ints[2]; - } - if (ints[0] >= 3) { - if (ints[3] >= 0) { - setup_sg_tablesize = ints[3]; - /* Must be <= SG_ALL (255) */ - if (setup_sg_tablesize > SG_ALL) - setup_sg_tablesize = SG_ALL; - } - } - if (ints[0] >= 4) { - /* Must be between 0 and 7 */ - if (ints[4] >= 0 && ints[4] <= 7) - setup_hostid = ints[4]; - else if (ints[4] > 7) - printk("atari_scsi_setup: invalid host ID %d !\n", ints[4]); - } + if (ints[0] >= 1) + setup_can_queue = ints[1]; + if (ints[0] >= 2) + setup_cmd_per_lun = ints[2]; + if (ints[0] >= 3) + setup_sg_tablesize = ints[3]; + if (ints[0] >= 4) + setup_hostid = ints[4]; #ifdef SUPPORT_TAGS - if (ints[0] >= 5) { - if (ints[5] >= 0) - setup_use_tagged_queuing = !!ints[5]; - } + if (ints[0] >= 5) + setup_use_tagged_queuing = ints[5]; #endif return 1; @@ -788,45 +515,6 @@ static int __init atari_scsi_setup(char *str) __setup("atascsi=", atari_scsi_setup); #endif /* !MODULE */ -static int atari_scsi_bus_reset(Scsi_Cmnd *cmd) -{ - int rv; - struct NCR5380_hostdata *hostdata = - (struct NCR5380_hostdata *)cmd->device->host->hostdata; - - /* For doing the reset, SCSI interrupts must be disabled first, - * since the 5380 raises its IRQ line while _RST is active and we - * can't disable interrupts completely, since we need the timer. - */ - /* And abort a maybe active DMA transfer */ - if (IS_A_TT()) { - atari_turnoff_irq(IRQ_TT_MFP_SCSI); -#ifdef REAL_DMA - tt_scsi_dma.dma_ctrl = 0; -#endif /* REAL_DMA */ - } else { - atari_turnoff_irq(IRQ_MFP_FSCSI); -#ifdef REAL_DMA - st_dma.dma_mode_status = 0x90; - atari_dma_active = 0; - atari_dma_orig_addr = NULL; -#endif /* REAL_DMA */ - } - - rv = NCR5380_bus_reset(cmd); - - /* Re-enable ints */ - if (IS_A_TT()) { - atari_turnon_irq(IRQ_TT_MFP_SCSI); - } else { - atari_turnon_irq(IRQ_MFP_FSCSI); - } - if (rv == SUCCESS) - falcon_release_lock_if_possible(hostdata); - - return rv; -} - #ifdef CONFIG_ATARI_SCSI_RESET_BOOT static void __init atari_scsi_reset_boot(void) @@ -860,15 +548,6 @@ static void __init atari_scsi_reset_boot(void) } #endif - -static const char *atari_scsi_info(struct Scsi_Host *host) -{ - /* atari_scsi_detect() is verbose enough... */ - static const char string[] = "Atari native SCSI"; - return string; -} - - #if defined(REAL_DMA) static unsigned long atari_scsi_dma_setup(struct Scsi_Host *instance, @@ -949,7 +628,7 @@ static long atari_scsi_dma_residual(struct Scsi_Host *instance) #define CMD_SURELY_BYTE_MODE 1 #define CMD_MODE_UNKNOWN 2 -static int falcon_classify_cmd(Scsi_Cmnd *cmd) +static int falcon_classify_cmd(struct scsi_cmnd *cmd) { unsigned char opcode = cmd->cmnd[0]; @@ -981,7 +660,7 @@ static int falcon_classify_cmd(Scsi_Cmnd *cmd) */ static unsigned long atari_dma_xfer_len(unsigned long wanted_len, - Scsi_Cmnd *cmd, int write_flag) + struct scsi_cmnd *cmd, int write_flag) { unsigned long possible_len, limit; @@ -1099,23 +778,247 @@ static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value) #include "atari_NCR5380.c" -static struct scsi_host_template driver_template = { +static int atari_scsi_bus_reset(struct scsi_cmnd *cmd) +{ + int rv; + unsigned long flags; + + local_irq_save(flags); + +#ifdef REAL_DMA + /* Abort a maybe active DMA transfer */ + if (IS_A_TT()) { + tt_scsi_dma.dma_ctrl = 0; + } else { + st_dma.dma_mode_status = 0x90; + atari_dma_active = 0; + atari_dma_orig_addr = NULL; + } +#endif + + rv = NCR5380_bus_reset(cmd); + + /* The 5380 raises its IRQ line while _RST is active but the ST DMA + * "lock" has been released so this interrupt may end up handled by + * floppy or IDE driver (if one of them holds the lock). The NCR5380 + * interrupt flag has been cleared already. + */ + + local_irq_restore(flags); + + return rv; +} + +#define DRV_MODULE_NAME "atari_scsi" +#define PFX DRV_MODULE_NAME ": " + +static struct scsi_host_template atari_scsi_template = { + .module = THIS_MODULE, + .proc_name = DRV_MODULE_NAME, .show_info = atari_scsi_show_info, .name = "Atari native SCSI", - .detect = atari_scsi_detect, - .release = atari_scsi_release, .info = atari_scsi_info, .queuecommand = atari_scsi_queue_command, .eh_abort_handler = atari_scsi_abort, .eh_bus_reset_handler = atari_scsi_bus_reset, - .can_queue = 0, /* initialized at run-time */ - .this_id = 0, /* initialized at run-time */ - .sg_tablesize = 0, /* initialized at run-time */ - .cmd_per_lun = 0, /* initialized at run-time */ + .this_id = 7, .use_clustering = DISABLE_CLUSTERING }; +static int __init atari_scsi_probe(struct platform_device *pdev) +{ + struct Scsi_Host *instance; + int error; + struct resource *irq; + int host_flags = 0; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) + return -ENODEV; + + if (ATARIHW_PRESENT(TT_SCSI)) { + atari_scsi_reg_read = atari_scsi_tt_reg_read; + atari_scsi_reg_write = atari_scsi_tt_reg_write; + } else { + atari_scsi_reg_read = atari_scsi_falcon_reg_read; + atari_scsi_reg_write = atari_scsi_falcon_reg_write; + } + + /* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. + * Higher values should work, too; try it! + * (But cmd_per_lun costs memory!) + * + * But there seems to be a bug somewhere that requires CAN_QUEUE to be + * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since + * changed CMD_PER_LUN... + * + * Note: The Falcon currently uses 8/1 setting due to unsolved problems + * with cmd_per_lun != 1 + */ + if (ATARIHW_PRESENT(TT_SCSI)) { + atari_scsi_template.can_queue = 16; + atari_scsi_template.cmd_per_lun = 8; + atari_scsi_template.sg_tablesize = SG_ALL; + } else { + atari_scsi_template.can_queue = 8; + atari_scsi_template.cmd_per_lun = 1; + atari_scsi_template.sg_tablesize = SG_NONE; + } + + if (setup_can_queue > 0) + atari_scsi_template.can_queue = setup_can_queue; + + if (setup_cmd_per_lun > 0) + atari_scsi_template.cmd_per_lun = setup_cmd_per_lun; + + /* Leave sg_tablesize at 0 on a Falcon! */ + if (ATARIHW_PRESENT(TT_SCSI) && setup_sg_tablesize >= 0) + atari_scsi_template.sg_tablesize = setup_sg_tablesize; + + if (setup_hostid >= 0) { + atari_scsi_template.this_id = setup_hostid & 7; + } else { + /* Test if a host id is set in the NVRam */ + if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) { + unsigned char b = nvram_read_byte(14); + + /* Arbitration enabled? (for TOS) + * If yes, use configured host ID + */ + if (b & 0x80) + atari_scsi_template.this_id = b & 7; + } + } + + +#ifdef REAL_DMA + /* If running on a Falcon and if there's TT-Ram (i.e., more than one + * memory block, since there's always ST-Ram in a Falcon), then + * allocate a STRAM_BUFFER_SIZE byte dribble buffer for transfers + * from/to alternative Ram. + */ + if (ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(EXTD_DMA) && + m68k_num_memory > 1) { + atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI"); + if (!atari_dma_buffer) { + pr_err(PFX "can't allocate ST-RAM double buffer\n"); + return -ENOMEM; + } + atari_dma_phys_buffer = atari_stram_to_phys(atari_dma_buffer); + atari_dma_orig_addr = 0; + } +#endif + + instance = scsi_host_alloc(&atari_scsi_template, + sizeof(struct NCR5380_hostdata)); + if (!instance) { + error = -ENOMEM; + goto fail_alloc; + } + atari_scsi_host = instance; + +#ifdef CONFIG_ATARI_SCSI_RESET_BOOT + atari_scsi_reset_boot(); +#endif + + instance->irq = irq->start; + + host_flags |= IS_A_TT() ? 0 : FLAG_LATE_DMA_SETUP; + +#ifdef SUPPORT_TAGS + host_flags |= setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0; +#endif + + NCR5380_init(instance, host_flags); + + if (IS_A_TT()) { + error = request_irq(instance->irq, scsi_tt_intr, 0, + "NCR5380", instance); + if (error) { + pr_err(PFX "request irq %d failed, aborting\n", + instance->irq); + goto fail_irq; + } + tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */ +#ifdef REAL_DMA + tt_scsi_dma.dma_ctrl = 0; + atari_dma_residual = 0; + + /* While the read overruns (described by Drew Eckhardt in + * NCR5380.c) never happened on TTs, they do in fact on the + * Medusa (This was the cause why SCSI didn't work right for + * so long there.) Since handling the overruns slows down + * a bit, I turned the #ifdef's into a runtime condition. + * + * In principle it should be sufficient to do max. 1 byte with + * PIO, but there is another problem on the Medusa with the DMA + * rest data register. So read_overruns is currently set + * to 4 to avoid having transfers that aren't a multiple of 4. + * If the rest data bug is fixed, this can be lowered to 1. + */ + if (MACH_IS_MEDUSA) { + struct NCR5380_hostdata *hostdata = + shost_priv(instance); + + hostdata->read_overruns = 4; + } +#endif + } else { + /* Nothing to do for the interrupt: the ST-DMA is initialized + * already. + */ +#ifdef REAL_DMA + atari_dma_residual = 0; + atari_dma_active = 0; + atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 + : 0xff000000); +#endif + } + + error = scsi_add_host(instance, NULL); + if (error) + goto fail_host; + + platform_set_drvdata(pdev, instance); + + scsi_scan_host(instance); + return 0; + +fail_host: + if (IS_A_TT()) + free_irq(instance->irq, instance); +fail_irq: + NCR5380_exit(instance); + scsi_host_put(instance); +fail_alloc: + if (atari_dma_buffer) + atari_stram_free(atari_dma_buffer); + return error; +} + +static int __exit atari_scsi_remove(struct platform_device *pdev) +{ + struct Scsi_Host *instance = platform_get_drvdata(pdev); + + scsi_remove_host(instance); + if (IS_A_TT()) + free_irq(instance->irq, instance); + NCR5380_exit(instance); + scsi_host_put(instance); + if (atari_dma_buffer) + atari_stram_free(atari_dma_buffer); + return 0; +} + +static struct platform_driver atari_scsi_driver = { + .remove = __exit_p(atari_scsi_remove), + .driver = { + .name = DRV_MODULE_NAME, + .owner = THIS_MODULE, + }, +}; -#include "scsi_module.c" +module_platform_driver_probe(atari_scsi_driver, atari_scsi_probe); +MODULE_ALIAS("platform:" DRV_MODULE_NAME); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/atari_scsi.h b/drivers/scsi/atari_scsi.h deleted file mode 100644 index 3299d91d7336..000000000000 --- a/drivers/scsi/atari_scsi.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * atari_scsi.h -- Header file for the Atari native SCSI driver - * - * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> - * - * (Loosely based on the work of Robert De Vries' team) - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * - */ - - -#ifndef ATARI_SCSI_H -#define ATARI_SCSI_H - -/* (I_HAVE_OVERRUNS stuff removed) */ - -#ifndef ASM -/* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher - * values should work, too; try it! (but cmd_per_lun costs memory!) */ - -/* But there seems to be a bug somewhere that requires CAN_QUEUE to be - * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since - * changed CMD_PER_LUN... */ - -/* Note: The Falcon currently uses 8/1 setting due to unsolved problems with - * cmd_per_lun != 1 */ - -#define ATARI_TT_CAN_QUEUE 16 -#define ATARI_TT_CMD_PER_LUN 8 -#define ATARI_TT_SG_TABLESIZE SG_ALL - -#define ATARI_FALCON_CAN_QUEUE 8 -#define ATARI_FALCON_CMD_PER_LUN 1 -#define ATARI_FALCON_SG_TABLESIZE SG_NONE - -#define DEFAULT_USE_TAGGED_QUEUING 0 - - -#define NCR5380_implementation_fields /* none */ - -#define NCR5380_read(reg) atari_scsi_reg_read( reg ) -#define NCR5380_write(reg, value) atari_scsi_reg_write( reg, value ) - -#define NCR5380_intr atari_scsi_intr -#define NCR5380_queue_command atari_scsi_queue_command -#define NCR5380_abort atari_scsi_abort -#define NCR5380_show_info atari_scsi_show_info -#define NCR5380_dma_read_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 0) -#define NCR5380_dma_write_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 1) -#define NCR5380_dma_residual(inst) atari_scsi_dma_residual( inst ) -#define NCR5380_dma_xfer_len(i,cmd,phase) \ - atari_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1) - -#endif /* ndef ASM */ -#endif /* ATARI_SCSI_H */ - - diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 30d74a06b993..f3193406776c 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -556,7 +556,7 @@ static struct scsi_host_template beiscsi_sht = { .name = "Emulex 10Gbe open-iscsi Initiator Driver", .proc_name = DRV_NAME, .queuecommand = iscsi_queuecommand, - .change_queue_depth = iscsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .slave_configure = beiscsi_slave_configure, .target_alloc = iscsi_target_alloc, .eh_abort_handler = beiscsi_eh_abort, @@ -570,7 +570,7 @@ static struct scsi_host_template beiscsi_sht = { .cmd_per_lun = BEISCSI_CMD_PER_LUN, .use_clustering = ENABLE_CLUSTERING, .vendor_id = SCSI_NL_VID_TYPE_PCI | BE_VENDOR_ID, - + .track_queue_depth = 1, }; static struct scsi_transport_template *beiscsi_scsi_transport; diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c index 8e83d0474fe7..74a307c0a240 100644 --- a/drivers/scsi/bfa/bfad_debugfs.c +++ b/drivers/scsi/bfa/bfad_debugfs.c @@ -260,18 +260,9 @@ bfad_debugfs_write_regrd(struct file *file, const char __user *buf, unsigned long flags; void *kern_buf; - kern_buf = kzalloc(nbytes, GFP_KERNEL); - - if (!kern_buf) { - printk(KERN_INFO "bfad[%d]: Failed to allocate buffer\n", - bfad->inst_no); - return -ENOMEM; - } - - if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) { - kfree(kern_buf); - return -ENOMEM; - } + kern_buf = memdup_user(buf, nbytes); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); rc = sscanf(kern_buf, "%x:%x", &addr, &len); if (rc < 2) { @@ -336,18 +327,9 @@ bfad_debugfs_write_regwr(struct file *file, const char __user *buf, unsigned long flags; void *kern_buf; - kern_buf = kzalloc(nbytes, GFP_KERNEL); - - if (!kern_buf) { - printk(KERN_INFO "bfad[%d]: Failed to allocate buffer\n", - bfad->inst_no); - return -ENOMEM; - } - - if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) { - kfree(kern_buf); - return -ENOMEM; - } + kern_buf = memdup_user(buf, nbytes); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); rc = sscanf(kern_buf, "%x:%x", &addr, &val); if (rc < 2) { diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index f067332bf763..7223b0006740 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -776,11 +776,7 @@ bfad_thread_workq(struct bfad_s *bfad) static int bfad_im_slave_configure(struct scsi_device *sdev) { - if (sdev->tagged_supported) - scsi_activate_tcq(sdev, bfa_lun_queue_depth); - else - scsi_deactivate_tcq(sdev, bfa_lun_queue_depth); - + scsi_change_queue_depth(sdev, bfa_lun_queue_depth); return 0; } @@ -804,6 +800,7 @@ struct scsi_host_template bfad_im_scsi_host_template = { .shost_attrs = bfad_im_host_attrs, .max_sectors = BFAD_MAX_SECTORS, .vendor_id = BFA_PCI_VENDOR_ID_BROCADE, + .use_blk_tags = 1, }; struct scsi_host_template bfad_im_vport_template = { @@ -825,6 +822,7 @@ struct scsi_host_template bfad_im_vport_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = bfad_im_vport_attrs, .max_sectors = BFAD_MAX_SECTORS, + .use_blk_tags = 1, }; bfa_status_t @@ -868,14 +866,8 @@ bfad_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev) if (bfa_lun_queue_depth > tmp_sdev->queue_depth) { if (tmp_sdev->id != sdev->id) continue; - if (tmp_sdev->ordered_tags) - scsi_adjust_queue_depth(tmp_sdev, - MSG_ORDERED_TAG, - tmp_sdev->queue_depth + 1); - else - scsi_adjust_queue_depth(tmp_sdev, - MSG_SIMPLE_TAG, - tmp_sdev->queue_depth + 1); + scsi_change_queue_depth(tmp_sdev, + tmp_sdev->queue_depth + 1); itnim->last_ramp_up_time = jiffies; } diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 79e5c94107a9..e861f286b42e 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -412,6 +412,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, struct fc_frame_header *fh; struct fcoe_rcv_info *fr; struct fcoe_percpu_s *bg; + struct sk_buff *tmp_skb; unsigned short oxid; interface = container_of(ptype, struct bnx2fc_interface, @@ -424,6 +425,12 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, goto err; } + tmp_skb = skb_share_check(skb, GFP_ATOMIC); + if (!tmp_skb) + goto err; + + skb = tmp_skb; + if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n"); goto err; @@ -1081,7 +1088,7 @@ static int bnx2fc_vport_create(struct fc_vport *vport, bool disabled) mutex_unlock(&bnx2fc_dev_lock); rtnl_unlock(); - if (IS_ERR(vn_port)) { + if (!vn_port) { printk(KERN_ERR PFX "bnx2fc_vport_create (%s) failed\n", netdev->name); return -EIO; @@ -2195,6 +2202,7 @@ static int _bnx2fc_create(struct net_device *netdev, interface = bnx2fc_interface_create(hba, netdev, fip_mode); if (!interface) { printk(KERN_ERR PFX "bnx2fc_interface_create failed\n"); + rc = -ENOMEM; goto ifput_err; } @@ -2783,13 +2791,15 @@ static struct scsi_host_template bnx2fc_shost_template = { .eh_target_reset_handler = bnx2fc_eh_target_reset, /* tgt reset */ .eh_host_reset_handler = fc_eh_host_reset, .slave_alloc = fc_slave_alloc, - .change_queue_depth = fc_change_queue_depth, - .change_queue_type = fc_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = BNX2FC_MAX_BDS_PER_CMD, .max_sectors = 1024, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static struct libfc_function_template bnx2fc_libfc_fcn_templ = { diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 5b99844ef6bf..4b56858c1df2 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1725,7 +1725,6 @@ void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req, struct fcp_cmnd *fcp_cmnd) { struct scsi_cmnd *sc_cmd = io_req->sc_cmd; - char tag[2]; memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd)); @@ -1739,21 +1738,10 @@ void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req, fcp_cmnd->fc_tm_flags = io_req->mp_req.tm_flags; fcp_cmnd->fc_flags = io_req->io_req_flags; - if (scsi_populate_tag_msg(sc_cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - fcp_cmnd->fc_pri_ta = FCP_PTA_HEADQ; - break; - case ORDERED_QUEUE_TAG: - fcp_cmnd->fc_pri_ta = FCP_PTA_ORDERED; - break; - default: - fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE; - break; - } - } else { + if (sc_cmd->flags & SCMD_TAGGED) + fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE; + else fcp_cmnd->fc_pri_ta = 0; - } } static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req, diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 7a36388822aa..e53078d03309 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -2259,7 +2259,7 @@ static struct scsi_host_template bnx2i_host_template = { .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, - .change_queue_depth = iscsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .target_alloc = iscsi_target_alloc, .can_queue = 2048, .max_sectors = 127, @@ -2268,6 +2268,7 @@ static struct scsi_host_template bnx2i_host_template = { .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = ISCSI_MAX_BDS_PER_CMD, .shost_attrs = bnx2i_dev_attributes, + .track_queue_depth = 1, }; struct iscsi_transport bnx2i_iscsi_transport = { diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index ef5ae0d03616..6bac8a746ee2 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -85,8 +85,7 @@ static const char * vendor_labels[CH_TYPES-4] = { // module_param_string_array(vendor_labels, NULL, 0444); #define ch_printk(prefix, ch, fmt, a...) \ - sdev_printk(prefix, (ch)->device, "[%s] " fmt, \ - (ch)->name, ##a) + sdev_prefix_printk(prefix, (ch)->device, (ch)->name, fmt, ##a) #define DPRINTK(fmt, arg...) \ do { \ @@ -183,7 +182,7 @@ static int ch_find_errno(struct scsi_sense_hdr *sshdr) } static int -ch_do_scsi(scsi_changer *ch, unsigned char *cmd, +ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len, void *buffer, unsigned buflength, enum dma_data_direction direction) { @@ -197,7 +196,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, errno = 0; if (debug) { DPRINTK("command: "); - __scsi_print_command(cmd); + __scsi_print_command(cmd, cmd_len); } result = scsi_execute_req(ch->device, cmd, direction, buffer, @@ -207,7 +206,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, DPRINTK("result: 0x%x\n",result); if (driver_byte(result) & DRIVER_SENSE) { if (debug) - scsi_print_sense_hdr(ch->name, &sshdr); + scsi_print_sense_hdr(ch->device, ch->name, &sshdr); errno = ch_find_errno(&sshdr); switch(sshdr.sense_key) { @@ -258,7 +257,8 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data) cmd[3] = elem & 0xff; cmd[5] = 1; cmd[9] = 255; - if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) { + if (0 == (result = ch_do_scsi(ch, cmd, 12, + buffer, 256, DMA_FROM_DEVICE))) { if (((buffer[16] << 8) | buffer[17]) != elem) { DPRINTK("asked for element 0x%02x, got 0x%02x\n", elem,(buffer[16] << 8) | buffer[17]); @@ -288,7 +288,7 @@ ch_init_elem(scsi_changer *ch) memset(cmd,0,sizeof(cmd)); cmd[0] = INITIALIZE_ELEMENT_STATUS; cmd[1] = (ch->device->lun & 0x7) << 5; - err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE); + err = ch_do_scsi(ch, cmd, 6, NULL, 0, DMA_NONE); VPRINTK(KERN_INFO, "... finished\n"); return err; } @@ -310,10 +310,10 @@ ch_readconfig(scsi_changer *ch) cmd[1] = (ch->device->lun & 0x7) << 5; cmd[2] = 0x1d; cmd[4] = 255; - result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE); + result = ch_do_scsi(ch, cmd, 10, buffer, 255, DMA_FROM_DEVICE); if (0 != result) { cmd[1] |= (1<<3); - result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE); + result = ch_do_scsi(ch, cmd, 10, buffer, 255, DMA_FROM_DEVICE); } if (0 == result) { ch->firsts[CHET_MT] = @@ -438,7 +438,7 @@ ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate) cmd[4] = (elem >> 8) & 0xff; cmd[5] = elem & 0xff; cmd[8] = rotate ? 1 : 0; - return ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE); + return ch_do_scsi(ch, cmd, 10, NULL, 0, DMA_NONE); } static int @@ -459,7 +459,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate) cmd[6] = (dest >> 8) & 0xff; cmd[7] = dest & 0xff; cmd[10] = rotate ? 1 : 0; - return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE); + return ch_do_scsi(ch, cmd, 12, NULL,0, DMA_NONE); } static int @@ -485,7 +485,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src, cmd[9] = dest2 & 0xff; cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0); - return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE); + return ch_do_scsi(ch, cmd, 12, NULL, 0, DMA_NONE); } static void @@ -535,7 +535,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem, memcpy(buffer,tag,32); ch_check_voltag(buffer); - result = ch_do_scsi(ch, cmd, buffer, 256, DMA_TO_DEVICE); + result = ch_do_scsi(ch, cmd, 12, buffer, 256, DMA_TO_DEVICE); kfree(buffer); return result; } @@ -616,6 +616,11 @@ static long ch_ioctl(struct file *file, int retval; void __user *argp = (void __user *)arg; + retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd, + file->f_flags & O_NDELAY); + if (retval) + return retval; + switch (cmd) { case CHIOGPARAMS: { @@ -766,7 +771,8 @@ static long ch_ioctl(struct file *file, ch_cmd[5] = 1; ch_cmd[9] = 255; - result = ch_do_scsi(ch, ch_cmd, buffer, 256, DMA_FROM_DEVICE); + result = ch_do_scsi(ch, ch_cmd, 12, + buffer, 256, DMA_FROM_DEVICE); if (!result) { cge.cge_status = buffer[18]; cge.cge_flags = 0; @@ -966,9 +972,9 @@ static int ch_remove(struct device *dev) } static struct scsi_driver ch_template = { - .owner = THIS_MODULE, .gendrv = { .name = "ch", + .owner = THIS_MODULE, .probe = ch_probe, .remove = ch_remove, }, diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index d35a5d6c8d7c..e2068a2621c4 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -21,15 +21,21 @@ /* Commands with service actions that change the command name */ -#define SERVICE_ACTION_IN_12 0xab -#define SERVICE_ACTION_OUT_12 0xa9 -#define SERVICE_ACTION_BIDIRECTIONAL 0x9d -#define SERVICE_ACTION_IN_16 0x9e -#define SERVICE_ACTION_OUT_16 0x9f #define THIRD_PARTY_COPY_OUT 0x83 #define THIRD_PARTY_COPY_IN 0x84 +#define VENDOR_SPECIFIC_CDB 0xc0 +struct sa_name_list { + int opcode; + const struct value_name_pair *arr; + int arr_sz; +}; + +struct value_name_pair { + int value; + const char * name; +}; #ifdef CONFIG_SCSI_CONSTANTS static const char * cdb_byte0_names[] = { @@ -102,11 +108,6 @@ static const char * cdb_byte0_names[] = { "Volume set (out), Send DVD structure", }; -struct value_name_pair { - int value; - const char * name; -}; - static const struct value_name_pair maint_in_arr[] = { {0x5, "Report identifying information"}, {0xa, "Report target port groups"}, @@ -244,170 +245,119 @@ static const struct value_name_pair variable_length_arr[] = { }; #define VARIABLE_LENGTH_SZ ARRAY_SIZE(variable_length_arr) -static const char * get_sa_name(const struct value_name_pair * arr, - int arr_sz, int service_action) -{ - int k; +static struct sa_name_list sa_names_arr[] = { + {VARIABLE_LENGTH_CMD, variable_length_arr, VARIABLE_LENGTH_SZ}, + {MAINTENANCE_IN, maint_in_arr, MAINT_IN_SZ}, + {MAINTENANCE_OUT, maint_out_arr, MAINT_OUT_SZ}, + {PERSISTENT_RESERVE_IN, pr_in_arr, PR_IN_SZ}, + {PERSISTENT_RESERVE_OUT, pr_out_arr, PR_OUT_SZ}, + {SERVICE_ACTION_IN_12, serv_in12_arr, SERV_IN12_SZ}, + {SERVICE_ACTION_OUT_12, serv_out12_arr, SERV_OUT12_SZ}, + {SERVICE_ACTION_BIDIRECTIONAL, serv_bidi_arr, SERV_BIDI_SZ}, + {SERVICE_ACTION_IN_16, serv_in16_arr, SERV_IN16_SZ}, + {SERVICE_ACTION_OUT_16, serv_out16_arr, SERV_OUT16_SZ}, + {THIRD_PARTY_COPY_IN, tpc_in_arr, TPC_IN_SZ}, + {THIRD_PARTY_COPY_OUT, tpc_out_arr, TPC_OUT_SZ}, + {0, NULL, 0}, +}; - for (k = 0; k < arr_sz; ++k, ++arr) { - if (service_action == arr->value) - break; - } - return (k < arr_sz) ? arr->name : NULL; -} +#else /* ifndef CONFIG_SCSI_CONSTANTS */ +static const char *cdb_byte0_names[0]; + +static struct sa_name_list sa_names_arr[] = { + {VARIABLE_LENGTH_CMD, NULL, 0}, + {MAINTENANCE_IN, NULL, 0}, + {MAINTENANCE_OUT, NULL, 0}, + {PERSISTENT_RESERVE_IN, NULL, 0}, + {PERSISTENT_RESERVE_OUT, NULL, 0}, + {SERVICE_ACTION_IN_12, NULL, 0}, + {SERVICE_ACTION_OUT_12, NULL, 0}, + {SERVICE_ACTION_BIDIRECTIONAL, NULL, 0}, + {SERVICE_ACTION_IN_16, NULL, 0}, + {SERVICE_ACTION_OUT_16, NULL, 0}, + {THIRD_PARTY_COPY_IN, NULL, 0}, + {THIRD_PARTY_COPY_OUT, NULL, 0}, + {0, NULL, 0}, +}; +#endif /* CONFIG_SCSI_CONSTANTS */ -/* attempt to guess cdb length if cdb_len==0 . No trailing linefeed. */ -static void print_opcode_name(unsigned char * cdbp, int cdb_len) +static bool scsi_opcode_sa_name(int opcode, int service_action, + const char **cdb_name, const char **sa_name) { - int sa, len, cdb0; - int fin_name = 0; - const char * name; + struct sa_name_list *sa_name_ptr; + const struct value_name_pair *arr = NULL; + int arr_sz, k; - cdb0 = cdbp[0]; - switch(cdb0) { - case VARIABLE_LENGTH_CMD: - len = scsi_varlen_cdb_length(cdbp); - if (len < 10) { - printk("short variable length command, " - "len=%d ext_len=%d", len, cdb_len); - break; - } - sa = (cdbp[8] << 8) + cdbp[9]; - name = get_sa_name(variable_length_arr, VARIABLE_LENGTH_SZ, - sa); - if (name) - printk("%s", name); - else - printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa); + *cdb_name = NULL; + if (opcode >= VENDOR_SPECIFIC_CDB) + return false; - if ((cdb_len > 0) && (len != cdb_len)) - printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len); + if (opcode < ARRAY_SIZE(cdb_byte0_names)) + *cdb_name = cdb_byte0_names[opcode]; - break; - case MAINTENANCE_IN: - sa = cdbp[1] & 0x1f; - name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa); - fin_name = 1; - break; - case MAINTENANCE_OUT: - sa = cdbp[1] & 0x1f; - name = get_sa_name(maint_out_arr, MAINT_OUT_SZ, sa); - fin_name = 1; - break; - case PERSISTENT_RESERVE_IN: - sa = cdbp[1] & 0x1f; - name = get_sa_name(pr_in_arr, PR_IN_SZ, sa); - fin_name = 1; - break; - case PERSISTENT_RESERVE_OUT: - sa = cdbp[1] & 0x1f; - name = get_sa_name(pr_out_arr, PR_OUT_SZ, sa); - fin_name = 1; - break; - case SERVICE_ACTION_IN_12: - sa = cdbp[1] & 0x1f; - name = get_sa_name(serv_in12_arr, SERV_IN12_SZ, sa); - fin_name = 1; - break; - case SERVICE_ACTION_OUT_12: - sa = cdbp[1] & 0x1f; - name = get_sa_name(serv_out12_arr, SERV_OUT12_SZ, sa); - fin_name = 1; - break; - case SERVICE_ACTION_BIDIRECTIONAL: - sa = cdbp[1] & 0x1f; - name = get_sa_name(serv_bidi_arr, SERV_BIDI_SZ, sa); - fin_name = 1; - break; - case SERVICE_ACTION_IN_16: - sa = cdbp[1] & 0x1f; - name = get_sa_name(serv_in16_arr, SERV_IN16_SZ, sa); - fin_name = 1; - break; - case SERVICE_ACTION_OUT_16: - sa = cdbp[1] & 0x1f; - name = get_sa_name(serv_out16_arr, SERV_OUT16_SZ, sa); - fin_name = 1; - break; - case THIRD_PARTY_COPY_IN: - sa = cdbp[1] & 0x1f; - name = get_sa_name(tpc_in_arr, TPC_IN_SZ, sa); - fin_name = 1; - break; - case THIRD_PARTY_COPY_OUT: - sa = cdbp[1] & 0x1f; - name = get_sa_name(tpc_out_arr, TPC_OUT_SZ, sa); - fin_name = 1; - break; - default: - if (cdb0 < 0xc0) { - name = cdb_byte0_names[cdb0]; - if (name) - printk("%s", name); - else - printk("cdb[0]=0x%x (reserved)", cdb0); - } else - printk("cdb[0]=0x%x (vendor)", cdb0); - break; + for (sa_name_ptr = sa_names_arr; sa_name_ptr->arr; ++sa_name_ptr) { + if (sa_name_ptr->opcode == opcode) { + arr = sa_name_ptr->arr; + arr_sz = sa_name_ptr->arr_sz; + break; + } } - if (fin_name) { - if (name) - printk("%s", name); - else - printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa); + if (!arr) + return false; + + for (k = 0; k < arr_sz; ++k, ++arr) { + if (service_action == arr->value) + break; } -} + if (k < arr_sz) + *sa_name = arr->name; -#else /* ifndef CONFIG_SCSI_CONSTANTS */ + return true; +} -static void print_opcode_name(unsigned char * cdbp, int cdb_len) +static void print_opcode_name(const unsigned char *cdbp, size_t cdb_len) { - int sa, len, cdb0; + int sa, cdb0; + const char *cdb_name = NULL, *sa_name = NULL; cdb0 = cdbp[0]; - switch(cdb0) { - case VARIABLE_LENGTH_CMD: - len = scsi_varlen_cdb_length(cdbp); - if (len < 10) { - printk("short opcode=0x%x command, len=%d " - "ext_len=%d", cdb0, len, cdb_len); - break; + if (cdb0 == VARIABLE_LENGTH_CMD) { + if (cdb_len < 10) { + printk("short variable length command, len=%zu", + cdb_len); + return; } sa = (cdbp[8] << 8) + cdbp[9]; - printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa); - if (len != cdb_len) - printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len); - break; - case MAINTENANCE_IN: - case MAINTENANCE_OUT: - case PERSISTENT_RESERVE_IN: - case PERSISTENT_RESERVE_OUT: - case SERVICE_ACTION_IN_12: - case SERVICE_ACTION_OUT_12: - case SERVICE_ACTION_BIDIRECTIONAL: - case SERVICE_ACTION_IN_16: - case SERVICE_ACTION_OUT_16: - case THIRD_PARTY_COPY_IN: - case THIRD_PARTY_COPY_OUT: + } else sa = cdbp[1] & 0x1f; - printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa); - break; - default: - if (cdb0 < 0xc0) + + if (!scsi_opcode_sa_name(cdb0, sa, &cdb_name, &sa_name)) { + if (cdb_name) + printk("%s", cdb_name); + else if (cdb0 >= VENDOR_SPECIFIC_CDB) + printk("cdb[0]=0x%x (vendor)", cdb0); + else if (cdb0 >= 0x60 && cdb0 < 0x7e) + printk("cdb[0]=0x%x (reserved)", cdb0); + else printk("cdb[0]=0x%x", cdb0); + } else { + if (sa_name) + printk("%s", sa_name); + else if (cdb_name) + printk("%s, sa=0x%x", cdb_name, sa); else - printk("cdb[0]=0x%x (vendor)", cdb0); - break; + printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa); } } -#endif -void __scsi_print_command(unsigned char *cdb) +void __scsi_print_command(const unsigned char *cdb, size_t cdb_len) { int k, len; - print_opcode_name(cdb, 0); + print_opcode_name(cdb, cdb_len); len = scsi_command_size(cdb); + if (cdb_len < len) + len = cdb_len; /* print out all bytes in cdb */ for (k = 0; k < len; ++k) printk(" %02x", cdb[k]); @@ -433,41 +383,6 @@ void scsi_print_command(struct scsi_cmnd *cmd) } EXPORT_SYMBOL(scsi_print_command); -/** - * scsi_print_status - print scsi status description - * @scsi_status: scsi status value - * - * If the status is recognized, the description is printed. - * Otherwise "Unknown status" is output. No trailing space. - * If CONFIG_SCSI_CONSTANTS is not set, then print status in hex - * (e.g. "0x2" for Check Condition). - **/ -void -scsi_print_status(unsigned char scsi_status) { -#ifdef CONFIG_SCSI_CONSTANTS - const char * ccp; - - switch (scsi_status) { - case 0: ccp = "Good"; break; - case 0x2: ccp = "Check Condition"; break; - case 0x4: ccp = "Condition Met"; break; - case 0x8: ccp = "Busy"; break; - case 0x10: ccp = "Intermediate"; break; - case 0x14: ccp = "Intermediate-Condition Met"; break; - case 0x18: ccp = "Reservation Conflict"; break; - case 0x22: ccp = "Command Terminated"; break; /* obsolete */ - case 0x28: ccp = "Task set Full"; break; /* was: Queue Full */ - case 0x30: ccp = "ACA Active"; break; - case 0x40: ccp = "Task Aborted"; break; - default: ccp = "Unknown status"; - } - printk(KERN_INFO "%s", ccp); -#else - printk(KERN_INFO "0x%0x", scsi_status); -#endif -} -EXPORT_SYMBOL(scsi_print_status); - #ifdef CONFIG_SCSI_CONSTANTS struct error_info { @@ -1292,18 +1207,19 @@ static const struct error_info additional[] = struct error_info2 { unsigned char code1, code2_min, code2_max; + const char * str; const char * fmt; }; static const struct error_info2 additional2[] = { - {0x40, 0x00, 0x7f, "Ram failure (%x)"}, - {0x40, 0x80, 0xff, "Diagnostic failure on component (%x)"}, - {0x41, 0x00, 0xff, "Data path failure (%x)"}, - {0x42, 0x00, 0xff, "Power-on or self-test failure (%x)"}, - {0x4D, 0x00, 0xff, "Tagged overlapped commands (task tag %x)"}, - {0x70, 0x00, 0xff, "Decompression exception short algorithm id of %x"}, - {0, 0, 0, NULL} + {0x40, 0x00, 0x7f, "Ram failure", ""}, + {0x40, 0x80, 0xff, "Diagnostic failure on component", ""}, + {0x41, 0x00, 0xff, "Data path failure", ""}, + {0x42, 0x00, 0xff, "Power-on or self-test failure", ""}, + {0x4D, 0x00, 0xff, "Tagged overlapped commands", "task tag "}, + {0x70, 0x00, 0xff, "Decompression exception", "short algorithm id of "}, + {0, 0, 0, NULL, NULL} }; /* description of the sense key values */ @@ -1349,69 +1265,79 @@ EXPORT_SYMBOL(scsi_sense_key_string); * This string may contain a "%x" and should be printed with ascq as arg. */ const char * -scsi_extd_sense_format(unsigned char asc, unsigned char ascq) { +scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt) +{ #ifdef CONFIG_SCSI_CONSTANTS int i; unsigned short code = ((asc << 8) | ascq); + *fmt = NULL; for (i = 0; additional[i].text; i++) if (additional[i].code12 == code) return additional[i].text; for (i = 0; additional2[i].fmt; i++) { if (additional2[i].code1 == asc && ascq >= additional2[i].code2_min && - ascq <= additional2[i].code2_max) - return additional2[i].fmt; + ascq <= additional2[i].code2_max) { + *fmt = additional2[i].fmt; + return additional2[i].str; + } } +#else + *fmt = NULL; #endif return NULL; } EXPORT_SYMBOL(scsi_extd_sense_format); void -scsi_show_extd_sense(unsigned char asc, unsigned char ascq) +scsi_show_extd_sense(const struct scsi_device *sdev, const char *name, + unsigned char asc, unsigned char ascq) { - const char *extd_sense_fmt = scsi_extd_sense_format(asc, ascq); - - if (extd_sense_fmt) { - if (strstr(extd_sense_fmt, "%x")) { - printk("Add. Sense: "); - printk(extd_sense_fmt, ascq); - } else - printk("Add. Sense: %s", extd_sense_fmt); - } else { - if (asc >= 0x80) - printk("<<vendor>> ASC=0x%x ASCQ=0x%x", asc, - ascq); - if (ascq >= 0x80) - printk("ASC=0x%x <<vendor>> ASCQ=0x%x", asc, - ascq); + const char *extd_sense_fmt = NULL; + const char *extd_sense_str = scsi_extd_sense_format(asc, ascq, + &extd_sense_fmt); + + if (extd_sense_str) { + if (extd_sense_fmt) + sdev_prefix_printk(KERN_INFO, sdev, name, + "Add. Sense: %s (%s%x)", + extd_sense_str, extd_sense_fmt, + ascq); else - printk("ASC=0x%x ASCQ=0x%x", asc, ascq); - } + sdev_prefix_printk(KERN_INFO, sdev, name, + "Add. Sense: %s", extd_sense_str); - printk("\n"); + } else { + sdev_prefix_printk(KERN_INFO, sdev, name, + "%sASC=0x%x %sASCQ=0x%x\n", + asc >= 0x80 ? "<<vendor>> " : "", asc, + ascq >= 0x80 ? "<<vendor>> " : "", ascq); + } } EXPORT_SYMBOL(scsi_show_extd_sense); void -scsi_show_sense_hdr(struct scsi_sense_hdr *sshdr) +scsi_show_sense_hdr(const struct scsi_device *sdev, const char *name, + const struct scsi_sense_hdr *sshdr) { const char *sense_txt; sense_txt = scsi_sense_key_string(sshdr->sense_key); if (sense_txt) - printk("Sense Key : %s ", sense_txt); + sdev_prefix_printk(KERN_INFO, sdev, name, + "Sense Key : %s [%s]%s\n", sense_txt, + scsi_sense_is_deferred(sshdr) ? + "deferred" : "current", + sshdr->response_code >= 0x72 ? + " [descriptor]" : ""); else - printk("Sense Key : 0x%x ", sshdr->sense_key); - - printk("%s", scsi_sense_is_deferred(sshdr) ? "[deferred] " : - "[current] "); - - if (sshdr->response_code >= 0x72) - printk("[descriptor]"); - - printk("\n"); + sdev_prefix_printk(KERN_INFO, sdev, name, + "Sense Key : 0x%x [%s]%s", sshdr->sense_key, + scsi_sense_is_deferred(sshdr) ? + "deferred" : "current", + sshdr->response_code >= 0x72 ? + " [descriptor]" : ""); } EXPORT_SYMBOL(scsi_show_sense_hdr); @@ -1419,141 +1345,55 @@ EXPORT_SYMBOL(scsi_show_sense_hdr); * Print normalized SCSI sense header with a prefix. */ void -scsi_print_sense_hdr(const char *name, struct scsi_sense_hdr *sshdr) +scsi_print_sense_hdr(const struct scsi_device *sdev, const char *name, + const struct scsi_sense_hdr *sshdr) { - printk(KERN_INFO "%s: ", name); - scsi_show_sense_hdr(sshdr); - printk(KERN_INFO "%s: ", name); - scsi_show_extd_sense(sshdr->asc, sshdr->ascq); + scsi_show_sense_hdr(sdev, name, sshdr); + scsi_show_extd_sense(sdev, name, sshdr->asc, sshdr->ascq); } EXPORT_SYMBOL(scsi_print_sense_hdr); -/* - * Print normalized SCSI sense header with device information and a prefix. - */ -void -scsi_cmd_print_sense_hdr(struct scsi_cmnd *scmd, const char *desc, - struct scsi_sense_hdr *sshdr) -{ - scmd_printk(KERN_INFO, scmd, "%s: ", desc); - scsi_show_sense_hdr(sshdr); - scmd_printk(KERN_INFO, scmd, "%s: ", desc); - scsi_show_extd_sense(sshdr->asc, sshdr->ascq); -} -EXPORT_SYMBOL(scsi_cmd_print_sense_hdr); - static void -scsi_decode_sense_buffer(const unsigned char *sense_buffer, int sense_len, - struct scsi_sense_hdr *sshdr) +scsi_dump_sense_buffer(const unsigned char *sense_buffer, int sense_len) { - int k, num, res; - - res = scsi_normalize_sense(sense_buffer, sense_len, sshdr); - if (0 == res) { - /* this may be SCSI-1 sense data */ - num = (sense_len < 32) ? sense_len : 32; - printk("Unrecognized sense data (in hex):"); - for (k = 0; k < num; ++k) { - if (0 == (k % 16)) { - printk("\n"); - printk(KERN_INFO " "); - } - printk("%02x ", sense_buffer[k]); + int k, num; + + num = (sense_len < 32) ? sense_len : 32; + printk("Unrecognized sense data (in hex):"); + for (k = 0; k < num; ++k) { + if (0 == (k % 16)) { + printk("\n"); + printk(KERN_INFO " "); } - printk("\n"); - return; + printk("%02x ", sense_buffer[k]); } -} - -static void -scsi_decode_sense_extras(const unsigned char *sense_buffer, int sense_len, - struct scsi_sense_hdr *sshdr) -{ - int k, num, res; - - if (sshdr->response_code < 0x72) - { - /* only decode extras for "fixed" format now */ - char buff[80]; - int blen, fixed_valid; - unsigned int info; - - fixed_valid = sense_buffer[0] & 0x80; - info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) | - (sense_buffer[5] << 8) | sense_buffer[6]); - res = 0; - memset(buff, 0, sizeof(buff)); - blen = sizeof(buff) - 1; - if (fixed_valid) - res += snprintf(buff + res, blen - res, - "Info fld=0x%x", info); - if (sense_buffer[2] & 0x80) { - /* current command has read a filemark */ - if (res > 0) - res += snprintf(buff + res, blen - res, ", "); - res += snprintf(buff + res, blen - res, "FMK"); - } - if (sense_buffer[2] & 0x40) { - /* end-of-medium condition exists */ - if (res > 0) - res += snprintf(buff + res, blen - res, ", "); - res += snprintf(buff + res, blen - res, "EOM"); - } - if (sense_buffer[2] & 0x20) { - /* incorrect block length requested */ - if (res > 0) - res += snprintf(buff + res, blen - res, ", "); - res += snprintf(buff + res, blen - res, "ILI"); - } - if (res > 0) - printk("%s\n", buff); - } else if (sshdr->additional_length > 0) { - /* descriptor format with sense descriptors */ - num = 8 + sshdr->additional_length; - num = (sense_len < num) ? sense_len : num; - printk("Descriptor sense data with sense descriptors " - "(in hex):"); - for (k = 0; k < num; ++k) { - if (0 == (k % 16)) { - printk("\n"); - printk(KERN_INFO " "); - } - printk("%02x ", sense_buffer[k]); - } - - printk("\n"); - } - + printk("\n"); + return; } /* Normalize and print sense buffer with name prefix */ -void __scsi_print_sense(const char *name, const unsigned char *sense_buffer, - int sense_len) +void __scsi_print_sense(const struct scsi_device *sdev, const char *name, + const unsigned char *sense_buffer, int sense_len) { struct scsi_sense_hdr sshdr; - printk(KERN_INFO "%s: ", name); - scsi_decode_sense_buffer(sense_buffer, sense_len, &sshdr); - scsi_show_sense_hdr(&sshdr); - scsi_decode_sense_extras(sense_buffer, sense_len, &sshdr); - printk(KERN_INFO "%s: ", name); - scsi_show_extd_sense(sshdr.asc, sshdr.ascq); + if (!scsi_normalize_sense(sense_buffer, sense_len, &sshdr)) { + scsi_dump_sense_buffer(sense_buffer, sense_len); + return; + } + scsi_show_sense_hdr(sdev, name, &sshdr); + scsi_show_extd_sense(sdev, name, sshdr.asc, sshdr.ascq); } EXPORT_SYMBOL(__scsi_print_sense); /* Normalize and print sense buffer in SCSI command */ -void scsi_print_sense(char *name, struct scsi_cmnd *cmd) +void scsi_print_sense(const struct scsi_cmnd *cmd) { - struct scsi_sense_hdr sshdr; + struct gendisk *disk = cmd->request->rq_disk; + const char *disk_name = disk ? disk->disk_name : NULL; - scmd_printk(KERN_INFO, cmd, " "); - scsi_decode_sense_buffer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE, - &sshdr); - scsi_show_sense_hdr(&sshdr); - scsi_decode_sense_extras(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE, - &sshdr); - scmd_printk(KERN_INFO, cmd, " "); - scsi_show_extd_sense(sshdr.asc, sshdr.ascq); + __scsi_print_sense(cmd->device, disk_name, cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE); } EXPORT_SYMBOL(scsi_print_sense); @@ -1565,38 +1405,87 @@ static const char * const hostbyte_table[]={ "DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY", "DID_REQUEUE", "DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE", "DID_NEXUS_FAILURE" }; -#define NUM_HOSTBYTE_STRS ARRAY_SIZE(hostbyte_table) static const char * const driverbyte_table[]={ "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"}; -#define NUM_DRIVERBYTE_STRS ARRAY_SIZE(driverbyte_table) -void scsi_show_result(int result) +#endif + +const char *scsi_hostbyte_string(int result) { + const char *hb_string = NULL; +#ifdef CONFIG_SCSI_CONSTANTS int hb = host_byte(result); - int db = driver_byte(result); - printk("Result: hostbyte=%s driverbyte=%s\n", - (hb < NUM_HOSTBYTE_STRS ? hostbyte_table[hb] : "invalid"), - (db < NUM_DRIVERBYTE_STRS ? driverbyte_table[db] : "invalid")); + if (hb < ARRAY_SIZE(hostbyte_table)) + hb_string = hostbyte_table[hb]; +#endif + return hb_string; } +EXPORT_SYMBOL(scsi_hostbyte_string); -#else - -void scsi_show_result(int result) +const char *scsi_driverbyte_string(int result) { - printk("Result: hostbyte=0x%02x driverbyte=0x%02x\n", - host_byte(result), driver_byte(result)); + const char *db_string = NULL; +#ifdef CONFIG_SCSI_CONSTANTS + int db = driver_byte(result); + + if (db < ARRAY_SIZE(driverbyte_table)) + db_string = driverbyte_table[db]; +#endif + return db_string; } +EXPORT_SYMBOL(scsi_driverbyte_string); +#ifdef CONFIG_SCSI_CONSTANTS +#define scsi_mlreturn_name(result) { result, #result } +static const struct value_name_pair scsi_mlreturn_arr[] = { + scsi_mlreturn_name(NEEDS_RETRY), + scsi_mlreturn_name(SUCCESS), + scsi_mlreturn_name(FAILED), + scsi_mlreturn_name(QUEUED), + scsi_mlreturn_name(SOFT_ERROR), + scsi_mlreturn_name(ADD_TO_MLQUEUE), + scsi_mlreturn_name(TIMEOUT_ERROR), + scsi_mlreturn_name(SCSI_RETURN_NOT_HANDLED), + scsi_mlreturn_name(FAST_IO_FAIL) +}; #endif -EXPORT_SYMBOL(scsi_show_result); +const char *scsi_mlreturn_string(int result) +{ +#ifdef CONFIG_SCSI_CONSTANTS + const struct value_name_pair *arr = scsi_mlreturn_arr; + int k; -void scsi_print_result(struct scsi_cmnd *cmd) + for (k = 0; k < ARRAY_SIZE(scsi_mlreturn_arr); ++k, ++arr) { + if (result == arr->value) + return arr->name; + } +#endif + return NULL; +} +EXPORT_SYMBOL(scsi_mlreturn_string); + +void scsi_print_result(struct scsi_cmnd *cmd, const char *msg, int disposition) { - scmd_printk(KERN_INFO, cmd, " "); - scsi_show_result(cmd->result); + const char *mlret_string = scsi_mlreturn_string(disposition); + const char *hb_string = scsi_hostbyte_string(cmd->result); + const char *db_string = scsi_driverbyte_string(cmd->result); + + if (hb_string || db_string) + scmd_printk(KERN_INFO, cmd, + "%s%s Result: hostbyte=%s driverbyte=%s", + msg ? msg : "", + mlret_string ? mlret_string : "UNKNOWN", + hb_string ? hb_string : "invalid", + db_string ? db_string : "invalid"); + else + scmd_printk(KERN_INFO, cmd, + "%s%s Result: hostbyte=0x%02x driverbyte=0x%02x", + msg ? msg : "", + mlret_string ? mlret_string : "UNKNOWN", + host_byte(cmd->result), driver_byte(cmd->result)); } EXPORT_SYMBOL(scsi_print_result); diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index 86103c8475d8..4d0b6ce55f20 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -152,28 +152,6 @@ csio_scsi_itnexus_loss_error(uint16_t error) return 0; } -static inline void -csio_scsi_tag(struct scsi_cmnd *scmnd, uint8_t *tag, uint8_t hq, - uint8_t oq, uint8_t sq) -{ - char stag[2]; - - if (scsi_populate_tag_msg(scmnd, stag)) { - switch (stag[0]) { - case HEAD_OF_QUEUE_TAG: - *tag = hq; - break; - case ORDERED_QUEUE_TAG: - *tag = oq; - break; - default: - *tag = sq; - break; - } - } else - *tag = 0; -} - /* * csio_scsi_fcp_cmnd - Frame the SCSI FCP command paylod. * @req: IO req structure. @@ -192,11 +170,12 @@ csio_scsi_fcp_cmnd(struct csio_ioreq *req, void *addr) int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun); fcp_cmnd->fc_tm_flags = 0; fcp_cmnd->fc_cmdref = 0; - fcp_cmnd->fc_pri_ta = 0; memcpy(fcp_cmnd->fc_cdb, scmnd->cmnd, 16); - csio_scsi_tag(scmnd, &fcp_cmnd->fc_pri_ta, - FCP_PTA_HEADQ, FCP_PTA_ORDERED, FCP_PTA_SIMPLE); + if (scmnd->flags & SCMD_TAGGED) + fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE; + else + fcp_cmnd->fc_pri_ta = 0; fcp_cmnd->fc_dl = cpu_to_be32(scsi_bufflen(scmnd)); if (req->nsge) @@ -2262,11 +2241,7 @@ csio_slave_alloc(struct scsi_device *sdev) static int csio_slave_configure(struct scsi_device *sdev) { - if (sdev->tagged_supported) - scsi_activate_tcq(sdev, csio_lun_qdepth); - else - scsi_deactivate_tcq(sdev, csio_lun_qdepth); - + scsi_change_queue_depth(sdev, csio_lun_qdepth); return 0; } @@ -2311,6 +2286,7 @@ struct scsi_host_template csio_fcoe_shost_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = csio_fcoe_lport_attrs, .max_sectors = CSIO_MAX_SECTOR_SIZE, + .use_blk_tags = 1, }; struct scsi_host_template csio_fcoe_shost_vport_template = { @@ -2330,6 +2306,7 @@ struct scsi_host_template csio_fcoe_shost_vport_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = csio_fcoe_vport_attrs, .max_sectors = CSIO_MAX_SECTOR_SIZE, + .use_blk_tags = 1, }; /* diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index 49692a1ac44a..3db4c63978c5 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -86,7 +86,7 @@ static struct scsi_host_template cxgb3i_host_template = { .proc_name = DRV_MODULE_NAME, .can_queue = CXGB3I_SCSI_HOST_QDEPTH, .queuecommand = iscsi_queuecommand, - .change_queue_depth = iscsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .sg_tablesize = SG_ALL, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, @@ -96,6 +96,7 @@ static struct scsi_host_template cxgb3i_host_template = { .target_alloc = iscsi_target_alloc, .use_clustering = DISABLE_CLUSTERING, .this_id = -1, + .track_queue_depth = 1, }; static struct iscsi_transport cxgb3i_iscsi_transport = { diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 15081257cfc8..e6c3f55d9d36 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -89,7 +89,7 @@ static struct scsi_host_template cxgb4i_host_template = { .proc_name = DRV_MODULE_NAME, .can_queue = CXGB4I_SCSI_HOST_QDEPTH, .queuecommand = iscsi_queuecommand, - .change_queue_depth = iscsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .sg_tablesize = SG_ALL, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, @@ -99,6 +99,7 @@ static struct scsi_host_template cxgb4i_host_template = { .target_alloc = iscsi_target_alloc, .use_clustering = DISABLE_CLUSTERING, .this_id = -1, + .track_queue_depth = 1, }; static struct iscsi_transport cxgb4i_iscsi_transport = { diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 33e422e75835..1dba62c5cf6a 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -98,27 +98,51 @@ device_handler_match(struct scsi_device_handler *scsi_dh, static int scsi_dh_handler_attach(struct scsi_device *sdev, struct scsi_device_handler *scsi_dh) { - int err = 0; + struct scsi_dh_data *d; if (sdev->scsi_dh_data) { if (sdev->scsi_dh_data->scsi_dh != scsi_dh) - err = -EBUSY; - else - kref_get(&sdev->scsi_dh_data->kref); - } else if (scsi_dh->attach) { - err = scsi_dh->attach(sdev); - if (!err) { - kref_init(&sdev->scsi_dh_data->kref); - sdev->scsi_dh_data->sdev = sdev; - } + return -EBUSY; + + kref_get(&sdev->scsi_dh_data->kref); + return 0; } - return err; + + if (!try_module_get(scsi_dh->module)) + return -EINVAL; + + d = scsi_dh->attach(sdev); + if (IS_ERR(d)) { + sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%ld)\n", + scsi_dh->name, PTR_ERR(d)); + module_put(scsi_dh->module); + return PTR_ERR(d); + } + + d->scsi_dh = scsi_dh; + kref_init(&d->kref); + d->sdev = sdev; + + spin_lock_irq(sdev->request_queue->queue_lock); + sdev->scsi_dh_data = d; + spin_unlock_irq(sdev->request_queue->queue_lock); + return 0; } static void __detach_handler (struct kref *kref) { - struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref); - scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev); + struct scsi_dh_data *scsi_dh_data = + container_of(kref, struct scsi_dh_data, kref); + struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh; + struct scsi_device *sdev = scsi_dh_data->sdev; + + spin_lock_irq(sdev->request_queue->queue_lock); + sdev->scsi_dh_data = NULL; + spin_unlock_irq(sdev->request_queue->queue_lock); + + scsi_dh->detach(sdev); + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", scsi_dh->name); + module_put(scsi_dh->module); } /* @@ -141,7 +165,7 @@ static void scsi_dh_handler_detach(struct scsi_device *sdev, if (!scsi_dh) scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (scsi_dh && scsi_dh->detach) + if (scsi_dh) kref_put(&sdev->scsi_dh_data->kref, __detach_handler); } @@ -330,6 +354,9 @@ int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) if (get_device_handler(scsi_dh->name)) return -EBUSY; + if (!scsi_dh->attach || !scsi_dh->detach) + return -EINVAL; + spin_lock(&list_lock); list_add(&scsi_dh->list, &scsi_dh_list); spin_unlock(&list_lock); diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index fd78bdc53528..854b568b9931 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -62,6 +62,7 @@ #define ALUA_OPTIMIZE_STPG 1 struct alua_dh_data { + struct scsi_dh_data dh_data; int group_id; int rel_port; int tpgs; @@ -87,9 +88,7 @@ static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *); static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; - BUG_ON(scsi_dh_data == NULL); - return ((struct alua_dh_data *) scsi_dh_data->buf); + return container_of(sdev->scsi_dh_data, struct alua_dh_data, dh_data); } static int realloc_buffer(struct alua_dh_data *h, unsigned len) @@ -825,42 +824,18 @@ static bool alua_match(struct scsi_device *sdev) return (scsi_device_tpgs(sdev) != 0); } -static int alua_bus_attach(struct scsi_device *sdev); -static void alua_bus_detach(struct scsi_device *sdev); - -static struct scsi_device_handler alua_dh = { - .name = ALUA_DH_NAME, - .module = THIS_MODULE, - .attach = alua_bus_attach, - .detach = alua_bus_detach, - .prep_fn = alua_prep_fn, - .check_sense = alua_check_sense, - .activate = alua_activate, - .set_params = alua_set_params, - .match = alua_match, -}; - /* * alua_bus_attach - Attach device handler * @sdev: device to be attached to */ -static int alua_bus_attach(struct scsi_device *sdev) +static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data; struct alua_dh_data *h; - unsigned long flags; - int err = SCSI_DH_OK; - - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", - ALUA_DH_NAME); - return -ENOMEM; - } + int err; - scsi_dh_data->scsi_dh = &alua_dh; - h = (struct alua_dh_data *) scsi_dh_data->buf; + h = kzalloc(sizeof(*h) , GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); h->tpgs = TPGS_MODE_UNINITIALIZED; h->state = TPGS_STATE_OPTIMIZED; h->group_id = -1; @@ -870,23 +845,14 @@ static int alua_bus_attach(struct scsi_device *sdev) h->sdev = sdev; err = alua_initialize(sdev, h); - if ((err != SCSI_DH_OK) && (err != SCSI_DH_DEV_OFFLINED)) - goto failed; - - if (!try_module_get(THIS_MODULE)) + if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED) goto failed; - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME); - - return 0; - + return &h->dh_data; failed: - kfree(scsi_dh_data); - sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME); - return -EINVAL; + kfree(h); + return ERR_PTR(-EINVAL); } /* @@ -895,23 +861,25 @@ failed: */ static void alua_bus_detach(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data; - struct alua_dh_data *h; - unsigned long flags; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - scsi_dh_data = sdev->scsi_dh_data; - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + struct alua_dh_data *h = get_alua_data(sdev); - h = (struct alua_dh_data *) scsi_dh_data->buf; if (h->buff && h->inq != h->buff) kfree(h->buff); - kfree(scsi_dh_data); - module_put(THIS_MODULE); - sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME); + kfree(h); } +static struct scsi_device_handler alua_dh = { + .name = ALUA_DH_NAME, + .module = THIS_MODULE, + .attach = alua_bus_attach, + .detach = alua_bus_detach, + .prep_fn = alua_prep_fn, + .check_sense = alua_check_sense, + .activate = alua_activate, + .set_params = alua_set_params, + .match = alua_match, +}; + static int __init alua_init(void) { int r; diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 84765384c47c..6ed1caadbc6a 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -72,6 +72,7 @@ static const char * lun_state[] = }; struct clariion_dh_data { + struct scsi_dh_data dh_data; /* * Flags: * CLARIION_SHORT_TRESPASS @@ -116,9 +117,8 @@ struct clariion_dh_data { static inline struct clariion_dh_data *get_clariion_data(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; - BUG_ON(scsi_dh_data == NULL); - return ((struct clariion_dh_data *) scsi_dh_data->buf); + return container_of(sdev->scsi_dh_data, struct clariion_dh_data, + dh_data); } /* @@ -622,7 +622,10 @@ done: return result; } -static const struct scsi_dh_devlist clariion_dev_list[] = { +static const struct { + char *vendor; + char *model; +} clariion_dev_list[] = { {"DGC", "RAID"}, {"DGC", "DISK"}, {"DGC", "VRAID"}, @@ -647,39 +650,14 @@ static bool clariion_match(struct scsi_device *sdev) return false; } -static int clariion_bus_attach(struct scsi_device *sdev); -static void clariion_bus_detach(struct scsi_device *sdev); - -static struct scsi_device_handler clariion_dh = { - .name = CLARIION_NAME, - .module = THIS_MODULE, - .devlist = clariion_dev_list, - .attach = clariion_bus_attach, - .detach = clariion_bus_detach, - .check_sense = clariion_check_sense, - .activate = clariion_activate, - .prep_fn = clariion_prep_fn, - .set_params = clariion_set_params, - .match = clariion_match, -}; - -static int clariion_bus_attach(struct scsi_device *sdev) +static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data; struct clariion_dh_data *h; - unsigned long flags; int err; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", - CLARIION_NAME); - return -ENOMEM; - } - - scsi_dh_data->scsi_dh = &clariion_dh; - h = (struct clariion_dh_data *) scsi_dh_data->buf; + h = kzalloc(sizeof(*h) , GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); h->lun_state = CLARIION_LUN_UNINITIALIZED; h->default_sp = CLARIION_UNBOUND_LU; h->current_sp = CLARIION_UNBOUND_LU; @@ -692,45 +670,37 @@ static int clariion_bus_attach(struct scsi_device *sdev) if (err != SCSI_DH_OK) goto failed; - if (!try_module_get(THIS_MODULE)) - goto failed; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - sdev_printk(KERN_INFO, sdev, "%s: connected to SP %c Port %d (%s, default SP %c)\n", CLARIION_NAME, h->current_sp + 'A', h->port, lun_state[h->lun_state], h->default_sp + 'A'); - - return 0; + return &h->dh_data; failed: - kfree(scsi_dh_data); - sdev_printk(KERN_ERR, sdev, "%s: not attached\n", - CLARIION_NAME); - return -EINVAL; + kfree(h); + return ERR_PTR(-EINVAL); } static void clariion_bus_detach(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data; - unsigned long flags; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - scsi_dh_data = sdev->scsi_dh_data; - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - - sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", - CLARIION_NAME); + struct clariion_dh_data *h = get_clariion_data(sdev); - kfree(scsi_dh_data); - module_put(THIS_MODULE); + kfree(h); } +static struct scsi_device_handler clariion_dh = { + .name = CLARIION_NAME, + .module = THIS_MODULE, + .attach = clariion_bus_attach, + .detach = clariion_bus_detach, + .check_sense = clariion_check_sense, + .activate = clariion_activate, + .prep_fn = clariion_prep_fn, + .set_params = clariion_set_params, + .match = clariion_match, +}; + static int __init clariion_init(void) { int r; diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index 4ee2759f5299..485d99544a15 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -38,6 +38,7 @@ #define HP_SW_PATH_PASSIVE 1 struct hp_sw_dh_data { + struct scsi_dh_data dh_data; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int path_state; int retries; @@ -51,9 +52,7 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *); static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; - BUG_ON(scsi_dh_data == NULL); - return ((struct hp_sw_dh_data *) scsi_dh_data->buf); + return container_of(sdev->scsi_dh_data, struct hp_sw_dh_data, dh_data); } /* @@ -312,7 +311,10 @@ static int hp_sw_activate(struct scsi_device *sdev, return 0; } -static const struct scsi_dh_devlist hp_sw_dh_data_list[] = { +static const struct { + char *vendor; + char *model; +} hp_sw_dh_data_list[] = { {"COMPAQ", "MSA1000 VOLUME"}, {"COMPAQ", "HSV110"}, {"HP", "HSV100"}, @@ -338,37 +340,14 @@ static bool hp_sw_match(struct scsi_device *sdev) return false; } -static int hp_sw_bus_attach(struct scsi_device *sdev); -static void hp_sw_bus_detach(struct scsi_device *sdev); - -static struct scsi_device_handler hp_sw_dh = { - .name = HP_SW_NAME, - .module = THIS_MODULE, - .devlist = hp_sw_dh_data_list, - .attach = hp_sw_bus_attach, - .detach = hp_sw_bus_detach, - .activate = hp_sw_activate, - .prep_fn = hp_sw_prep_fn, - .match = hp_sw_match, -}; - -static int hp_sw_bus_attach(struct scsi_device *sdev) +static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data; struct hp_sw_dh_data *h; - unsigned long flags; int ret; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n", - HP_SW_NAME); - return 0; - } - - scsi_dh_data->scsi_dh = &hp_sw_dh; - h = (struct hp_sw_dh_data *) scsi_dh_data->buf; + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); h->path_state = HP_SW_PATH_UNINITIALIZED; h->retries = HP_SW_RETRIES; h->sdev = sdev; @@ -377,42 +356,32 @@ static int hp_sw_bus_attach(struct scsi_device *sdev) if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) goto failed; - if (!try_module_get(THIS_MODULE)) - goto failed; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? "active":"passive"); - - return 0; - + return &h->dh_data; failed: - kfree(scsi_dh_data); - sdev_printk(KERN_ERR, sdev, "%s: not attached\n", - HP_SW_NAME); - return -EINVAL; + kfree(h); + return ERR_PTR(-EINVAL); } static void hp_sw_bus_detach( struct scsi_device *sdev ) { - struct scsi_dh_data *scsi_dh_data; - unsigned long flags; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - scsi_dh_data = sdev->scsi_dh_data; - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - module_put(THIS_MODULE); - - sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME); + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); - kfree(scsi_dh_data); + kfree(h); } +static struct scsi_device_handler hp_sw_dh = { + .name = HP_SW_NAME, + .module = THIS_MODULE, + .attach = hp_sw_bus_attach, + .detach = hp_sw_bus_detach, + .activate = hp_sw_activate, + .prep_fn = hp_sw_prep_fn, + .match = hp_sw_match, +}; + static int __init hp_sw_init(void) { return scsi_register_device_handler(&hp_sw_dh); diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 1b5bc9293e37..b46ace3d4bf0 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -181,6 +181,7 @@ struct c2_inquiry { }; struct rdac_dh_data { + struct scsi_dh_data dh_data; struct rdac_controller *ctlr; #define UNINITIALIZED_LUN (1 << 8) unsigned lun; @@ -261,9 +262,7 @@ do { \ static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; - BUG_ON(scsi_dh_data == NULL); - return ((struct rdac_dh_data *) scsi_dh_data->buf); + return container_of(sdev->scsi_dh_data, struct rdac_dh_data, dh_data); } static struct request *get_rdac_req(struct scsi_device *sdev, @@ -779,7 +778,10 @@ static int rdac_check_sense(struct scsi_device *sdev, return SCSI_RETURN_NOT_HANDLED; } -static const struct scsi_dh_devlist rdac_dev_list[] = { +static const struct { + char *vendor; + char *model; +} rdac_dev_list[] = { {"IBM", "1722"}, {"IBM", "1724"}, {"IBM", "1726"}, @@ -825,40 +827,16 @@ static bool rdac_match(struct scsi_device *sdev) return false; } -static int rdac_bus_attach(struct scsi_device *sdev); -static void rdac_bus_detach(struct scsi_device *sdev); - -static struct scsi_device_handler rdac_dh = { - .name = RDAC_NAME, - .module = THIS_MODULE, - .devlist = rdac_dev_list, - .prep_fn = rdac_prep_fn, - .check_sense = rdac_check_sense, - .attach = rdac_bus_attach, - .detach = rdac_bus_detach, - .activate = rdac_activate, - .match = rdac_match, -}; - -static int rdac_bus_attach(struct scsi_device *sdev) +static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) { - struct scsi_dh_data *scsi_dh_data; struct rdac_dh_data *h; - unsigned long flags; int err; char array_name[ARRAY_LABEL_LEN]; char array_id[UNIQUE_ID_LEN]; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", - RDAC_NAME); - return -ENOMEM; - } - - scsi_dh_data->scsi_dh = &rdac_dh; - h = (struct rdac_dh_data *) scsi_dh_data->buf; + h = kzalloc(sizeof(*h) , GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); h->lun = UNINITIALIZED_LUN; h->state = RDAC_STATE_ACTIVE; @@ -878,19 +856,12 @@ static int rdac_bus_attach(struct scsi_device *sdev) if (err != SCSI_DH_OK) goto clean_ctlr; - if (!try_module_get(THIS_MODULE)) - goto clean_ctlr; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - sdev_printk(KERN_NOTICE, sdev, "%s: LUN %d (%s) (%s)\n", RDAC_NAME, h->lun, mode[(int)h->mode], lun_state[(int)h->lun_state]); - return 0; + return &h->dh_data; clean_ctlr: spin_lock(&list_lock); @@ -898,37 +869,34 @@ clean_ctlr: spin_unlock(&list_lock); failed: - kfree(scsi_dh_data); - sdev_printk(KERN_ERR, sdev, "%s: not attached\n", - RDAC_NAME); - return -EINVAL; + kfree(h); + return ERR_PTR(-EINVAL); } static void rdac_bus_detach( struct scsi_device *sdev ) { - struct scsi_dh_data *scsi_dh_data; - struct rdac_dh_data *h; - unsigned long flags; + struct rdac_dh_data *h = get_rdac_data(sdev); - scsi_dh_data = sdev->scsi_dh_data; - h = (struct rdac_dh_data *) scsi_dh_data->buf; if (h->ctlr && h->ctlr->ms_queued) flush_workqueue(kmpath_rdacd); - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - spin_lock(&list_lock); if (h->ctlr) kref_put(&h->ctlr->kref, release_controller); spin_unlock(&list_lock); - kfree(scsi_dh_data); - module_put(THIS_MODULE); - sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME); + kfree(h); } - +static struct scsi_device_handler rdac_dh = { + .name = RDAC_NAME, + .module = THIS_MODULE, + .prep_fn = rdac_prep_fn, + .check_sense = rdac_check_sense, + .attach = rdac_bus_attach, + .detach = rdac_bus_detach, + .activate = rdac_activate, + .match = rdac_match, +}; static int __init rdac_init(void) { diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c index 4b0dd8c56707..3e088125a8be 100644 --- a/drivers/scsi/dmx3191d.c +++ b/drivers/scsi/dmx3191d.c @@ -33,20 +33,20 @@ /* * Definitions for the generic 5380 driver. */ -#define AUTOSENSE + +#define DONT_USE_INTR #define NCR5380_read(reg) inb(port + reg) #define NCR5380_write(reg, value) outb(value, port + reg) -#define NCR5380_implementation_fields unsigned int port -#define NCR5380_local_declare() NCR5380_implementation_fields +#define NCR5380_implementation_fields /* none */ +#define NCR5380_local_declare() unsigned int port #define NCR5380_setup(instance) port = instance->io_port /* * Includes needed for NCR5380.[ch] (XXX: Move them to NCR5380.h) */ #include <linux/delay.h> -#include "scsi.h" #include "NCR5380.h" #include "NCR5380.c" @@ -58,6 +58,7 @@ static struct scsi_host_template dmx3191d_driver_template = { .proc_name = DMX3191D_DRIVER_NAME, .name = "Domex DMX3191D", + .info = NCR5380_info, .queuecommand = NCR5380_queue_command, .eh_abort_handler = NCR5380_abort, .eh_bus_reset_handler = NCR5380_bus_reset, @@ -90,31 +91,23 @@ static int dmx3191d_probe_one(struct pci_dev *pdev, if (!shost) goto out_release_region; shost->io_port = io; - shost->irq = pdev->irq; - NCR5380_init(shost, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E); + /* This card does not seem to raise an interrupt on pdev->irq. + * Steam-powered SCSI controllers run without an IRQ anyway. + */ + shost->irq = NO_IRQ; - if (request_irq(pdev->irq, NCR5380_intr, IRQF_SHARED, - DMX3191D_DRIVER_NAME, shost)) { - /* - * Steam powered scsi controllers run without an IRQ anyway - */ - printk(KERN_WARNING "dmx3191: IRQ %d not available - " - "switching to polled mode.\n", pdev->irq); - shost->irq = SCSI_IRQ_NONE; - } + NCR5380_init(shost, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E); pci_set_drvdata(pdev, shost); error = scsi_add_host(shost, &pdev->dev); if (error) - goto out_free_irq; + goto out_release_region; scsi_scan_host(shost); return 0; - out_free_irq: - free_irq(shost->irq, shost); out_release_region: release_region(io, DMX3191D_REGION_LEN); out_disable_device: @@ -131,8 +124,6 @@ static void dmx3191d_remove_one(struct pci_dev *pdev) NCR5380_exit(shost); - if (shost->irq != SCSI_IRQ_NONE) - free_irq(shost->irq, shost); release_region(shost->io_port, DMX3191D_REGION_LEN); pci_disable_device(pdev); diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 072f0ec2851e..0bf976936a10 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -415,10 +415,8 @@ static int adpt_slave_configure(struct scsi_device * device) pHba = (adpt_hba *) host->hostdata[0]; if (host->can_queue && device->tagged_supported) { - scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG, + scsi_change_queue_depth(device, host->can_queue - 1); - } else { - scsi_adjust_queue_depth(device, 0, 1); } return 0; } diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c index 0a667fe05006..4c74c7ba2dff 100644 --- a/drivers/scsi/dtc.c +++ b/drivers/scsi/dtc.c @@ -1,5 +1,4 @@ -#define AUTOSENSE #define PSEUDO_DMA #define DONT_USE_INTR #define UNSAFE /* Leave interrupts enabled during pseudo-dma I/O */ @@ -18,29 +17,9 @@ * (Unix and Linux consulting and custom programming) * drew@colorado.edu * +1 (303) 440-4894 - * - * DISTRIBUTION RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook -*/ + */ /* - * Options : - * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically - * for commands that return with a CHECK CONDITION status. - * - * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance - * increase compared to polled I/O. - * - * PARITY - enable parity checking. Not supported. - * - * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. - * You probably want this. - * * The card is detected and initialized in one of several ways : * 1. Autoprobe (default) - since the board is memory mapped, * a BIOS signature is scanned for to locate the registers. @@ -79,15 +58,11 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> -#include "scsi.h" #include <scsi/scsi_host.h> #include "dtc.h" #define AUTOPROBE_IRQ #include "NCR5380.h" - -#define DTC_PUBLIC_RELEASE 2 - /* * The DTC3180 & 3280 boards are memory mapped. * @@ -173,10 +148,13 @@ static const struct signature { * */ -static void __init dtc_setup(char *str, int *ints) +static int __init dtc_setup(char *str) { static int commandline_current = 0; int i; + int ints[10]; + + get_options(str, ARRAY_SIZE(ints), ints); if (ints[0] != 2) printk("dtc_setup: usage dtc=address,irq\n"); else if (commandline_current < NO_OVERRIDES) { @@ -189,7 +167,10 @@ static void __init dtc_setup(char *str, int *ints) } ++commandline_current; } + return 1; } + +__setup("dtc=", dtc_setup); #endif /* @@ -213,10 +194,6 @@ static int __init dtc_detect(struct scsi_host_template * tpnt) void __iomem *base; int sig, count; - tpnt->proc_name = "dtc3x80"; - tpnt->show_info = dtc_show_info; - tpnt->write_info = dtc_write_info; - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { addr = 0; base = NULL; @@ -271,38 +248,33 @@ found: else instance->irq = NCR5380_probe_irq(instance, DTC_IRQS); + /* Compatibility with documented NCR5380 kernel parameters */ + if (instance->irq == 255) + instance->irq = NO_IRQ; + #ifndef DONT_USE_INTR /* With interrupts enabled, it will sometimes hang when doing heavy * reads. So better not enable them until I finger it out. */ - if (instance->irq != SCSI_IRQ_NONE) + if (instance->irq != NO_IRQ) if (request_irq(instance->irq, dtc_intr, 0, "dtc", instance)) { printk(KERN_ERR "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); - instance->irq = SCSI_IRQ_NONE; + instance->irq = NO_IRQ; } - if (instance->irq == SCSI_IRQ_NONE) { + if (instance->irq == NO_IRQ) { printk(KERN_WARNING "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); printk(KERN_WARNING "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); } #else - if (instance->irq != SCSI_IRQ_NONE) + if (instance->irq != NO_IRQ) printk(KERN_WARNING "scsi%d : interrupts not used. Might as well not jumper it.\n", instance->host_no); - instance->irq = SCSI_IRQ_NONE; + instance->irq = NO_IRQ; #endif #if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT) printk("scsi%d : irq = %d\n", instance->host_no, instance->irq); #endif - printk(KERN_INFO "scsi%d : at 0x%05X", instance->host_no, (int) instance->base); - if (instance->irq == SCSI_IRQ_NONE) - printk(" interrupts disabled"); - else - printk(" irq %d", instance->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, DTC_PUBLIC_RELEASE); - NCR5380_print_options(instance); - printk("\n"); - ++current_override; ++count; } @@ -354,20 +326,18 @@ static int dtc_biosparam(struct scsi_device *sdev, struct block_device *dev, * timeout. */ -static int dtc_maxi = 0; -static int dtc_wmaxi = 0; - static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, int len) { unsigned char *d = dst; int i; /* For counting time spent in the poll-loop */ + struct NCR5380_hostdata *hostdata = shost_priv(instance); NCR5380_local_declare(); NCR5380_setup(instance); i = 0; NCR5380_read(RESET_PARITY_INTERRUPT_REG); NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE); - if (instance->irq == SCSI_IRQ_NONE) + if (instance->irq == NO_IRQ) NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ); else NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE); @@ -391,8 +361,8 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, NCR5380_write(MODE_REG, 0); /* Clear the operating mode */ rtrc(0); NCR5380_read(RESET_PARITY_INTERRUPT_REG); - if (i > dtc_maxi) - dtc_maxi = i; + if (i > hostdata->spin_max_r) + hostdata->spin_max_r = i; return (0); } @@ -412,13 +382,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, int len) { int i; + struct NCR5380_hostdata *hostdata = shost_priv(instance); NCR5380_local_declare(); NCR5380_setup(instance); NCR5380_read(RESET_PARITY_INTERRUPT_REG); NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE); /* set direction (write) */ - if (instance->irq == SCSI_IRQ_NONE) + if (instance->irq == NO_IRQ) NCR5380_write(DTC_CONTROL_REG, 0); else NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); @@ -444,8 +415,8 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, /* Check for parity error here. fixme. */ NCR5380_write(MODE_REG, 0); /* Clear the operating mode */ rtrc(0); - if (i > dtc_wmaxi) - dtc_wmaxi = i; + if (i > hostdata->spin_max_w) + hostdata->spin_max_w = i; return (0); } @@ -457,7 +428,7 @@ static int dtc_release(struct Scsi_Host *shost) { NCR5380_local_declare(); NCR5380_setup(shost); - if (shost->irq) + if (shost->irq != NO_IRQ) free_irq(shost->irq, shost); NCR5380_exit(shost); if (shost->io_port && shost->n_io_port) @@ -471,6 +442,10 @@ static struct scsi_host_template driver_template = { .name = "DTC 3180/3280 ", .detect = dtc_detect, .release = dtc_release, + .proc_name = "dtc3x80", + .show_info = dtc_show_info, + .write_info = dtc_write_info, + .info = dtc_info, .queuecommand = dtc_queue_command, .eh_abort_handler = dtc_abort, .eh_bus_reset_handler = dtc_bus_reset, diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h index 92d7cfc3f4fc..78a2332e9064 100644 --- a/drivers/scsi/dtc.h +++ b/drivers/scsi/dtc.h @@ -5,24 +5,6 @@ * (Unix and Linux consulting and custom programming) * drew@colorado.edu * +1 (303) 440-4894 - * - * DISTRIBUTION RELEASE 2. - * - * For more information, please consult - * - * - * - * and - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ #ifndef DTC3280_H @@ -32,13 +14,6 @@ #define DTCDEBUG_INIT 0x1 #define DTCDEBUG_TRANSFER 0x2 -static int dtc_abort(Scsi_Cmnd *); -static int dtc_biosparam(struct scsi_device *, struct block_device *, - sector_t, int*); -static int dtc_detect(struct scsi_host_template *); -static int dtc_queue_command(struct Scsi_Host *, struct scsi_cmnd *); -static int dtc_bus_reset(Scsi_Cmnd *); - #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 #endif @@ -88,6 +63,7 @@ static int dtc_bus_reset(Scsi_Cmnd *); #define NCR5380_queue_command dtc_queue_command #define NCR5380_abort dtc_abort #define NCR5380_bus_reset dtc_bus_reset +#define NCR5380_info dtc_info #define NCR5380_show_info dtc_show_info #define NCR5380_write_info dtc_write_info diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index 943ad3a19661..227dd2c2ec2f 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -946,20 +946,18 @@ static int eata2x_slave_configure(struct scsi_device *dev) if (TLDEV(dev->type) && dev->tagged_supported) { if (tag_mode == TAG_SIMPLE) { - scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd); tag_suffix = ", simple tags"; } else if (tag_mode == TAG_ORDERED) { - scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd); tag_suffix = ", ordered tags"; } else { - scsi_adjust_queue_depth(dev, 0, tqd); tag_suffix = ", no tags"; } + scsi_change_queue_depth(dev, tqd); } else if (TLDEV(dev->type) && linked_comm) { - scsi_adjust_queue_depth(dev, 0, tqd); + scsi_change_queue_depth(dev, tqd); tag_suffix = ", untagged"; } else { - scsi_adjust_queue_depth(dev, 0, utqd); + scsi_change_queue_depth(dev, utqd); tag_suffix = ""; } diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h index 3fd305d6b67d..b6030e3edd01 100644 --- a/drivers/scsi/esas2r/esas2r.h +++ b/drivers/scsi/esas2r/esas2r.h @@ -972,11 +972,6 @@ u8 handle_hba_ioctl(struct esas2r_adapter *a, struct atto_ioctl *ioctl_hba); int esas2r_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd); int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh); -int esas2r_slave_alloc(struct scsi_device *dev); -int esas2r_slave_configure(struct scsi_device *dev); -void esas2r_slave_destroy(struct scsi_device *dev); -int esas2r_change_queue_depth(struct scsi_device *dev, int depth, int reason); -int esas2r_change_queue_type(struct scsi_device *dev, int type); long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); /* SCSI error handler (eh) functions */ diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c index d89a0277a8e1..baf913047b48 100644 --- a/drivers/scsi/esas2r/esas2r_ioctl.c +++ b/drivers/scsi/esas2r/esas2r_ioctl.c @@ -117,9 +117,8 @@ static void do_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi) rq = esas2r_alloc_request(a); if (rq == NULL) { - up(&a->fm_api_semaphore); fi->status = FI_STAT_BUSY; - return; + goto free_sem; } if (fi == &a->firmware.header) { @@ -135,7 +134,7 @@ static void do_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi) if (a->firmware.header_buff == NULL) { esas2r_debug("failed to allocate header buffer!"); fi->status = FI_STAT_BUSY; - return; + goto free_req; } memcpy(a->firmware.header_buff, fi, @@ -171,9 +170,10 @@ all_done: a->firmware.header_buff, (dma_addr_t)a->firmware.header_buff_phys); } - - up(&a->fm_api_semaphore); +free_req: esas2r_free_request(a, (struct esas2r_request *)rq); +free_sem: + up(&a->fm_api_semaphore); return; } @@ -1420,9 +1420,10 @@ int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg) rq = esas2r_alloc_request(a); if (rq == NULL) { - up(&a->nvram_semaphore); - ioctl->data.prw.code = 0; - break; + kfree(ioctl); + esas2r_log(ESAS2R_LOG_WARN, + "could not allocate an internal request"); + return -ENOMEM; } code = esas2r_write_params(a, rq, @@ -1523,9 +1524,12 @@ ioctl_done: case -EINVAL: ioctl->header.return_code = IOCTL_INVALID_PARAM; break; + + default: + ioctl->header.return_code = IOCTL_GENERAL_ERROR; + break; } - ioctl->header.return_code = IOCTL_GENERAL_ERROR; } /* Always copy the buffer back, if only to pick up the status */ diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index 6504a195c874..593ff8a63c70 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -254,12 +254,10 @@ static struct scsi_host_template driver_template = { .use_clustering = ENABLE_CLUSTERING, .emulated = 0, .proc_name = ESAS2R_DRVR_NAME, - .slave_configure = esas2r_slave_configure, - .slave_alloc = esas2r_slave_alloc, - .slave_destroy = esas2r_slave_destroy, - .change_queue_depth = esas2r_change_queue_depth, - .change_queue_type = esas2r_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, .max_sectors = 0xFFFF, + .use_blk_tags = 1, }; int sgl_page_size = 512; @@ -1057,7 +1055,7 @@ int esas2r_eh_abort(struct scsi_cmnd *cmd) cmd->scsi_done(cmd); - return 0; + return SUCCESS; } spin_lock_irqsave(&a->queue_lock, flags); @@ -1259,60 +1257,6 @@ int esas2r_target_reset(struct scsi_cmnd *cmd) return esas2r_dev_targ_reset(cmd, true); } -int esas2r_change_queue_depth(struct scsi_device *dev, int depth, int reason) -{ - esas2r_log(ESAS2R_LOG_INFO, "change_queue_depth %p, %d", dev, depth); - - scsi_adjust_queue_depth(dev, scsi_get_tag_type(dev), depth); - - return dev->queue_depth; -} - -int esas2r_change_queue_type(struct scsi_device *dev, int type) -{ - esas2r_log(ESAS2R_LOG_INFO, "change_queue_type %p, %d", dev, type); - - if (dev->tagged_supported) { - scsi_set_tag_type(dev, type); - - if (type) - scsi_activate_tcq(dev, dev->queue_depth); - else - scsi_deactivate_tcq(dev, dev->queue_depth); - } else { - type = 0; - } - - return type; -} - -int esas2r_slave_alloc(struct scsi_device *dev) -{ - return 0; -} - -int esas2r_slave_configure(struct scsi_device *dev) -{ - esas2r_log_dev(ESAS2R_LOG_INFO, &(dev->sdev_gendev), - "esas2r_slave_configure()"); - - if (dev->tagged_supported) { - scsi_set_tag_type(dev, MSG_SIMPLE_TAG); - scsi_activate_tcq(dev, cmd_per_lun); - } else { - scsi_set_tag_type(dev, 0); - scsi_deactivate_tcq(dev, cmd_per_lun); - } - - return 0; -} - -void esas2r_slave_destroy(struct scsi_device *dev) -{ - esas2r_log_dev(ESAS2R_LOG_INFO, &(dev->sdev_gendev), - "esas2r_slave_destroy()"); -} - void esas2r_log_request_failure(struct esas2r_adapter *a, struct esas2r_request *rq) { diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c index 55548dc5cec3..ce5bd52fe692 100644 --- a/drivers/scsi/esp_scsi.c +++ b/drivers/scsi/esp_scsi.c @@ -49,55 +49,67 @@ static u32 esp_debug; #define ESP_DEBUG_DATADONE 0x00000100 #define ESP_DEBUG_RECONNECT 0x00000200 #define ESP_DEBUG_AUTOSENSE 0x00000400 +#define ESP_DEBUG_EVENT 0x00000800 +#define ESP_DEBUG_COMMAND 0x00001000 #define esp_log_intr(f, a...) \ do { if (esp_debug & ESP_DEBUG_INTR) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_reset(f, a...) \ do { if (esp_debug & ESP_DEBUG_RESET) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_msgin(f, a...) \ do { if (esp_debug & ESP_DEBUG_MSGIN) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_msgout(f, a...) \ do { if (esp_debug & ESP_DEBUG_MSGOUT) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_cmddone(f, a...) \ do { if (esp_debug & ESP_DEBUG_CMDDONE) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_disconnect(f, a...) \ do { if (esp_debug & ESP_DEBUG_DISCONNECT) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_datastart(f, a...) \ do { if (esp_debug & ESP_DEBUG_DATASTART) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_datadone(f, a...) \ do { if (esp_debug & ESP_DEBUG_DATADONE) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_reconnect(f, a...) \ do { if (esp_debug & ESP_DEBUG_RECONNECT) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_log_autosense(f, a...) \ do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \ - printk(f, ## a); \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ +} while (0) + +#define esp_log_event(f, a...) \ +do { if (esp_debug & ESP_DEBUG_EVENT) \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ +} while (0) + +#define esp_log_command(f, a...) \ +do { if (esp_debug & ESP_DEBUG_COMMAND) \ + shost_printk(KERN_DEBUG, esp->host, f, ## a); \ } while (0) #define esp_read8(REG) esp->ops->esp_read8(esp, REG) @@ -126,10 +138,29 @@ void scsi_esp_cmd(struct esp *esp, u8 val) esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + esp_log_command("cmd[%02x]\n", val); esp_write8(val, ESP_CMD); } EXPORT_SYMBOL(scsi_esp_cmd); +static void esp_send_dma_cmd(struct esp *esp, int len, int max_len, int cmd) +{ + if (esp->flags & ESP_FLAG_USE_FIFO) { + int i; + + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + for (i = 0; i < len; i++) + esp_write8(esp->command_block[i], ESP_FDATA); + scsi_esp_cmd(esp, cmd); + } else { + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + cmd |= ESP_CMD_DMA; + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + len, max_len, 0, cmd); + } +} + static void esp_event(struct esp *esp, u8 val) { struct esp_event_ent *p; @@ -150,19 +181,17 @@ static void esp_dump_cmd_log(struct esp *esp) int idx = esp->esp_event_cur; int stop = idx; - printk(KERN_INFO PFX "esp%d: Dumping command log\n", - esp->host->unique_id); + shost_printk(KERN_INFO, esp->host, "Dumping command log\n"); do { struct esp_event_ent *p = &esp->esp_event_log[idx]; - printk(KERN_INFO PFX "esp%d: ent[%d] %s ", - esp->host->unique_id, idx, - p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT"); - - printk("val[%02x] sreg[%02x] seqreg[%02x] " - "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n", - p->val, p->sreg, p->seqreg, - p->sreg2, p->ireg, p->select_state, p->event); + shost_printk(KERN_INFO, esp->host, + "ent[%d] %s val[%02x] sreg[%02x] seqreg[%02x] " + "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n", + idx, + p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT", + p->val, p->sreg, p->seqreg, + p->sreg2, p->ireg, p->select_state, p->event); idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); } while (idx != stop); @@ -176,9 +205,8 @@ static void esp_flush_fifo(struct esp *esp) while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) { if (--lim == 0) { - printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES " - "will not clear!\n", - esp->host->unique_id); + shost_printk(KERN_ALERT, esp->host, + "ESP_FF_BYTES will not clear!\n"); break; } udelay(1); @@ -240,6 +268,19 @@ static void esp_reset_esp(struct esp *esp) } else { esp->min_period = ((5 * esp->ccycle) / 1000); } + if (esp->rev == FAS236) { + /* + * The AM53c974 chip returns the same ID as FAS236; + * try to configure glitch eater. + */ + u8 config4 = ESP_CONFIG4_GE1; + esp_write8(config4, ESP_CFG4); + config4 = esp_read8(ESP_CFG4); + if (config4 & ESP_CONFIG4_GE1) { + esp->rev = PCSCSI; + esp_write8(esp->config4, ESP_CFG4); + } + } esp->max_period = (esp->max_period + 3)>>2; esp->min_period = (esp->min_period + 3)>>2; @@ -265,7 +306,8 @@ static void esp_reset_esp(struct esp *esp) /* fallthrough... */ case FAS236: - /* Fast 236 or HME */ + case PCSCSI: + /* Fast 236, AM53c974 or HME */ esp_write8(esp->config2, ESP_CFG2); if (esp->rev == FASHME) { u8 cfg3 = esp->target[0].esp_config3; @@ -383,12 +425,11 @@ static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent, p->cur_residue -= len; p->tot_residue -= len; if (p->cur_residue < 0 || p->tot_residue < 0) { - printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n", - esp->host->unique_id); - printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] " - "len[%u]\n", - esp->host->unique_id, - p->cur_residue, p->tot_residue, len); + shost_printk(KERN_ERR, esp->host, + "Data transfer overflow.\n"); + shost_printk(KERN_ERR, esp->host, + "cur_residue[%d] tot_residue[%d] len[%u]\n", + p->cur_residue, p->tot_residue, len); p->cur_residue = 0; p->tot_residue = 0; } @@ -604,9 +645,8 @@ static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent) if (!ent->sense_ptr) { - esp_log_autosense("esp%d: Doing auto-sense for " - "tgt[%d] lun[%d]\n", - esp->host->unique_id, tgt, lun); + esp_log_autosense("Doing auto-sense for tgt[%d] lun[%d]\n", + tgt, lun); ent->sense_ptr = cmd->sense_buffer; ent->sense_dma = esp->ops->map_single(esp, @@ -642,10 +682,7 @@ static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent) val = (p - esp->command_block); - if (esp->rev == FASHME) - scsi_esp_cmd(esp, ESP_CMD_FLUSH); - esp->ops->send_dma_cmd(esp, esp->command_block_dma, - val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA); + esp_send_dma_cmd(esp, val, 16, ESP_CMD_SELA); } static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp) @@ -663,7 +700,7 @@ static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp) return ent; } - if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) { + if (!spi_populate_tag_msg(&ent->tag[0], cmd)) { ent->tag[0] = 0; ent->tag[1] = 0; } @@ -781,12 +818,12 @@ build_identify: } if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) { - start_cmd = ESP_CMD_DMA | ESP_CMD_SELA; + start_cmd = ESP_CMD_SELA; if (ent->tag[0]) { *p++ = ent->tag[0]; *p++ = ent->tag[1]; - start_cmd = ESP_CMD_DMA | ESP_CMD_SA3; + start_cmd = ESP_CMD_SA3; } for (i = 0; i < cmd->cmd_len; i++) @@ -806,7 +843,7 @@ build_identify: esp->msg_out_len += 2; } - start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS; + start_cmd = ESP_CMD_SELAS; esp->select_state = ESP_SELECT_MSGOUT; } val = tgt; @@ -826,10 +863,7 @@ build_identify: printk("]\n"); } - if (esp->rev == FASHME) - scsi_esp_cmd(esp, ESP_CMD_FLUSH); - esp->ops->send_dma_cmd(esp, esp->command_block_dma, - val, 16, 0, start_cmd); + esp_send_dma_cmd(esp, val, 16, start_cmd); } static struct esp_cmd_entry *esp_get_ent(struct esp *esp) @@ -953,8 +987,8 @@ static int esp_check_gross_error(struct esp *esp) * - DMA programmed with wrong direction * - improper phase change */ - printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n", - esp->host->unique_id, esp->sreg); + shost_printk(KERN_ERR, esp->host, + "Gross error sreg[%02x]\n", esp->sreg); /* XXX Reset the chip. XXX */ return 1; } @@ -974,7 +1008,6 @@ static int esp_check_spur_intr(struct esp *esp) default: if (!(esp->sreg & ESP_STAT_INTR)) { - esp->ireg = esp_read8(ESP_INTRPT); if (esp->ireg & ESP_INTR_SR) return 1; @@ -982,14 +1015,13 @@ static int esp_check_spur_intr(struct esp *esp) * ESP is not, the only possibility is a DMA error. */ if (!esp->ops->dma_error(esp)) { - printk(KERN_ERR PFX "esp%d: Spurious irq, " - "sreg=%02x.\n", - esp->host->unique_id, esp->sreg); + shost_printk(KERN_ERR, esp->host, + "Spurious irq, sreg=%02x.\n", + esp->sreg); return -1; } - printk(KERN_ERR PFX "esp%d: DMA error\n", - esp->host->unique_id); + shost_printk(KERN_ERR, esp->host, "DMA error\n"); /* XXX Reset the chip. XXX */ return -1; @@ -1002,7 +1034,7 @@ static int esp_check_spur_intr(struct esp *esp) static void esp_schedule_reset(struct esp *esp) { - esp_log_reset("ESP: esp_schedule_reset() from %pf\n", + esp_log_reset("esp_schedule_reset() from %pf\n", __builtin_return_address(0)); esp->flags |= ESP_FLAG_RESETTING; esp_event(esp, ESP_EVENT_RESET); @@ -1019,20 +1051,20 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp, int i; if (!lp->num_tagged) { - printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n", - esp->host->unique_id); + shost_printk(KERN_ERR, esp->host, + "Reconnect w/num_tagged==0\n"); return NULL; } - esp_log_reconnect("ESP: reconnect tag, "); + esp_log_reconnect("reconnect tag, "); for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) { if (esp->ops->irq_pending(esp)) break; } if (i == ESP_QUICKIRQ_LIMIT) { - printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n", - esp->host->unique_id); + shost_printk(KERN_ERR, esp->host, + "Reconnect IRQ1 timeout\n"); return NULL; } @@ -1043,14 +1075,14 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp, i, esp->ireg, esp->sreg); if (esp->ireg & ESP_INTR_DC) { - printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n", - esp->host->unique_id); + shost_printk(KERN_ERR, esp->host, + "Reconnect, got disconnect.\n"); return NULL; } if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) { - printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n", - esp->host->unique_id, esp->sreg); + shost_printk(KERN_ERR, esp->host, + "Reconnect, not MIP sreg[%02x].\n", esp->sreg); return NULL; } @@ -1073,8 +1105,7 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp, udelay(1); } if (i == ESP_RESELECT_TAG_LIMIT) { - printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n", - esp->host->unique_id); + shost_printk(KERN_ERR, esp->host, "Reconnect IRQ2 timeout\n"); return NULL; } esp->ops->dma_drain(esp); @@ -1087,17 +1118,17 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp, if (esp->command_block[0] < SIMPLE_QUEUE_TAG || esp->command_block[0] > ORDERED_QUEUE_TAG) { - printk(KERN_ERR PFX "esp%d: Reconnect, bad tag " - "type %02x.\n", - esp->host->unique_id, esp->command_block[0]); + shost_printk(KERN_ERR, esp->host, + "Reconnect, bad tag type %02x.\n", + esp->command_block[0]); return NULL; } ent = lp->tagged_cmds[esp->command_block[1]]; if (!ent) { - printk(KERN_ERR PFX "esp%d: Reconnect, no entry for " - "tag %02x.\n", - esp->host->unique_id, esp->command_block[1]); + shost_printk(KERN_ERR, esp->host, + "Reconnect, no entry for tag %02x.\n", + esp->command_block[1]); return NULL; } @@ -1163,9 +1194,9 @@ static int esp_reconnect(struct esp *esp) tp = &esp->target[target]; dev = __scsi_device_lookup_by_target(tp->starget, lun); if (!dev) { - printk(KERN_ERR PFX "esp%d: Reconnect, no lp " - "tgt[%u] lun[%u]\n", - esp->host->unique_id, target, lun); + shost_printk(KERN_ERR, esp->host, + "Reconnect, no lp tgt[%u] lun[%u]\n", + target, lun); goto do_reset; } lp = dev->hostdata; @@ -1291,8 +1322,8 @@ static int esp_finish_select(struct esp *esp) return 0; } - printk("ESP: Unexpected selection completion ireg[%x].\n", - esp->ireg); + shost_printk(KERN_INFO, esp->host, + "Unexpected selection completion ireg[%x]\n", esp->ireg); esp_schedule_reset(esp); return 0; } @@ -1312,11 +1343,42 @@ static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent, (((unsigned int)esp_read8(ESP_TCMED)) << 8)); if (esp->rev == FASHME) ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16; + if (esp->rev == PCSCSI && (esp->config2 & ESP_CONFIG2_FENAB)) + ecount |= ((unsigned int)esp_read8(ESP_TCHI)) << 16; } bytes_sent = esp->data_dma_len; bytes_sent -= ecount; + /* + * The am53c974 has a DMA 'pecularity'. The doc states: + * In some odd byte conditions, one residual byte will + * be left in the SCSI FIFO, and the FIFO Flags will + * never count to '0 '. When this happens, the residual + * byte should be retrieved via PIO following completion + * of the BLAST operation. + */ + if (fifo_cnt == 1 && ent->flags & ESP_CMD_FLAG_RESIDUAL) { + size_t count = 1; + size_t offset = bytes_sent; + u8 bval = esp_read8(ESP_FDATA); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) + ent->sense_ptr[bytes_sent] = bval; + else { + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + u8 *ptr; + + ptr = scsi_kmap_atomic_sg(p->cur_sg, p->u.num_sg, + &offset, &count); + if (likely(ptr)) { + *(ptr + offset) = bval; + scsi_kunmap_atomic_sg(ptr); + } + } + bytes_sent += fifo_cnt; + ent->flags &= ~ESP_CMD_FLAG_RESIDUAL; + } if (!(ent->flags & ESP_CMD_FLAG_WRITE)) bytes_sent -= fifo_cnt; @@ -1556,8 +1618,8 @@ static void esp_msgin_extended(struct esp *esp) return; } - printk("ESP: Unexpected extended msg type %x\n", - esp->msg_in[2]); + shost_printk(KERN_INFO, esp->host, + "Unexpected extended msg type %x\n", esp->msg_in[2]); esp->msg_out[0] = ABORT_TASK_SET; esp->msg_out_len = 1; @@ -1574,7 +1636,8 @@ static int esp_msgin_process(struct esp *esp) if (msg0 & 0x80) { /* Identify */ - printk("ESP: Unexpected msgin identify\n"); + shost_printk(KERN_INFO, esp->host, + "Unexpected msgin identify\n"); return 0; } @@ -1640,10 +1703,12 @@ static int esp_msgin_process(struct esp *esp) static int esp_process_event(struct esp *esp) { - int write; + int write, i; again: write = 0; + esp_log_event("process event %d phase %x\n", + esp->event, esp->sreg & ESP_STAT_PMASK); switch (esp->event) { case ESP_EVENT_CHECK_PHASE: switch (esp->sreg & ESP_STAT_PMASK) { @@ -1673,8 +1738,9 @@ again: break; default: - printk("ESP: Unexpected phase, sreg=%02x\n", - esp->sreg); + shost_printk(KERN_INFO, esp->host, + "Unexpected phase, sreg=%02x\n", + esp->sreg); esp_schedule_reset(esp); return 0; } @@ -1708,18 +1774,17 @@ again: esp->data_dma_len = dma_len; if (!dma_len) { - printk(KERN_ERR PFX "esp%d: DMA length is zero!\n", - esp->host->unique_id); - printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n", - esp->host->unique_id, - (unsigned long long)esp_cur_dma_addr(ent, cmd), - esp_cur_dma_len(ent, cmd)); + shost_printk(KERN_ERR, esp->host, + "DMA length is zero!\n"); + shost_printk(KERN_ERR, esp->host, + "cur adr[%08llx] len[%08x]\n", + (unsigned long long)esp_cur_dma_addr(ent, cmd), + esp_cur_dma_len(ent, cmd)); esp_schedule_reset(esp); return 0; } - esp_log_datastart("ESP: start data addr[%08llx] len[%u] " - "write(%d)\n", + esp_log_datastart("start data addr[%08llx] len[%u] write(%d)\n", (unsigned long long)dma_addr, dma_len, write); esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len, @@ -1733,7 +1798,8 @@ again: int bytes_sent; if (esp->ops->dma_error(esp)) { - printk("ESP: data done, DMA error, resetting\n"); + shost_printk(KERN_INFO, esp->host, + "data done, DMA error, resetting\n"); esp_schedule_reset(esp); return 0; } @@ -1749,14 +1815,15 @@ again: /* We should always see exactly a bus-service * interrupt at the end of a successful transfer. */ - printk("ESP: data done, not BSERV, resetting\n"); + shost_printk(KERN_INFO, esp->host, + "data done, not BSERV, resetting\n"); esp_schedule_reset(esp); return 0; } bytes_sent = esp_data_bytes_sent(esp, ent, cmd); - esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n", + esp_log_datadone("data done flgs[%x] sent[%d]\n", ent->flags, bytes_sent); if (bytes_sent < 0) { @@ -1785,8 +1852,9 @@ again: } if (ent->message != COMMAND_COMPLETE) { - printk("ESP: Unexpected message %x in status\n", - ent->message); + shost_printk(KERN_INFO, esp->host, + "Unexpected message %x in status\n", + ent->message); esp_schedule_reset(esp); return 0; } @@ -1804,8 +1872,7 @@ again: scsi_esp_cmd(esp, ESP_CMD_ESEL); if (ent->message == COMMAND_COMPLETE) { - esp_log_cmddone("ESP: Command done status[%x] " - "message[%x]\n", + esp_log_cmddone("Command done status[%x] message[%x]\n", ent->status, ent->message); if (ent->status == SAM_STAT_TASK_SET_FULL) esp_event_queue_full(esp, ent); @@ -1821,16 +1888,16 @@ again: DID_OK)); } } else if (ent->message == DISCONNECT) { - esp_log_disconnect("ESP: Disconnecting tgt[%d] " - "tag[%x:%x]\n", + esp_log_disconnect("Disconnecting tgt[%d] tag[%x:%x]\n", cmd->device->id, ent->tag[0], ent->tag[1]); esp->active_cmd = NULL; esp_maybe_execute_command(esp); } else { - printk("ESP: Unexpected message %x in freebus\n", - ent->message); + shost_printk(KERN_INFO, esp->host, + "Unexpected message %x in freebus\n", + ent->message); esp_schedule_reset(esp); return 0; } @@ -1862,6 +1929,10 @@ again: if (esp->msg_out_len == 1) { esp_write8(esp->msg_out[0], ESP_FDATA); scsi_esp_cmd(esp, ESP_CMD_TI); + } else if (esp->flags & ESP_FLAG_USE_FIFO) { + for (i = 0; i < esp->msg_out_len; i++) + esp_write8(esp->msg_out[i], ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_TI); } else { /* Use DMA. */ memcpy(esp->command_block, @@ -1917,7 +1988,7 @@ again: val = esp_read8(ESP_FDATA); esp->msg_in[esp->msg_in_len++] = val; - esp_log_msgin("ESP: Got msgin byte %x\n", val); + esp_log_msgin("Got msgin byte %x\n", val); if (!esp_msgin_process(esp)) esp->msg_in_len = 0; @@ -1930,7 +2001,8 @@ again: if (esp->event != ESP_EVENT_FREE_BUS) esp_event(esp, ESP_EVENT_CHECK_PHASE); } else { - printk("ESP: MSGIN neither BSERV not FDON, resetting"); + shost_printk(KERN_INFO, esp->host, + "MSGIN neither BSERV not FDON, resetting"); esp_schedule_reset(esp); return 0; } @@ -1938,11 +2010,7 @@ again: case ESP_EVENT_CMD_START: memcpy(esp->command_block, esp->cmd_bytes_ptr, esp->cmd_bytes_left); - if (esp->rev == FASHME) - scsi_esp_cmd(esp, ESP_CMD_FLUSH); - esp->ops->send_dma_cmd(esp, esp->command_block_dma, - esp->cmd_bytes_left, 16, 0, - ESP_CMD_DMA | ESP_CMD_TI); + esp_send_dma_cmd(esp, esp->cmd_bytes_left, 16, ESP_CMD_TI); esp_event(esp, ESP_EVENT_CMD_DONE); esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; break; @@ -1961,8 +2029,8 @@ again: break; default: - printk("ESP: Unexpected event %x, resetting\n", - esp->event); + shost_printk(KERN_INFO, esp->host, + "Unexpected event %x, resetting\n", esp->event); esp_schedule_reset(esp); return 0; break; @@ -2044,7 +2112,12 @@ static void __esp_interrupt(struct esp *esp) int finish_reset, intr_done; u8 phase; + /* + * Once INTRPT is read STATUS and SSTEP are cleared. + */ esp->sreg = esp_read8(ESP_STATUS); + esp->seqreg = esp_read8(ESP_SSTEP); + esp->ireg = esp_read8(ESP_INTRPT); if (esp->flags & ESP_FLAG_RESETTING) { finish_reset = 1; @@ -2057,8 +2130,6 @@ static void __esp_interrupt(struct esp *esp) return; } - esp->ireg = esp_read8(ESP_INTRPT); - if (esp->ireg & ESP_INTR_SR) finish_reset = 1; @@ -2085,14 +2156,15 @@ static void __esp_interrupt(struct esp *esp) } } - esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] " + esp_log_intr("intr sreg[%02x] seqreg[%02x] " "sreg2[%02x] ireg[%02x]\n", esp->sreg, esp->seqreg, esp->sreg2, esp->ireg); intr_done = 0; if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) { - printk("ESP: unexpected IREG %02x\n", esp->ireg); + shost_printk(KERN_INFO, esp->host, + "unexpected IREG %02x\n", esp->ireg); if (esp->ireg & ESP_INTR_IC) esp_dump_cmd_log(esp); @@ -2149,46 +2221,50 @@ static void esp_get_revision(struct esp *esp) u8 val; esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); - esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); + if (esp->config2 == 0) { + esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); + esp_write8(esp->config2, ESP_CFG2); + + val = esp_read8(ESP_CFG2); + val &= ~ESP_CONFIG2_MAGIC; + + esp->config2 = 0; + if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { + /* + * If what we write to cfg2 does not come back, + * cfg2 is not implemented. + * Therefore this must be a plain esp100. + */ + esp->rev = ESP100; + return; + } + } + + esp_set_all_config3(esp, 5); + esp->prev_cfg3 = 5; esp_write8(esp->config2, ESP_CFG2); + esp_write8(0, ESP_CFG3); + esp_write8(esp->prev_cfg3, ESP_CFG3); - val = esp_read8(ESP_CFG2); - val &= ~ESP_CONFIG2_MAGIC; - if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { - /* If what we write to cfg2 does not come back, cfg2 is not - * implemented, therefore this must be a plain esp100. + val = esp_read8(ESP_CFG3); + if (val != 5) { + /* The cfg2 register is implemented, however + * cfg3 is not, must be esp100a. */ - esp->rev = ESP100; + esp->rev = ESP100A; } else { - esp->config2 = 0; - esp_set_all_config3(esp, 5); - esp->prev_cfg3 = 5; - esp_write8(esp->config2, ESP_CFG2); - esp_write8(0, ESP_CFG3); + esp_set_all_config3(esp, 0); + esp->prev_cfg3 = 0; esp_write8(esp->prev_cfg3, ESP_CFG3); - val = esp_read8(ESP_CFG3); - if (val != 5) { - /* The cfg2 register is implemented, however - * cfg3 is not, must be esp100a. - */ - esp->rev = ESP100A; + /* All of cfg{1,2,3} implemented, must be one of + * the fas variants, figure out which one. + */ + if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) { + esp->rev = FAST; + esp->sync_defp = SYNC_DEFP_FAST; } else { - esp_set_all_config3(esp, 0); - esp->prev_cfg3 = 0; - esp_write8(esp->prev_cfg3, ESP_CFG3); - - /* All of cfg{1,2,3} implemented, must be one of - * the fas variants, figure out which one. - */ - if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) { - esp->rev = FAST; - esp->sync_defp = SYNC_DEFP_FAST; - } else { - esp->rev = ESP236; - } - esp->config2 = 0; - esp_write8(esp->config2, ESP_CFG2); + esp->rev = ESP236; } } } @@ -2308,6 +2384,7 @@ static const char *esp_chip_names[] = { "FAS100A", "FAST", "FASHME", + "AM53C974", }; static struct scsi_transport_template *esp_transport_template; @@ -2317,6 +2394,10 @@ int scsi_esp_register(struct esp *esp, struct device *dev) static int instance; int err; + if (!esp->num_tags) + esp->num_tags = ESP_DEFAULT_TAGS; + else if (esp->num_tags >= ESP_MAX_TAG) + esp->num_tags = ESP_MAX_TAG - 1; esp->host->transportt = esp_transport_template; esp->host->max_lun = ESP_MAX_LUN; esp->host->cmd_per_lun = 2; @@ -2330,12 +2411,13 @@ int scsi_esp_register(struct esp *esp, struct device *dev) esp_bootup_reset(esp); - printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n", - esp->host->unique_id, esp->regs, esp->dma_regs, - esp->host->irq); - printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n", - esp->host->unique_id, esp_chip_names[esp->rev], - esp->cfreq / 1000000, esp->cfact, esp->scsi_id); + dev_printk(KERN_INFO, dev, "esp%u: regs[%1p:%1p] irq[%u]\n", + esp->host->unique_id, esp->regs, esp->dma_regs, + esp->host->irq); + dev_printk(KERN_INFO, dev, + "esp%u: is a %s, %u MHz (ccf=%u), SCSI ID %u\n", + esp->host->unique_id, esp_chip_names[esp->rev], + esp->cfreq / 1000000, esp->cfact, esp->scsi_id); /* Let the SCSI bus reset settle. */ ssleep(esp_bus_reset_settle); @@ -2402,28 +2484,10 @@ static int esp_slave_configure(struct scsi_device *dev) { struct esp *esp = shost_priv(dev->host); struct esp_target_data *tp = &esp->target[dev->id]; - int goal_tags, queue_depth; - - goal_tags = 0; - if (dev->tagged_supported) { - /* XXX make this configurable somehow XXX */ - goal_tags = ESP_DEFAULT_TAGS; - - if (goal_tags > ESP_MAX_TAG) - goal_tags = ESP_MAX_TAG; - } + if (dev->tagged_supported) + scsi_change_queue_depth(dev, esp->num_tags); - queue_depth = goal_tags; - if (queue_depth < dev->host->cmd_per_lun) - queue_depth = dev->host->cmd_per_lun; - - if (goal_tags) { - scsi_set_tag_type(dev, MSG_ORDERED_TAG); - scsi_activate_tcq(dev, queue_depth); - } else { - scsi_deactivate_tcq(dev, queue_depth); - } tp->flags |= ESP_TGT_DISCONNECT; if (!spi_initial_dv(dev->sdev_target)) @@ -2451,19 +2515,20 @@ static int esp_eh_abort_handler(struct scsi_cmnd *cmd) * XXX much for the final driver. */ spin_lock_irqsave(esp->host->host_lock, flags); - printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n", - esp->host->unique_id, cmd, cmd->cmnd[0]); + shost_printk(KERN_ERR, esp->host, "Aborting command [%p:%02x]\n", + cmd, cmd->cmnd[0]); ent = esp->active_cmd; if (ent) - printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n", - esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + shost_printk(KERN_ERR, esp->host, + "Current command [%p:%02x]\n", + ent->cmd, ent->cmd->cmnd[0]); list_for_each_entry(ent, &esp->queued_cmds, list) { - printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n", - esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + shost_printk(KERN_ERR, esp->host, "Queued command [%p:%02x]\n", + ent->cmd, ent->cmd->cmnd[0]); } list_for_each_entry(ent, &esp->active_cmds, list) { - printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n", - esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + shost_printk(KERN_ERR, esp->host, " Active command [%p:%02x]\n", + ent->cmd, ent->cmd->cmnd[0]); } esp_dump_cmd_log(esp); spin_unlock_irqrestore(esp->host->host_lock, flags); @@ -2631,6 +2696,7 @@ struct scsi_host_template scsi_esp_template = { .use_clustering = ENABLE_CLUSTERING, .max_sectors = 0xffff, .skip_settle_delay = 1, + .use_blk_tags = 1, }; EXPORT_SYMBOL(scsi_esp_template); diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h index cd68805e8d78..84dcbe4a6268 100644 --- a/drivers/scsi/esp_scsi.h +++ b/drivers/scsi/esp_scsi.h @@ -1,4 +1,4 @@ -/* esp_scsi.h: Defines and structures for the ESP drier. +/* esp_scsi.h: Defines and structures for the ESP driver. * * Copyright (C) 2007 David S. Miller (davem@davemloft.net) */ @@ -25,6 +25,7 @@ #define ESP_CTEST 0x0aUL /* wo Chip test register 0x28 */ #define ESP_CFG2 0x0bUL /* rw Second cfg register 0x2c */ #define ESP_CFG3 0x0cUL /* rw Third cfg register 0x30 */ +#define ESP_CFG4 0x0dUL /* rw Fourth cfg register 0x34 */ #define ESP_TCHI 0x0eUL /* rw High bits transf count 0x38 */ #define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */ #define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */ @@ -76,6 +77,18 @@ #define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ #define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ +/* ESP config register 4 read-write, found only on am53c974 chips */ +#define ESP_CONFIG4_RADE 0x04 /* Active negation */ +#define ESP_CONFIG4_RAE 0x08 /* Active negation on REQ and ACK */ +#define ESP_CONFIG4_PWD 0x20 /* Reduced power feature */ +#define ESP_CONFIG4_GE0 0x40 /* Glitch eater bit 0 */ +#define ESP_CONFIG4_GE1 0x80 /* Glitch eater bit 1 */ + +#define ESP_CONFIG_GE_12NS (0) +#define ESP_CONFIG_GE_25NS (ESP_CONFIG_GE1) +#define ESP_CONFIG_GE_35NS (ESP_CONFIG_GE0) +#define ESP_CONFIG_GE_0NS (ESP_CONFIG_GE0 | ESP_CONFIG_GE1) + /* ESP command register read-write */ /* Group 1 commands: These may be sent at any point in time to the ESP * chip. None of them can generate interrupts 'cept @@ -254,6 +267,7 @@ enum esp_rev { FAS100A = 0x04, FAST = 0x05, FASHME = 0x06, + PCSCSI = 0x07, /* AM53c974 */ }; struct esp_cmd_entry { @@ -269,6 +283,7 @@ struct esp_cmd_entry { #define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */ #define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */ #define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */ +#define ESP_CMD_FLAG_RESIDUAL 0x08 /* AM53c974 BLAST residual */ u8 tag[2]; u8 orig_tag[2]; @@ -283,7 +298,6 @@ struct esp_cmd_entry { struct completion *eh_done; }; -/* XXX make this configurable somehow XXX */ #define ESP_DEFAULT_TAGS 16 #define ESP_MAX_TARGET 16 @@ -445,7 +459,7 @@ struct esp { u8 prev_soff; u8 prev_stp; u8 prev_cfg3; - u8 __pad; + u8 num_tags; struct list_head esp_cmd_pool; @@ -466,6 +480,7 @@ struct esp { u8 bursts; u8 config1; u8 config2; + u8 config4; u8 scsi_id; u32 scsi_id_mask; @@ -479,6 +494,7 @@ struct esp { #define ESP_FLAG_WIDE_CAPABLE 0x00000008 #define ESP_FLAG_QUICKIRQ_CHECK 0x00000010 #define ESP_FLAG_DISABLE_SYNC 0x00000020 +#define ESP_FLAG_USE_FIFO 0x00000040 u8 select_state; #define ESP_SELECT_NONE 0x00 /* Not selecting */ diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 4a8ac7d8c76b..308a016fdaea 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -280,14 +280,16 @@ static struct scsi_host_template fcoe_shost_template = { .eh_device_reset_handler = fc_eh_device_reset, .eh_host_reset_handler = fc_eh_host_reset, .slave_alloc = fc_slave_alloc, - .change_queue_depth = fc_change_queue_depth, - .change_queue_type = fc_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, .max_sectors = 0xffff, + .use_blk_tags = 1, + .track_queue_depth = 1, }; /** diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index bf8d34c26f13..3b73b96619e2 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -39,7 +39,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.6.0.11" +#define DRV_VERSION "1.6.0.16" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index f3984b48f8e9..bf0bbd42efb5 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -135,6 +135,11 @@ void fnic_handle_link(struct work_struct *work) fnic->lport->host->host_no, FNIC_FC_LE, "Link Status: UP_DOWN", strlen("Link Status: UP_DOWN")); + if (fnic->config.flags & VFCF_FIP_CAPABLE) { + FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, + "deleting fip-timer during link-down\n"); + del_timer_sync(&fnic->fip_timer); + } fcoe_ctlr_link_down(&fnic->ctlr); } diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 8c56fdc3a456..0c1f8177b5b7 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -95,12 +95,10 @@ static int fnic_slave_alloc(struct scsi_device *sdev) { struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); - sdev->tagged_supported = 1; - if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; - scsi_activate_tcq(sdev, fnic_max_qdepth); + scsi_change_queue_depth(sdev, fnic_max_qdepth); return 0; } @@ -112,8 +110,8 @@ static struct scsi_host_template fnic_host_template = { .eh_device_reset_handler = fnic_device_reset, .eh_host_reset_handler = fnic_host_reset, .slave_alloc = fnic_slave_alloc, - .change_queue_depth = fc_change_queue_depth, - .change_queue_type = fc_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .can_queue = FNIC_DFLT_IO_REQ, @@ -121,6 +119,8 @@ static struct scsi_host_template fnic_host_template = { .sg_tablesize = FNIC_MAX_SG_DESC_CNT, .max_sectors = 0xffff, .shost_attrs = fnic_attrs, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static void @@ -438,21 +438,30 @@ static int fnic_dev_wait(struct vnic_dev *vdev, unsigned long time; int done; int err; + int count; + + count = 0; err = start(vdev, arg); if (err) return err; - /* Wait for func to complete...2 seconds max */ + /* Wait for func to complete. + * Sometime schedule_timeout_uninterruptible take long time + * to wake up so we do not retry as we are only waiting for + * 2 seconds in while loop. By adding count, we make sure + * we try atleast three times before returning -ETIMEDOUT + */ time = jiffies + (HZ * 2); do { err = finished(vdev, &done); + count++; if (err) return err; if (done) return 0; schedule_timeout_uninterruptible(HZ / 10); - } while (time_after(time, jiffies)); + } while (time_after(time, jiffies) || (count < 3)); return -ETIMEDOUT; } diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 961bdf5d31cd..2097de42a147 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -325,13 +325,11 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, struct fc_rport_libfc_priv *rp = rport->dd_data; struct host_sg_desc *desc; struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats; - u8 pri_tag = 0; unsigned int i; unsigned long intr_flags; int flags; u8 exch_flags; struct scsi_lun fc_lun; - char msg[2]; if (sg_count) { /* For each SGE, create a device desc entry */ @@ -357,12 +355,6 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, int_to_scsilun(sc->device->lun, &fc_lun); - pri_tag = FCPIO_ICMND_PTA_SIMPLE; - msg[0] = MSG_SIMPLE_TAG; - scsi_populate_tag_msg(sc, msg); - if (msg[0] == MSG_ORDERED_TAG) - pri_tag = FCPIO_ICMND_PTA_ORDERED; - /* Enqueue the descriptor in the Copy WQ */ spin_lock_irqsave(&fnic->wq_copy_lock[0], intr_flags); @@ -394,7 +386,8 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, io_req->sgl_list_pa, io_req->sense_buf_pa, 0, /* scsi cmd ref, always 0 */ - pri_tag, /* scsi pri and tag */ + FCPIO_ICMND_PTA_SIMPLE, + /* scsi pri and tag */ flags, /* command flags */ sc->cmnd, sc->cmd_len, scsi_bufflen(sc), @@ -428,8 +421,10 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ int ret; u64 cmd_trace; int sg_count = 0; - unsigned long flags; + unsigned long flags = 0; unsigned long ptr; + struct fc_rport_priv *rdata; + spinlock_t *io_lock = NULL; if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) return SCSI_MLQUEUE_HOST_BUSY; @@ -443,6 +438,16 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ return 0; } + rdata = lp->tt.rport_lookup(lp, rport->port_id); + if (!rdata || (rdata->rp_state == RPORT_ST_DELETE)) { + FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, + "returning IO as rport is removed\n"); + atomic64_inc(&fnic_stats->misc_stats.rport_not_ready); + sc->result = DID_NO_CONNECT; + done(sc); + return 0; + } + if (lp->state != LPORT_ST_READY || !(lp->link_up)) return SCSI_MLQUEUE_HOST_BUSY; @@ -505,6 +510,13 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ } } + /* + * Will acquire lock defore setting to IO initialized. + */ + + io_lock = fnic_io_lock_hash(fnic, sc); + spin_lock_irqsave(io_lock, flags); + /* initialize rest of io_req */ io_req->port_id = rport->port_id; io_req->start_time = jiffies; @@ -521,11 +533,9 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ * In case another thread cancelled the request, * refetch the pointer under the lock. */ - spinlock_t *io_lock = fnic_io_lock_hash(fnic, sc); FNIC_TRACE(fnic_queuecommand, sc->device->host->host_no, sc->request->tag, sc, 0, 0, 0, (((u64)CMD_FLAGS(sc) << 32) | CMD_STATE(sc))); - spin_lock_irqsave(io_lock, flags); io_req = (struct fnic_io_req *)CMD_SP(sc); CMD_SP(sc) = NULL; CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE; @@ -534,6 +544,10 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ fnic_release_ioreq_buf(fnic, io_req, sc); mempool_free(io_req, fnic->io_req_pool); } + atomic_dec(&fnic->in_flight); + /* acquire host lock before returning to SCSI */ + spin_lock(lp->host->host_lock); + return ret; } else { atomic64_inc(&fnic_stats->io_stats.active_ios); atomic64_inc(&fnic_stats->io_stats.num_ios); @@ -555,6 +569,11 @@ out: sc->request->tag, sc, io_req, sg_count, cmd_trace, (((u64)CMD_FLAGS(sc) >> 32) | CMD_STATE(sc))); + + /* if only we issued IO, will we have the io lock */ + if (CMD_FLAGS(sc) & FNIC_IO_INITIALIZED) + spin_unlock_irqrestore(io_lock, flags); + atomic_dec(&fnic->in_flight); /* acquire host lock before returning to SCSI */ spin_lock(lp->host->host_lock); diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c index acf1f95cb5c5..65a9bde26974 100644 --- a/drivers/scsi/fnic/fnic_trace.c +++ b/drivers/scsi/fnic/fnic_trace.c @@ -624,12 +624,12 @@ int fnic_fc_trace_set_data(u32 host_no, u8 frame_type, if (frame_type == FNIC_FC_RECV) { eth_fcoe_hdr_len = sizeof(struct ethhdr) + sizeof(struct fcoe_hdr); - fc_trc_frame_len = fc_trc_frame_len + eth_fcoe_hdr_len; memset((char *)fc_trace, 0xff, eth_fcoe_hdr_len); /* Copy the rest of data frame */ memcpy((char *)(fc_trace + eth_fcoe_hdr_len), (void *)frame, min_t(u8, fc_trc_frame_len, - (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE))); + (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE + - eth_fcoe_hdr_len))); } else { memcpy((char *)fc_trace, (void *)frame, min_t(u8, fc_trc_frame_len, diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index b331272e93bc..f35792f7051c 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -18,20 +18,6 @@ * * Added ISAPNP support for DTC436 adapters, * Thomas Sailer, sailer@ife.ee.ethz.ch - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ /* @@ -40,14 +26,6 @@ */ /* - * Options : - * - * PARITY - enable parity checking. Not supported. - * - * SCSI2 - enable support for SCSI-II tagged queueing. Untested. - * - * USLEEP - enable support for devices that don't disconnect. Untested. - * * The card is detected and initialized in one of several ways : * 1. With command line overrides - NCR5380=port,irq may be * used on the LILO command line to override the defaults. @@ -79,27 +57,21 @@ */ /* settings for DTC3181E card with only Mustek scanner attached */ -#define USLEEP #define USLEEP_POLL 1 #define USLEEP_SLEEP 20 #define USLEEP_WAITLONG 500 #define AUTOPROBE_IRQ -#define AUTOSENSE - #ifdef CONFIG_SCSI_GENERIC_NCR53C400 #define NCR53C400_PSEUDO_DMA 1 #define PSEUDO_DMA #define NCR53C400 -#define NCR5380_STATS -#undef NCR5380_STAT_LIMIT #endif #include <asm/io.h> #include <linux/signal.h> #include <linux/blkdev.h> -#include "scsi.h" #include <scsi/scsi_host.h> #include "g_NCR5380.h" #include "NCR5380.h" @@ -277,7 +249,7 @@ static int __init do_DTC3181E_setup(char *str) * Locks: none */ -int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) +static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) { static int current_override = 0; int count; @@ -335,7 +307,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) if (pnp_irq_valid(dev, 0)) overrides[count].irq = pnp_irq(dev, 0); else - overrides[count].irq = SCSI_IRQ_NONE; + overrides[count].irq = NO_IRQ; if (pnp_dma_valid(dev, 0)) overrides[count].dma = pnp_dma(dev, 0); else @@ -455,27 +427,22 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) else instance->irq = NCR5380_probe_irq(instance, 0xffff); - if (instance->irq != SCSI_IRQ_NONE) + /* Compatibility with documented NCR5380 kernel parameters */ + if (instance->irq == 255) + instance->irq = NO_IRQ; + + if (instance->irq != NO_IRQ) if (request_irq(instance->irq, generic_NCR5380_intr, 0, "NCR5380", instance)) { printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); - instance->irq = SCSI_IRQ_NONE; + instance->irq = NO_IRQ; } - if (instance->irq == SCSI_IRQ_NONE) { + if (instance->irq == NO_IRQ) { printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); } - printk(KERN_INFO "scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int) instance->NCR5380_instance_name); - if (instance->irq == SCSI_IRQ_NONE) - printk(" interrupts disabled"); - else - printk(" irq %d", instance->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE); - NCR5380_print_options(instance); - printk("\n"); - ++current_override; ++count; } @@ -483,19 +450,6 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) } /** - * generic_NCR5380_info - reporting string - * @host: NCR5380 to report on - * - * Report driver information for the NCR5380 - */ - -const char *generic_NCR5380_info(struct Scsi_Host *host) -{ - static const char string[] = "Generic NCR5380/53C400 Driver"; - return string; -} - -/** * generic_NCR5380_release_resources - free resources * @instance: host adapter to clean up * @@ -504,12 +458,12 @@ const char *generic_NCR5380_info(struct Scsi_Host *host) * Locks: none */ -int generic_NCR5380_release_resources(struct Scsi_Host *instance) +static int generic_NCR5380_release_resources(struct Scsi_Host *instance) { NCR5380_local_declare(); NCR5380_setup(instance); - if (instance->irq != SCSI_IRQ_NONE) + if (instance->irq != NO_IRQ) free_irq(instance->irq, instance); NCR5380_exit(instance); @@ -741,163 +695,9 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, #include "NCR5380.c" -#define PRINTP(x) seq_printf(m, x) -#define ANDP , - -static void sprint_opcode(struct seq_file *m, int opcode) -{ - PRINTP("0x%02x " ANDP opcode); -} - -static void sprint_command(struct seq_file *m, unsigned char *command) -{ - int i, s; - sprint_opcode(m, command[0]); - for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) - PRINTP("%02x " ANDP command[i]); - PRINTP("\n"); -} - -/** - * sprintf_Scsi_Cmnd - print a scsi command - * @m: seq_fil to print into - * @cmd: SCSI command block - * - * Print out the target and command data in hex - */ - -static void sprint_Scsi_Cmnd(struct seq_file *m, Scsi_Cmnd * cmd) -{ - PRINTP("host number %d destination target %d, lun %llu\n" ANDP cmd->device->host->host_no ANDP cmd->device->id ANDP cmd->device->lun); - PRINTP(" command = "); - sprint_command(m, cmd->cmnd); -} - -/** - * generic_NCR5380_proc_info - /proc for NCR5380 driver - * @buffer: buffer to print into - * @start: start position - * @offset: offset into buffer - * @len: length - * @hostno: instance to affect - * @inout: read/write - * - * Provide the procfs information for the 5380 controller. We fill - * this with useful debugging information including the commands - * being executed, disconnected command queue and the statistical - * data - * - * Locks: global cli/lock for queue walk - */ - -static int generic_NCR5380_show_info(struct seq_file *m, struct Scsi_Host *scsi_ptr) -{ - NCR5380_local_declare(); - unsigned long flags; - unsigned char status; - int i; - Scsi_Cmnd *ptr; - struct NCR5380_hostdata *hostdata; -#ifdef NCR5380_STATS - struct scsi_device *dev; -#endif - - NCR5380_setup(scsi_ptr); - hostdata = (struct NCR5380_hostdata *) scsi_ptr->hostdata; - - spin_lock_irqsave(scsi_ptr->host_lock, flags); - PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name); - PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE); - PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE); -#ifdef NCR53C400 - PRINTP("NCR53C400 extension version %d\n" ANDP NCR53C400_PUBLIC_RELEASE); - PRINTP("NCR53C400 card%s detected\n" ANDP(((struct NCR5380_hostdata *) scsi_ptr->hostdata)->flags & FLAG_NCR53C400) ? "" : " not"); -# if NCR53C400_PSEUDO_DMA - PRINTP("NCR53C400 pseudo DMA used\n"); -# endif -#else - PRINTP("NO NCR53C400 driver extensions\n"); -#endif - PRINTP("Using %s mapping at %s 0x%lx, " ANDP STRVAL(NCR5380_map_config) ANDP STRVAL(NCR5380_map_name) ANDP scsi_ptr->NCR5380_instance_name); - if (scsi_ptr->irq == SCSI_IRQ_NONE) - PRINTP("no interrupt\n"); - else - PRINTP("on interrupt %d\n" ANDP scsi_ptr->irq); - -#ifdef NCR5380_STATS - if (hostdata->connected || hostdata->issue_queue || hostdata->disconnected_queue) - PRINTP("There are commands pending, transfer rates may be crud\n"); - if (hostdata->pendingr) - PRINTP(" %d pending reads" ANDP hostdata->pendingr); - if (hostdata->pendingw) - PRINTP(" %d pending writes" ANDP hostdata->pendingw); - if (hostdata->pendingr || hostdata->pendingw) - PRINTP("\n"); - shost_for_each_device(dev, scsi_ptr) { - unsigned long br = hostdata->bytes_read[dev->id]; - unsigned long bw = hostdata->bytes_write[dev->id]; - long tr = hostdata->time_read[dev->id] / HZ; - long tw = hostdata->time_write[dev->id] / HZ; - - PRINTP(" T:%d %s " ANDP dev->id ANDP scsi_device_type(dev->type)); - for (i = 0; i < 8; i++) - if (dev->vendor[i] >= 0x20) - seq_putc(m, dev->vendor[i]); - seq_putc(m, ' '); - for (i = 0; i < 16; i++) - if (dev->model[i] >= 0x20) - seq_putc(m, dev->model[i]); - seq_putc(m, ' '); - for (i = 0; i < 4; i++) - if (dev->rev[i] >= 0x20) - seq_putc(m, dev->rev[i]); - seq_putc(m, ' '); - - PRINTP("\n%10ld kb read in %5ld secs" ANDP br / 1024 ANDP tr); - if (tr) - PRINTP(" @ %5ld bps" ANDP br / tr); - - PRINTP("\n%10ld kb written in %5ld secs" ANDP bw / 1024 ANDP tw); - if (tw) - PRINTP(" @ %5ld bps" ANDP bw / tw); - PRINTP("\n"); - } -#endif - - status = NCR5380_read(STATUS_REG); - if (!(status & SR_REQ)) - PRINTP("REQ not asserted, phase unknown.\n"); - else { - for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i); - PRINTP("Phase %s\n" ANDP phases[i].name); - } - - if (!hostdata->connected) { - PRINTP("No currently connected command\n"); - } else { - sprint_Scsi_Cmnd(m, (Scsi_Cmnd *) hostdata->connected); - } - - PRINTP("issue_queue\n"); - - for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) - sprint_Scsi_Cmnd(m, ptr); - - PRINTP("disconnected_queue\n"); - - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) - sprint_Scsi_Cmnd(m, ptr); - - spin_unlock_irqrestore(scsi_ptr->host_lock, flags); - return 0; -} - -#undef PRINTP -#undef ANDP - static struct scsi_host_template driver_template = { .show_info = generic_NCR5380_show_info, - .name = "Generic NCR5380/NCR53C400 Scsi Driver", + .name = "Generic NCR5380/NCR53C400 SCSI", .detect = generic_NCR5380_detect, .release = generic_NCR5380_release_resources, .info = generic_NCR5380_info, diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h index 703adf78e0b2..bea1a3b9b862 100644 --- a/drivers/scsi/g_NCR5380.h +++ b/drivers/scsi/g_NCR5380.h @@ -9,28 +9,11 @@ * * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin * K.Lentin@cs.monash.edu.au - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ #ifndef GENERIC_NCR5380_H #define GENERIC_NCR5380_H - -#define GENERIC_NCR5380_PUBLIC_RELEASE 1 - #ifdef NCR53C400 #define BIOSPARAM #define NCR5380_BIOSPARAM generic_NCR5380_biosparam @@ -39,12 +22,6 @@ #endif #ifndef ASM -static int generic_NCR5380_abort(Scsi_Cmnd *); -static int generic_NCR5380_detect(struct scsi_host_template *); -static int generic_NCR5380_release_resources(struct Scsi_Host *); -static int generic_NCR5380_queue_command(struct Scsi_Host *, struct scsi_cmnd *); -static int generic_NCR5380_bus_reset(Scsi_Cmnd *); -static const char* generic_NCR5380_info(struct Scsi_Host *); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 @@ -118,7 +95,8 @@ static const char* generic_NCR5380_info(struct Scsi_Host *); #define NCR5380_bus_reset generic_NCR5380_bus_reset #define NCR5380_pread generic_NCR5380_pread #define NCR5380_pwrite generic_NCR5380_pwrite -#define NCR5380_proc_info notyet_generic_proc_info +#define NCR5380_info generic_NCR5380_info +#define NCR5380_show_info generic_NCR5380_show_info #define BOARD_NCR5380 0 #define BOARD_NCR53C400 1 diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 0f1ae13ce7c7..71e138044379 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -2159,7 +2159,7 @@ static void gdth_next(gdth_ha_str *ha) case VERIFY: case START_STOP: case MODE_SENSE: - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], nscp->cmnd[4],nscp->cmnd[5])); @@ -2391,7 +2391,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) gdth_copy_internal_data(ha, scp, (char*)&rdc, sizeof(gdth_rdcap_data)); break; - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: if ((scp->cmnd[1] & 0x1f) == SAI_READ_CAPACITY_16 && (ha->cache_feat & GDT_64BIT)) { gdth_rdcap16_data rdc16; @@ -4661,7 +4661,6 @@ static void gdth_flush(gdth_ha_str *ha) /* configure lun */ static int gdth_slave_configure(struct scsi_device *sdev) { - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); sdev->skip_ms_page_3f = 1; sdev->skip_ms_page_8 = 1; return 0; diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 6de80e352871..8bb173e01084 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -418,7 +418,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->cmd_per_lun = sht->cmd_per_lun; shost->unchecked_isa_dma = sht->unchecked_isa_dma; shost->use_clustering = sht->use_clustering; - shost->ordered_tag = sht->ordered_tag; shost->no_write_same = sht->no_write_same; if (shost_eh_deadline == -1 || !sht->eh_host_reset_handler) @@ -485,8 +484,8 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) WQ_UNBOUND | WQ_MEM_RECLAIM, 1, shost->host_no); if (!shost->tmf_work_q) { - printk(KERN_WARNING "scsi%d: failed to create tmf workq\n", - shost->host_no); + shost_printk(KERN_WARNING, shost, + "failed to create tmf workq\n"); goto fail_kthread; } scsi_proc_hostdir_add(shost->hostt); diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index cef5d49b59cd..6bb4611b238a 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -48,6 +48,7 @@ #include <linux/bitmap.h> #include <linux/atomic.h> #include <linux/jiffies.h> +#include <linux/percpu-defs.h> #include <linux/percpu.h> #include <asm/div64.h> #include "hpsa_cmd.h" @@ -103,7 +104,6 @@ static const struct pci_device_id hpsa_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1922}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1923}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1924}, - {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1925}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1926}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1928}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1929}, @@ -149,6 +149,7 @@ static struct board_type products[] = { {0x3249103C, "Smart Array P812", &SA5_access}, {0x324A103C, "Smart Array P712m", &SA5_access}, {0x324B103C, "Smart Array P711m", &SA5_access}, + {0x3233103C, "HP StorageWorks 1210m", &SA5_access}, /* alias of 333f */ {0x3350103C, "Smart Array P222", &SA5_access}, {0x3351103C, "Smart Array P420", &SA5_access}, {0x3352103C, "Smart Array P421", &SA5_access}, @@ -193,12 +194,13 @@ static int number_of_controllers; static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id); static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id); -static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg); +static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg); static void lock_and_start_io(struct ctlr_info *h); static void start_io(struct ctlr_info *h, unsigned long *flags); #ifdef CONFIG_COMPAT -static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg); +static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, + void __user *arg); #endif static void cmd_free(struct ctlr_info *h, struct CommandList *c); @@ -214,8 +216,6 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd); static void hpsa_scan_start(struct Scsi_Host *); static int hpsa_scan_finished(struct Scsi_Host *sh, unsigned long elapsed_time); -static int hpsa_change_queue_depth(struct scsi_device *sdev, - int qdepth, int reason); static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd); static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd); @@ -274,12 +274,12 @@ static int check_for_unit_attention(struct ctlr_info *h, "detected, command retried\n", h->ctlr); break; case LUN_FAILED: - dev_warn(&h->pdev->dev, HPSA "%d: LUN failure " - "detected, action required\n", h->ctlr); + dev_warn(&h->pdev->dev, + HPSA "%d: LUN failure detected\n", h->ctlr); break; case REPORT_LUNS_CHANGED: - dev_warn(&h->pdev->dev, HPSA "%d: report LUN data " - "changed, action required\n", h->ctlr); + dev_warn(&h->pdev->dev, + HPSA "%d: report LUN data changed\n", h->ctlr); /* * Note: this REPORT_LUNS_CHANGED condition only occurs on the external * target (array) devices. @@ -392,7 +392,8 @@ static ssize_t host_show_commands_outstanding(struct device *dev, struct Scsi_Host *shost = class_to_shost(dev); struct ctlr_info *h = shost_to_hba(shost); - return snprintf(buf, 20, "%d\n", h->commands_outstanding); + return snprintf(buf, 20, "%d\n", + atomic_read(&h->commands_outstanding)); } static ssize_t host_show_transport_mode(struct device *dev, @@ -670,7 +671,7 @@ static struct scsi_host_template hpsa_driver_template = { .queuecommand = hpsa_scsi_queue_command, .scan_start = hpsa_scan_start, .scan_finished = hpsa_scan_finished, - .change_queue_depth = hpsa_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .this_id = -1, .use_clustering = ENABLE_CLUSTERING, .eh_abort_handler = hpsa_eh_abort_handler, @@ -698,7 +699,6 @@ static inline u32 next_command(struct ctlr_info *h, u8 q) { u32 a; struct reply_queue_buffer *rq = &h->reply_queue[q]; - unsigned long flags; if (h->transMethod & CFGTBL_Trans_io_accel1) return h->access.command_completed(h, q); @@ -709,9 +709,7 @@ static inline u32 next_command(struct ctlr_info *h, u8 q) if ((rq->head[rq->current_entry] & 1) == rq->wraparound) { a = rq->head[rq->current_entry]; rq->current_entry++; - spin_lock_irqsave(&h->lock, flags); - h->commands_outstanding--; - spin_unlock_irqrestore(&h->lock, flags); + atomic_dec(&h->commands_outstanding); } else { a = FIFO_EMPTY; } @@ -1500,22 +1498,22 @@ static int hpsa_map_sg_chain_block(struct ctlr_info *h, { struct SGDescriptor *chain_sg, *chain_block; u64 temp64; + u32 chain_len; chain_sg = &c->SG[h->max_cmd_sg_entries - 1]; chain_block = h->cmd_sg_list[c->cmdindex]; - chain_sg->Ext = HPSA_SG_CHAIN; - chain_sg->Len = sizeof(*chain_sg) * + chain_sg->Ext = cpu_to_le32(HPSA_SG_CHAIN); + chain_len = sizeof(*chain_sg) * (c->Header.SGTotal - h->max_cmd_sg_entries); - temp64 = pci_map_single(h->pdev, chain_block, chain_sg->Len, + chain_sg->Len = cpu_to_le32(chain_len); + temp64 = pci_map_single(h->pdev, chain_block, chain_len, PCI_DMA_TODEVICE); if (dma_mapping_error(&h->pdev->dev, temp64)) { /* prevent subsequent unmapping */ - chain_sg->Addr.lower = 0; - chain_sg->Addr.upper = 0; + chain_sg->Addr = cpu_to_le64(0); return -1; } - chain_sg->Addr.lower = (u32) (temp64 & 0x0FFFFFFFFULL); - chain_sg->Addr.upper = (u32) ((temp64 >> 32) & 0x0FFFFFFFFULL); + chain_sg->Addr = cpu_to_le64(temp64); return 0; } @@ -1523,15 +1521,13 @@ static void hpsa_unmap_sg_chain_block(struct ctlr_info *h, struct CommandList *c) { struct SGDescriptor *chain_sg; - union u64bit temp64; - if (c->Header.SGTotal <= h->max_cmd_sg_entries) + if (le16_to_cpu(c->Header.SGTotal) <= h->max_cmd_sg_entries) return; chain_sg = &c->SG[h->max_cmd_sg_entries - 1]; - temp64.val32.lower = chain_sg->Addr.lower; - temp64.val32.upper = chain_sg->Addr.upper; - pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE); + pci_unmap_single(h->pdev, le64_to_cpu(chain_sg->Addr), + le32_to_cpu(chain_sg->Len), PCI_DMA_TODEVICE); } @@ -1732,8 +1728,7 @@ static void complete_scsi_command(struct CommandList *cp) struct io_accel1_cmd *c = &h->ioaccel_cmd_pool[cp->cmdindex]; cp->Header.SGList = cp->Header.SGTotal = scsi_sg_count(cmd); cp->Request.CDBLen = c->io_flags & IOACCEL1_IOFLAGS_CDBLEN_MASK; - cp->Header.Tag.lower = c->Tag.lower; - cp->Header.Tag.upper = c->Tag.upper; + cp->Header.tag = c->tag; memcpy(cp->Header.LUN.LunAddrBytes, c->CISS_LUN, 8); memcpy(cp->Request.CDB, c->CDB, cp->Request.CDBLen); @@ -1763,72 +1758,13 @@ static void complete_scsi_command(struct CommandList *cp) /* Get addition sense code qualifier */ ascq = ei->SenseInfo[13]; } - if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) { - if (check_for_unit_attention(h, cp)) - break; - if (sense_key == ILLEGAL_REQUEST) { - /* - * SCSI REPORT_LUNS is commonly unsupported on - * Smart Array. Suppress noisy complaint. - */ - if (cp->Request.CDB[0] == REPORT_LUNS) - break; - - /* If ASC/ASCQ indicate Logical Unit - * Not Supported condition, - */ - if ((asc == 0x25) && (ascq == 0x0)) { - dev_warn(&h->pdev->dev, "cp %p " - "has check condition\n", cp); - break; - } - } - - if (sense_key == NOT_READY) { - /* If Sense is Not Ready, Logical Unit - * Not ready, Manual Intervention - * required - */ - if ((asc == 0x04) && (ascq == 0x03)) { - dev_warn(&h->pdev->dev, "cp %p " - "has check condition: unit " - "not ready, manual " - "intervention required\n", cp); - break; - } - } if (sense_key == ABORTED_COMMAND) { - /* Aborted command is retryable */ - dev_warn(&h->pdev->dev, "cp %p " - "has check condition: aborted command: " - "ASC: 0x%x, ASCQ: 0x%x\n", - cp, asc, ascq); cmd->result |= DID_SOFT_ERROR << 16; break; } - /* Must be some other type of check condition */ - dev_dbg(&h->pdev->dev, "cp %p has check condition: " - "unknown type: " - "Sense: 0x%x, ASC: 0x%x, ASCQ: 0x%x, " - "Returning result: 0x%x, " - "cmd=[%02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x]\n", - cp, sense_key, asc, ascq, - cmd->result, - cmd->cmnd[0], cmd->cmnd[1], - cmd->cmnd[2], cmd->cmnd[3], - cmd->cmnd[4], cmd->cmnd[5], - cmd->cmnd[6], cmd->cmnd[7], - cmd->cmnd[8], cmd->cmnd[9], - cmd->cmnd[10], cmd->cmnd[11], - cmd->cmnd[12], cmd->cmnd[13], - cmd->cmnd[14], cmd->cmnd[15]); break; } - - /* Problem was not a check condition * Pass it up to the upper layers... */ @@ -1934,14 +1870,11 @@ static void hpsa_pci_unmap(struct pci_dev *pdev, struct CommandList *c, int sg_used, int data_direction) { int i; - union u64bit addr64; - for (i = 0; i < sg_used; i++) { - addr64.val32.lower = c->SG[i].Addr.lower; - addr64.val32.upper = c->SG[i].Addr.upper; - pci_unmap_single(pdev, (dma_addr_t) addr64.val, c->SG[i].Len, - data_direction); - } + for (i = 0; i < sg_used; i++) + pci_unmap_single(pdev, (dma_addr_t) le64_to_cpu(c->SG[i].Addr), + le32_to_cpu(c->SG[i].Len), + data_direction); } static int hpsa_map_one(struct pci_dev *pdev, @@ -1954,25 +1887,22 @@ static int hpsa_map_one(struct pci_dev *pdev, if (buflen == 0 || data_direction == PCI_DMA_NONE) { cp->Header.SGList = 0; - cp->Header.SGTotal = 0; + cp->Header.SGTotal = cpu_to_le16(0); return 0; } - addr64 = (u64) pci_map_single(pdev, buf, buflen, data_direction); + addr64 = pci_map_single(pdev, buf, buflen, data_direction); if (dma_mapping_error(&pdev->dev, addr64)) { /* Prevent subsequent unmap of something never mapped */ cp->Header.SGList = 0; - cp->Header.SGTotal = 0; + cp->Header.SGTotal = cpu_to_le16(0); return -1; } - cp->SG[0].Addr.lower = - (u32) (addr64 & (u64) 0x00000000FFFFFFFF); - cp->SG[0].Addr.upper = - (u32) ((addr64 >> 32) & (u64) 0x00000000FFFFFFFF); - cp->SG[0].Len = buflen; - cp->SG[0].Ext = HPSA_SG_LAST; /* we are not chaining */ - cp->Header.SGList = (u8) 1; /* no. SGs contig in this cmd */ - cp->Header.SGTotal = (u16) 1; /* total sgs in this cmd list */ + cp->SG[0].Addr = cpu_to_le64(addr64); + cp->SG[0].Len = cpu_to_le32(buflen); + cp->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* we are not chaining */ + cp->Header.SGList = 1; /* no. SGs contig in this cmd */ + cp->Header.SGTotal = cpu_to_le16(1); /* total sgs in cmd list */ return 0; } @@ -2830,8 +2760,8 @@ static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h, if (d == NULL) return 0; /* no match */ - it_nexus = cpu_to_le32((u32) d->ioaccel_handle); - scsi_nexus = cpu_to_le32((u32) c2a->scsi_nexus); + it_nexus = cpu_to_le32(d->ioaccel_handle); + scsi_nexus = cpu_to_le32(c2a->scsi_nexus); find = c2a->scsi_nexus; if (h->raid_offload_debug > 0) @@ -2891,7 +2821,7 @@ static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h, * Returns 0 on success, -1 otherwise. */ static int hpsa_gather_lun_info(struct ctlr_info *h, - int reportlunsize, + int reportphyslunsize, int reportloglunsize, struct ReportLUNdata *physdev, u32 *nphysicals, int *physical_mode, struct ReportLUNdata *logdev, u32 *nlogicals) { @@ -2905,7 +2835,7 @@ static int hpsa_gather_lun_info(struct ctlr_info *h, *physical_mode = HPSA_REPORT_PHYS_EXTENDED; physical_entry_size = 24; } - if (hpsa_scsi_do_report_phys_luns(h, physdev, reportlunsize, + if (hpsa_scsi_do_report_phys_luns(h, physdev, reportphyslunsize, *physical_mode)) { dev_err(&h->pdev->dev, "report physical LUNs failed.\n"); return -1; @@ -2918,7 +2848,7 @@ static int hpsa_gather_lun_info(struct ctlr_info *h, *nphysicals - HPSA_MAX_PHYS_LUN); *nphysicals = HPSA_MAX_PHYS_LUN; } - if (hpsa_scsi_do_report_log_luns(h, logdev, reportlunsize)) { + if (hpsa_scsi_do_report_log_luns(h, logdev, reportloglunsize)) { dev_err(&h->pdev->dev, "report logical LUNs failed.\n"); return -1; } @@ -2941,8 +2871,8 @@ static int hpsa_gather_lun_info(struct ctlr_info *h, return 0; } -u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i, - int nphysicals, int nlogicals, +static u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, + int i, int nphysicals, int nlogicals, struct ReportExtendedLUNdata *physdev_list, struct ReportLUNdata *logdev_list) { @@ -3011,15 +2941,14 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) u32 ndev_allocated = 0; struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice; int ncurrent = 0; - int reportlunsize = sizeof(*physdev_list) + HPSA_MAX_PHYS_LUN * 24; int i, n_ext_target_devs, ndevs_to_allocate; int raid_ctlr_position; int rescan_hba_mode; DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS); currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL); - physdev_list = kzalloc(reportlunsize, GFP_KERNEL); - logdev_list = kzalloc(reportlunsize, GFP_KERNEL); + physdev_list = kzalloc(sizeof(*physdev_list), GFP_KERNEL); + logdev_list = kzalloc(sizeof(*logdev_list), GFP_KERNEL); tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL); if (!currentsd || !physdev_list || !logdev_list || !tmpdevice) { @@ -3039,7 +2968,8 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) h->hba_mode_enabled = rescan_hba_mode; - if (hpsa_gather_lun_info(h, reportlunsize, + if (hpsa_gather_lun_info(h, + sizeof(*physdev_list), sizeof(*logdev_list), (struct ReportLUNdata *) physdev_list, &nphysicals, &physical_mode, logdev_list, &nlogicals)) goto out; @@ -3210,19 +3140,19 @@ static int hpsa_scatter_gather(struct ctlr_info *h, } addr64 = (u64) sg_dma_address(sg); len = sg_dma_len(sg); - curr_sg->Addr.lower = (u32) (addr64 & 0x0FFFFFFFFULL); - curr_sg->Addr.upper = (u32) ((addr64 >> 32) & 0x0FFFFFFFFULL); - curr_sg->Len = len; - curr_sg->Ext = (i < scsi_sg_count(cmd) - 1) ? 0 : HPSA_SG_LAST; + curr_sg->Addr = cpu_to_le64(addr64); + curr_sg->Len = cpu_to_le32(len); + curr_sg->Ext = cpu_to_le32(0); curr_sg++; } + (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST); if (use_sg + chained > h->maxSG) h->maxSG = use_sg + chained; if (chained) { cp->Header.SGList = h->max_cmd_sg_entries; - cp->Header.SGTotal = (u16) (use_sg + 1); + cp->Header.SGTotal = cpu_to_le16(use_sg + 1); if (hpsa_map_sg_chain_block(h, cp)) { scsi_dma_unmap(cmd); return -1; @@ -3233,7 +3163,7 @@ static int hpsa_scatter_gather(struct ctlr_info *h, sglist_finished: cp->Header.SGList = (u8) use_sg; /* no. SGs contig in this cmd */ - cp->Header.SGTotal = (u16) use_sg; /* total sgs in this cmd list */ + cp->Header.SGTotal = cpu_to_le16(use_sg); /* total sgs in this cmd list */ return 0; } @@ -3325,17 +3255,12 @@ static int hpsa_scsi_ioaccel1_queue_command(struct ctlr_info *h, addr64 = (u64) sg_dma_address(sg); len = sg_dma_len(sg); total_len += len; - curr_sg->Addr.lower = (u32) (addr64 & 0x0FFFFFFFFULL); - curr_sg->Addr.upper = - (u32) ((addr64 >> 32) & 0x0FFFFFFFFULL); - curr_sg->Len = len; - - if (i == (scsi_sg_count(cmd) - 1)) - curr_sg->Ext = HPSA_SG_LAST; - else - curr_sg->Ext = 0; /* we are not chaining */ + curr_sg->Addr = cpu_to_le64(addr64); + curr_sg->Len = cpu_to_le32(len); + curr_sg->Ext = cpu_to_le32(0); curr_sg++; } + (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST); switch (cmd->sc_data_direction) { case DMA_TO_DEVICE: @@ -3592,7 +3517,7 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, cp->data_len = cpu_to_le32(total_len); cp->err_ptr = cpu_to_le64(c->busaddr + offsetof(struct io_accel2_cmd, error_data)); - cp->err_len = cpu_to_le32((u32) sizeof(cp->error_data)); + cp->err_len = cpu_to_le32(sizeof(cp->error_data)); enqueue_cmd_and_start_io(h, c); return 0; @@ -3809,11 +3734,6 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, offload_to_mirror = (offload_to_mirror >= map->layout_map_count - 1) ? 0 : offload_to_mirror + 1; - /* FIXME: remove after debug/dev */ - BUG_ON(offload_to_mirror >= map->layout_map_count); - dev_warn(&h->pdev->dev, - "DEBUG: Using physical disk map index %d from mirror group %d\n", - map_index, offload_to_mirror); dev->offload_to_mirror = offload_to_mirror; /* Avoid direct use of dev->offload_to_mirror within this * function since multiple threads might simultaneously @@ -3959,8 +3879,11 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, dev->scsi3addr); } -static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) +/* + * Running in struct Scsi_Host->host_lock less mode using LLD internal + * struct ctlr_info *h->lock w/ spin_lock_irqsave() protection. + */ +static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd) { struct ctlr_info *h; struct hpsa_scsi_dev_t *dev; @@ -3973,14 +3896,14 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, dev = cmd->device->hostdata; if (!dev) { cmd->result = DID_NO_CONNECT << 16; - done(cmd); + cmd->scsi_done(cmd); return 0; } memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr)); if (unlikely(lockup_detected(h))) { cmd->result = DID_ERROR << 16; - done(cmd); + cmd->scsi_done(cmd); return 0; } c = cmd_alloc(h); @@ -3990,9 +3913,6 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, } /* Fill in the command list header */ - - cmd->scsi_done = done; /* save this for use by completion code */ - /* save c in case we have to abort it */ cmd->host_scribble = (unsigned char *) c; @@ -4026,8 +3946,8 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, c->Header.ReplyQueue = 0; /* unused in simple mode */ memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8); - c->Header.Tag.lower = (c->cmdindex << DIRECT_LOOKUP_SHIFT); - c->Header.Tag.lower |= DIRECT_LOOKUP_BIT; + c->Header.tag = cpu_to_le64((c->cmdindex << DIRECT_LOOKUP_SHIFT) | + DIRECT_LOOKUP_BIT); /* Fill in the request block... */ @@ -4036,17 +3956,18 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB)); c->Request.CDBLen = cmd->cmd_len; memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len); - c->Request.Type.Type = TYPE_CMD; - c->Request.Type.Attribute = ATTR_SIMPLE; switch (cmd->sc_data_direction) { case DMA_TO_DEVICE: - c->Request.Type.Direction = XFER_WRITE; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_WRITE); break; case DMA_FROM_DEVICE: - c->Request.Type.Direction = XFER_READ; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_READ); break; case DMA_NONE: - c->Request.Type.Direction = XFER_NONE; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_NONE); break; case DMA_BIDIRECTIONAL: /* This can happen if a buggy application does a scsi passthru @@ -4054,7 +3975,8 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, * ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() ) */ - c->Request.Type.Direction = XFER_RSVD; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_RSVD); /* This is technically wrong, and hpsa controllers should * reject it with CMD_INVALID, which is the most correct * response, but non-fibre backends appear to let it @@ -4081,8 +4003,6 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, return 0; } -static DEF_SCSI_QCMD(hpsa_scsi_queue_command) - static int do_not_scan_if_controller_locked_up(struct ctlr_info *h) { unsigned long flags; @@ -4152,23 +4072,6 @@ static int hpsa_scan_finished(struct Scsi_Host *sh, return finished; } -static int hpsa_change_queue_depth(struct scsi_device *sdev, - int qdepth, int reason) -{ - struct ctlr_info *h = sdev_to_hba(sdev); - - if (reason != SCSI_QDEPTH_DEFAULT) - return -ENOTSUPP; - - if (qdepth < 1) - qdepth = 1; - else - if (qdepth > h->nr_cmds) - qdepth = h->nr_cmds; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - return sdev->queue_depth; -} - static void hpsa_unregister_scsi(struct ctlr_info *h) { /* we are being forcibly unloaded, and may not refuse. */ @@ -4329,8 +4232,8 @@ static void hpsa_get_tag(struct ctlr_info *h, if (c->cmd_type == CMD_IOACCEL1) { struct io_accel1_cmd *cm1 = (struct io_accel1_cmd *) &h->ioaccel_cmd_pool[c->cmdindex]; - *tagupper = cm1->Tag.upper; - *taglower = cm1->Tag.lower; + *tagupper = (u32) (cm1->tag >> 32); + *taglower = (u32) (cm1->tag & 0x0ffffffffULL); return; } if (c->cmd_type == CMD_IOACCEL2) { @@ -4341,11 +4244,10 @@ static void hpsa_get_tag(struct ctlr_info *h, *taglower = cm2->Tag; return; } - *tagupper = c->Header.Tag.upper; - *taglower = c->Header.Tag.lower; + *tagupper = (u32) (c->Header.tag >> 32); + *taglower = (u32) (c->Header.tag & 0x0ffffffffULL); } - static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, struct CommandList *abort, int swizzle) { @@ -4410,7 +4312,7 @@ static struct CommandList *hpsa_find_cmd_in_queue(struct ctlr_info *h, struct CommandList *c = NULL; /* ptr into cmpQ */ if (!find) - return 0; + return NULL; spin_lock_irqsave(&h->lock, flags); list_for_each_entry(c, queue_head, list) { if (c->scsi_cmd == NULL) /* e.g.: passthru ioctl */ @@ -4432,7 +4334,7 @@ static struct CommandList *hpsa_find_cmd_in_queue_by_tag(struct ctlr_info *h, spin_lock_irqsave(&h->lock, flags); list_for_each_entry(c, queue_head, list) { - if (memcmp(&c->Header.Tag, tag, 8) != 0) + if (memcmp(&c->Header.tag, tag, 8) != 0) continue; spin_unlock_irqrestore(&h->lock, flags); return c; @@ -4686,19 +4588,32 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h) int i; union u64bit temp64; dma_addr_t cmd_dma_handle, err_dma_handle; - unsigned long flags; + int loopcount; + + /* There is some *extremely* small but non-zero chance that that + * multiple threads could get in here, and one thread could + * be scanning through the list of bits looking for a free + * one, but the free ones are always behind him, and other + * threads sneak in behind him and eat them before he can + * get to them, so that while there is always a free one, a + * very unlucky thread might be starved anyway, never able to + * beat the other threads. In reality, this happens so + * infrequently as to be indistinguishable from never. + */ - spin_lock_irqsave(&h->lock, flags); + loopcount = 0; do { i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds); - if (i == h->nr_cmds) { - spin_unlock_irqrestore(&h->lock, flags); - return NULL; - } - } while (test_and_set_bit - (i & (BITS_PER_LONG - 1), - h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0); - spin_unlock_irqrestore(&h->lock, flags); + if (i == h->nr_cmds) + i = 0; + loopcount++; + } while (test_and_set_bit(i & (BITS_PER_LONG - 1), + h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0 && + loopcount < 10); + + /* Thread got starved? We do not expect this to ever happen. */ + if (loopcount >= 10) + return NULL; c = h->cmd_pool + i; memset(c, 0, sizeof(*c)); @@ -4714,9 +4629,8 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h) INIT_LIST_HEAD(&c->list); c->busaddr = (u32) cmd_dma_handle; temp64.val = (u64) err_dma_handle; - c->ErrDesc.Addr.lower = temp64.val32.lower; - c->ErrDesc.Addr.upper = temp64.val32.upper; - c->ErrDesc.Len = sizeof(*c->err_info); + c->ErrDesc.Addr = cpu_to_le64(err_dma_handle); + c->ErrDesc.Len = cpu_to_le32(sizeof(*c->err_info)); c->h = h; return c; @@ -4729,7 +4643,6 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h) static struct CommandList *cmd_special_alloc(struct ctlr_info *h) { struct CommandList *c; - union u64bit temp64; dma_addr_t cmd_dma_handle, err_dma_handle; c = pci_zalloc_consistent(h->pdev, sizeof(*c), &cmd_dma_handle); @@ -4750,10 +4663,8 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h) INIT_LIST_HEAD(&c->list); c->busaddr = (u32) cmd_dma_handle; - temp64.val = (u64) err_dma_handle; - c->ErrDesc.Addr.lower = temp64.val32.lower; - c->ErrDesc.Addr.upper = temp64.val32.upper; - c->ErrDesc.Len = sizeof(*c->err_info); + c->ErrDesc.Addr = cpu_to_le64(err_dma_handle); + c->ErrDesc.Len = cpu_to_le32(sizeof(*c->err_info)); c->h = h; return c; @@ -4762,30 +4673,25 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h) static void cmd_free(struct ctlr_info *h, struct CommandList *c) { int i; - unsigned long flags; i = c - h->cmd_pool; - spin_lock_irqsave(&h->lock, flags); clear_bit(i & (BITS_PER_LONG - 1), h->cmd_pool_bits + (i / BITS_PER_LONG)); - spin_unlock_irqrestore(&h->lock, flags); } static void cmd_special_free(struct ctlr_info *h, struct CommandList *c) { - union u64bit temp64; - - temp64.val32.lower = c->ErrDesc.Addr.lower; - temp64.val32.upper = c->ErrDesc.Addr.upper; pci_free_consistent(h->pdev, sizeof(*c->err_info), - c->err_info, (dma_addr_t) temp64.val); + c->err_info, + (dma_addr_t) le64_to_cpu(c->ErrDesc.Addr)); pci_free_consistent(h->pdev, sizeof(*c), c, (dma_addr_t) (c->busaddr & DIRECT_LOOKUP_MASK)); } #ifdef CONFIG_COMPAT -static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg) +static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, + void __user *arg) { IOCTL32_Command_struct __user *arg32 = (IOCTL32_Command_struct __user *) arg; @@ -4810,7 +4716,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg) if (err) return -EFAULT; - err = hpsa_ioctl(dev, CCISS_PASSTHRU, (void *)p); + err = hpsa_ioctl(dev, CCISS_PASSTHRU, p); if (err) return err; err |= copy_in_user(&arg32->error_info, &p->error_info, @@ -4821,7 +4727,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg) } static int hpsa_ioctl32_big_passthru(struct scsi_device *dev, - int cmd, void *arg) + int cmd, void __user *arg) { BIG_IOCTL32_Command_struct __user *arg32 = (BIG_IOCTL32_Command_struct __user *) arg; @@ -4848,7 +4754,7 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev, if (err) return -EFAULT; - err = hpsa_ioctl(dev, CCISS_BIG_PASSTHRU, (void *)p); + err = hpsa_ioctl(dev, CCISS_BIG_PASSTHRU, p); if (err) return err; err |= copy_in_user(&arg32->error_info, &p->error_info, @@ -4858,7 +4764,7 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev, return err; } -static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg) +static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void __user *arg) { switch (cmd) { case CCISS_GETPCIINFO: @@ -4932,7 +4838,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) IOCTL_Command_struct iocommand; struct CommandList *c; char *buff = NULL; - union u64bit temp64; + u64 temp64; int rc = 0; if (!argp) @@ -4971,14 +4877,14 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) c->Header.ReplyQueue = 0; /* unused in simple mode */ if (iocommand.buf_size > 0) { /* buffer to fill */ c->Header.SGList = 1; - c->Header.SGTotal = 1; + c->Header.SGTotal = cpu_to_le16(1); } else { /* no buffers to fill */ c->Header.SGList = 0; - c->Header.SGTotal = 0; + c->Header.SGTotal = cpu_to_le16(0); } memcpy(&c->Header.LUN, &iocommand.LUN_info, sizeof(c->Header.LUN)); /* use the kernel address the cmd block for tag */ - c->Header.Tag.lower = c->busaddr; + c->Header.tag = c->busaddr; /* Fill in Request block */ memcpy(&c->Request, &iocommand.Request, @@ -4986,19 +4892,17 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) /* Fill in the scatter gather information */ if (iocommand.buf_size > 0) { - temp64.val = pci_map_single(h->pdev, buff, + temp64 = pci_map_single(h->pdev, buff, iocommand.buf_size, PCI_DMA_BIDIRECTIONAL); - if (dma_mapping_error(&h->pdev->dev, temp64.val)) { - c->SG[0].Addr.lower = 0; - c->SG[0].Addr.upper = 0; - c->SG[0].Len = 0; + if (dma_mapping_error(&h->pdev->dev, (dma_addr_t) temp64)) { + c->SG[0].Addr = cpu_to_le64(0); + c->SG[0].Len = cpu_to_le32(0); rc = -ENOMEM; goto out; } - c->SG[0].Addr.lower = temp64.val32.lower; - c->SG[0].Addr.upper = temp64.val32.upper; - c->SG[0].Len = iocommand.buf_size; - c->SG[0].Ext = HPSA_SG_LAST; /* we are not chaining*/ + c->SG[0].Addr = cpu_to_le64(temp64); + c->SG[0].Len = cpu_to_le32(iocommand.buf_size); + c->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* not chaining */ } hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); if (iocommand.buf_size > 0) @@ -5033,7 +4937,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) struct CommandList *c; unsigned char **buff = NULL; int *buff_size = NULL; - union u64bit temp64; + u64 temp64; BYTE sg_used = 0; int status = 0; int i; @@ -5107,29 +5011,30 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) } c->cmd_type = CMD_IOCTL_PEND; c->Header.ReplyQueue = 0; - c->Header.SGList = c->Header.SGTotal = sg_used; + c->Header.SGList = (u8) sg_used; + c->Header.SGTotal = cpu_to_le16(sg_used); memcpy(&c->Header.LUN, &ioc->LUN_info, sizeof(c->Header.LUN)); - c->Header.Tag.lower = c->busaddr; + c->Header.tag = c->busaddr; memcpy(&c->Request, &ioc->Request, sizeof(c->Request)); if (ioc->buf_size > 0) { int i; for (i = 0; i < sg_used; i++) { - temp64.val = pci_map_single(h->pdev, buff[i], + temp64 = pci_map_single(h->pdev, buff[i], buff_size[i], PCI_DMA_BIDIRECTIONAL); - if (dma_mapping_error(&h->pdev->dev, temp64.val)) { - c->SG[i].Addr.lower = 0; - c->SG[i].Addr.upper = 0; - c->SG[i].Len = 0; + if (dma_mapping_error(&h->pdev->dev, + (dma_addr_t) temp64)) { + c->SG[i].Addr = cpu_to_le64(0); + c->SG[i].Len = cpu_to_le32(0); hpsa_pci_unmap(h->pdev, c, i, PCI_DMA_BIDIRECTIONAL); status = -ENOMEM; goto cleanup0; } - c->SG[i].Addr.lower = temp64.val32.lower; - c->SG[i].Addr.upper = temp64.val32.upper; - c->SG[i].Len = buff_size[i]; - c->SG[i].Ext = i < sg_used - 1 ? 0 : HPSA_SG_LAST; + c->SG[i].Addr = cpu_to_le64(temp64); + c->SG[i].Len = cpu_to_le32(buff_size[i]); + c->SG[i].Ext = cpu_to_le32(0); } + c->SG[--i].Ext = cpu_to_le32(HPSA_SG_LAST); } hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); if (sg_used) @@ -5206,7 +5111,7 @@ static void decrement_passthru_count(struct ctlr_info *h) /* * ioctl */ -static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg) +static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg) { struct ctlr_info *h; void __user *argp = (void __user *)arg; @@ -5268,20 +5173,20 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, { int pci_dir = XFER_NONE; struct CommandList *a; /* for commands to be aborted */ + u32 tupper, tlower; c->cmd_type = CMD_IOCTL_PEND; c->Header.ReplyQueue = 0; if (buff != NULL && size > 0) { c->Header.SGList = 1; - c->Header.SGTotal = 1; + c->Header.SGTotal = cpu_to_le16(1); } else { c->Header.SGList = 0; - c->Header.SGTotal = 0; + c->Header.SGTotal = cpu_to_le16(0); } - c->Header.Tag.lower = c->busaddr; + c->Header.tag = c->busaddr; memcpy(c->Header.LUN.LunAddrBytes, scsi3addr, 8); - c->Request.Type.Type = cmd_type; if (cmd_type == TYPE_CMD) { switch (cmd) { case HPSA_INQUIRY: @@ -5291,8 +5196,8 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.CDB[2] = (page_code & 0xff); } c->Request.CDBLen = 6; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_READ; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); c->Request.Timeout = 0; c->Request.CDB[0] = HPSA_INQUIRY; c->Request.CDB[4] = size & 0xFF; @@ -5303,8 +5208,8 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, mode = 00 target = 0. Nothing to write. */ c->Request.CDBLen = 12; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_READ; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); c->Request.Timeout = 0; c->Request.CDB[0] = cmd; c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */ @@ -5314,8 +5219,9 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, break; case HPSA_CACHE_FLUSH: c->Request.CDBLen = 12; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_WRITE; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, + ATTR_SIMPLE, XFER_WRITE); c->Request.Timeout = 0; c->Request.CDB[0] = BMIC_WRITE; c->Request.CDB[6] = BMIC_CACHE_FLUSH; @@ -5324,14 +5230,14 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, break; case TEST_UNIT_READY: c->Request.CDBLen = 6; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_NONE; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE); c->Request.Timeout = 0; break; case HPSA_GET_RAID_MAP: c->Request.CDBLen = 12; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_READ; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); c->Request.Timeout = 0; c->Request.CDB[0] = HPSA_CISS_READ; c->Request.CDB[1] = cmd; @@ -5342,8 +5248,8 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, break; case BMIC_SENSE_CONTROLLER_PARAMETERS: c->Request.CDBLen = 10; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_READ; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); c->Request.Timeout = 0; c->Request.CDB[0] = BMIC_READ; c->Request.CDB[6] = BMIC_SENSE_CONTROLLER_PARAMETERS; @@ -5360,9 +5266,8 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, case HPSA_DEVICE_RESET_MSG: c->Request.CDBLen = 16; - c->Request.Type.Type = 1; /* It is a MSG not a CMD */ - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_NONE; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE); c->Request.Timeout = 0; /* Don't time out */ memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB)); c->Request.CDB[0] = cmd; @@ -5376,27 +5281,28 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, break; case HPSA_ABORT_MSG: a = buff; /* point to command to be aborted */ - dev_dbg(&h->pdev->dev, "Abort Tag:0x%08x:%08x using request Tag:0x%08x:%08x\n", - a->Header.Tag.upper, a->Header.Tag.lower, - c->Header.Tag.upper, c->Header.Tag.lower); + dev_dbg(&h->pdev->dev, "Abort Tag:0x%016llx using request Tag:0x%016llx", + a->Header.tag, c->Header.tag); + tlower = (u32) (a->Header.tag >> 32); + tupper = (u32) (a->Header.tag & 0x0ffffffffULL); c->Request.CDBLen = 16; - c->Request.Type.Type = TYPE_MSG; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_WRITE; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, + ATTR_SIMPLE, XFER_WRITE); c->Request.Timeout = 0; /* Don't time out */ c->Request.CDB[0] = HPSA_TASK_MANAGEMENT; c->Request.CDB[1] = HPSA_TMF_ABORT_TASK; c->Request.CDB[2] = 0x00; /* reserved */ c->Request.CDB[3] = 0x00; /* reserved */ /* Tag to abort goes in CDB[4]-CDB[11] */ - c->Request.CDB[4] = a->Header.Tag.lower & 0xFF; - c->Request.CDB[5] = (a->Header.Tag.lower >> 8) & 0xFF; - c->Request.CDB[6] = (a->Header.Tag.lower >> 16) & 0xFF; - c->Request.CDB[7] = (a->Header.Tag.lower >> 24) & 0xFF; - c->Request.CDB[8] = a->Header.Tag.upper & 0xFF; - c->Request.CDB[9] = (a->Header.Tag.upper >> 8) & 0xFF; - c->Request.CDB[10] = (a->Header.Tag.upper >> 16) & 0xFF; - c->Request.CDB[11] = (a->Header.Tag.upper >> 24) & 0xFF; + c->Request.CDB[4] = tlower & 0xFF; + c->Request.CDB[5] = (tlower >> 8) & 0xFF; + c->Request.CDB[6] = (tlower >> 16) & 0xFF; + c->Request.CDB[7] = (tlower >> 24) & 0xFF; + c->Request.CDB[8] = tupper & 0xFF; + c->Request.CDB[9] = (tupper >> 8) & 0xFF; + c->Request.CDB[10] = (tupper >> 16) & 0xFF; + c->Request.CDB[11] = (tupper >> 24) & 0xFF; c->Request.CDB[12] = 0x00; /* reserved */ c->Request.CDB[13] = 0x00; /* reserved */ c->Request.CDB[14] = 0x00; /* reserved */ @@ -5412,7 +5318,7 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, BUG(); } - switch (c->Request.Type.Direction) { + switch (GET_DIR(c->Request.type_attr_dir)) { case XFER_READ: pci_dir = PCI_DMA_FROMDEVICE; break; @@ -5467,15 +5373,9 @@ static void start_io(struct ctlr_info *h, unsigned long *flags) /* Put job onto the completed Q */ addQ(&h->cmpQ, c); - - /* Must increment commands_outstanding before unlocking - * and submitting to avoid race checking for fifo full - * condition. - */ - h->commands_outstanding++; - - /* Tell the controller execute command */ + atomic_inc(&h->commands_outstanding); spin_unlock_irqrestore(&h->lock, *flags); + /* Tell the controller execute command */ h->access.submit_command(h, c); spin_lock_irqsave(&h->lock, *flags); } @@ -5521,6 +5421,7 @@ static inline void finish_cmd(struct CommandList *c) unsigned long flags; int io_may_be_stalled = 0; struct ctlr_info *h = c->h; + int count; spin_lock_irqsave(&h->lock, flags); removeQ(c); @@ -5541,11 +5442,10 @@ static inline void finish_cmd(struct CommandList *c) * want to get in a cycle where we call start_io every time * through here. */ - if (unlikely(h->fifo_recently_full) && - h->commands_outstanding < 5) - io_may_be_stalled = 1; - + count = atomic_read(&h->commands_outstanding); spin_unlock_irqrestore(&h->lock, flags); + if (unlikely(h->fifo_recently_full) && count < 5) + io_may_be_stalled = 1; dial_up_lockup_detection_on_fw_flash_complete(c->h, c); if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI @@ -5765,22 +5665,20 @@ static int hpsa_message(struct pci_dev *pdev, unsigned char opcode, cmd->CommandHeader.ReplyQueue = 0; cmd->CommandHeader.SGList = 0; - cmd->CommandHeader.SGTotal = 0; - cmd->CommandHeader.Tag.lower = paddr32; - cmd->CommandHeader.Tag.upper = 0; + cmd->CommandHeader.SGTotal = cpu_to_le16(0); + cmd->CommandHeader.tag = paddr32; memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8); cmd->Request.CDBLen = 16; - cmd->Request.Type.Type = TYPE_MSG; - cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE; - cmd->Request.Type.Direction = XFER_NONE; + cmd->Request.type_attr_dir = + TYPE_ATTR_DIR(TYPE_MSG, ATTR_HEADOFQUEUE, XFER_NONE); cmd->Request.Timeout = 0; /* Don't time out */ cmd->Request.CDB[0] = opcode; cmd->Request.CDB[1] = type; memset(&cmd->Request.CDB[2], 0, 14); /* rest of the CDB is reserved */ - cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(*cmd); - cmd->ErrorDescriptor.Addr.upper = 0; - cmd->ErrorDescriptor.Len = sizeof(struct ErrorInfo); + cmd->ErrorDescriptor.Addr = + cpu_to_le64((paddr32 + sizeof(*cmd))); + cmd->ErrorDescriptor.Len = cpu_to_le32(sizeof(struct ErrorInfo)); writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET); @@ -5818,7 +5716,7 @@ static int hpsa_message(struct pci_dev *pdev, unsigned char opcode, #define hpsa_noop(p) hpsa_message(p, 3, 0) static int hpsa_controller_hard_reset(struct pci_dev *pdev, - void * __iomem vaddr, u32 use_doorbell) + void __iomem *vaddr, u32 use_doorbell) { u16 pmcsr; int pos; @@ -6056,7 +5954,7 @@ unmap_vaddr: * the io functions. * This is for debug only. */ -static void print_cfg_table(struct device *dev, struct CfgTable *tb) +static void print_cfg_table(struct device *dev, struct CfgTable __iomem *tb) { #ifdef HPSA_DEBUG int i; @@ -6323,11 +6221,11 @@ static void hpsa_find_board_params(struct ctlr_info *h) h->max_cmd_sg_entries = 31; if (h->maxsgentries > 512) { h->max_cmd_sg_entries = 32; - h->chainsize = h->maxsgentries - h->max_cmd_sg_entries + 1; + h->chainsize = h->maxsgentries - h->max_cmd_sg_entries; h->maxsgentries--; /* save one for chain pointer */ } else { - h->maxsgentries = 31; /* default to traditional values */ h->chainsize = 0; + h->maxsgentries = 31; /* default to traditional values */ } /* Find out what task management functions are supported and cache */ @@ -6456,15 +6354,15 @@ static int hpsa_pci_init(struct ctlr_info *h) return err; } - /* Enable bus mastering (pci_disable_device may disable this) */ - pci_set_master(h->pdev); - err = pci_request_regions(h->pdev, HPSA); if (err) { dev_err(&h->pdev->dev, "cannot obtain PCI resources, aborting\n"); return err; } + + pci_set_master(h->pdev); + hpsa_interrupt_mode(h); err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr); if (err) @@ -6544,7 +6442,9 @@ static int hpsa_init_reset_devices(struct pci_dev *pdev) dev_warn(&pdev->dev, "failed to enable device.\n"); return -ENODEV; } + pci_set_master(pdev); + /* Reset the controller with a PCI power-cycle or via doorbell */ rc = hpsa_kdump_hard_reset_controller(pdev); @@ -7431,13 +7331,12 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support) cp->host_context_flags = IOACCEL1_HCFLAGS_CISS_FORMAT; cp->timeout_sec = 0; cp->ReplyQueue = 0; - cp->Tag.lower = (i << DIRECT_LOOKUP_SHIFT) | - DIRECT_LOOKUP_BIT; - cp->Tag.upper = 0; - cp->host_addr.lower = - (u32) (h->ioaccel_cmd_pool_dhandle + + cp->tag = + cpu_to_le64((i << DIRECT_LOOKUP_SHIFT) | + DIRECT_LOOKUP_BIT); + cp->host_addr = + cpu_to_le64(h->ioaccel_cmd_pool_dhandle + (i * sizeof(struct io_accel1_cmd))); - cp->host_addr.upper = 0; } } else if (trans_support & CFGTBL_Trans_io_accel2) { u64 cfg_offset, cfg_base_addr_index; @@ -7711,7 +7610,7 @@ static void __attribute__((unused)) verify_offsets(void) VERIFY_OFFSET(timeout_sec, 0x62); VERIFY_OFFSET(ReplyQueue, 0x64); VERIFY_OFFSET(reserved9, 0x65); - VERIFY_OFFSET(Tag, 0x68); + VERIFY_OFFSET(tag, 0x68); VERIFY_OFFSET(host_addr, 0x70); VERIFY_OFFSET(CISS_LUN, 0x78); VERIFY_OFFSET(SG, 0x78 + 8); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 24472cec7de3..8e06d9e280ec 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -118,7 +118,7 @@ struct ctlr_info { struct CfgTable __iomem *cfgtable; int interrupts_enabled; int max_commands; - int commands_outstanding; + atomic_t commands_outstanding; # define PERF_MODE_INT 0 # define DOORBELL_INT 1 # define SIMPLE_MODE_INT 2 @@ -164,7 +164,7 @@ struct ctlr_info { */ u32 trans_support; u32 trans_offset; - struct TransTable_struct *transtable; + struct TransTable_struct __iomem *transtable; unsigned long transMethod; /* cap concurrent passthrus at some reasonable maximum */ @@ -181,7 +181,7 @@ struct ctlr_info { u32 *blockFetchTable; u32 *ioaccel1_blockFetchTable; u32 *ioaccel2_blockFetchTable; - u32 *ioaccel2_bft2_regs; + u32 __iomem *ioaccel2_bft2_regs; unsigned char *hba_inquiry_data; u32 driver_support; u32 fw_support; @@ -192,7 +192,7 @@ struct ctlr_info { u64 last_heartbeat_timestamp; u32 heartbeat_sample_interval; atomic_t firmware_flash_in_progress; - u32 *lockup_detected; + u32 __percpu *lockup_detected; struct delayed_work monitor_ctlr_work; int remove_in_progress; u32 fifo_recently_full; @@ -395,7 +395,7 @@ static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val) static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q) { struct reply_queue_buffer *rq = &h->reply_queue[q]; - unsigned long flags, register_value = FIFO_EMPTY; + unsigned long register_value = FIFO_EMPTY; /* msi auto clears the interrupt pending bit. */ if (!(h->msi_vector || h->msix_vector)) { @@ -413,9 +413,7 @@ static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q) if ((rq->head[rq->current_entry] & 1) == rq->wraparound) { register_value = rq->head[rq->current_entry]; rq->current_entry++; - spin_lock_irqsave(&h->lock, flags); - h->commands_outstanding--; - spin_unlock_irqrestore(&h->lock, flags); + atomic_dec(&h->commands_outstanding); } else { register_value = FIFO_EMPTY; } @@ -433,11 +431,7 @@ static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q) */ static unsigned long SA5_fifo_full(struct ctlr_info *h) { - if (h->commands_outstanding >= h->max_commands) - return 1; - else - return 0; - + return atomic_read(&h->commands_outstanding) >= h->max_commands; } /* * returns value read from hardware. @@ -448,13 +442,9 @@ static unsigned long SA5_completed(struct ctlr_info *h, { unsigned long register_value = readl(h->vaddr + SA5_REPLY_PORT_OFFSET); - unsigned long flags; - if (register_value != FIFO_EMPTY) { - spin_lock_irqsave(&h->lock, flags); - h->commands_outstanding--; - spin_unlock_irqrestore(&h->lock, flags); - } + if (register_value != FIFO_EMPTY) + atomic_dec(&h->commands_outstanding); #ifdef HPSA_DEBUG if (register_value != FIFO_EMPTY) @@ -510,7 +500,6 @@ static unsigned long SA5_ioaccel_mode1_completed(struct ctlr_info *h, u8 q) { u64 register_value; struct reply_queue_buffer *rq = &h->reply_queue[q]; - unsigned long flags; BUG_ON(q >= h->nreply_queues); @@ -528,9 +517,7 @@ static unsigned long SA5_ioaccel_mode1_completed(struct ctlr_info *h, u8 q) wmb(); writel((q << 24) | rq->current_entry, h->vaddr + IOACCEL_MODE1_CONSUMER_INDEX); - spin_lock_irqsave(&h->lock, flags); - h->commands_outstanding--; - spin_unlock_irqrestore(&h->lock, flags); + atomic_dec(&h->commands_outstanding); } return (unsigned long) register_value; } diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index b5125dc31439..cb988c41cad9 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -252,7 +252,7 @@ struct ReportExtendedLUNdata { u8 LUNListLength[4]; u8 extended_response_flag; u8 reserved[3]; - struct ext_report_lun_entry LUN[HPSA_MAX_LUN]; + struct ext_report_lun_entry LUN[HPSA_MAX_PHYS_LUN]; }; struct SenseSubsystem_info { @@ -314,28 +314,36 @@ struct CommandListHeader { u8 ReplyQueue; u8 SGList; u16 SGTotal; - struct vals32 Tag; + u64 tag; union LUNAddr LUN; }; struct RequestBlock { u8 CDBLen; - struct { - u8 Type:3; - u8 Attribute:3; - u8 Direction:2; - } Type; + /* + * type_attr_dir: + * type: low 3 bits + * attr: middle 3 bits + * dir: high 2 bits + */ + u8 type_attr_dir; +#define TYPE_ATTR_DIR(t, a, d) ((((d) & 0x03) << 6) |\ + (((a) & 0x07) << 3) |\ + ((t) & 0x07)) +#define GET_TYPE(tad) ((tad) & 0x07) +#define GET_ATTR(tad) (((tad) >> 3) & 0x07) +#define GET_DIR(tad) (((tad) >> 6) & 0x03) u16 Timeout; u8 CDB[16]; }; struct ErrDescriptor { - struct vals32 Addr; + u64 Addr; u32 Len; }; struct SGDescriptor { - struct vals32 Addr; + u64 Addr; u32 Len; u32 Ext; }; @@ -434,8 +442,8 @@ struct io_accel1_cmd { u16 timeout_sec; /* 0x62 - 0x63 */ u8 ReplyQueue; /* 0x64 */ u8 reserved9[3]; /* 0x65 - 0x67 */ - struct vals32 Tag; /* 0x68 - 0x6F */ - struct vals32 host_addr; /* 0x70 - 0x77 */ + u64 tag; /* 0x68 - 0x6F */ + u64 host_addr; /* 0x70 - 0x77 */ u8 CISS_LUN[8]; /* 0x78 - 0x7F */ struct SGDescriptor SG[IOACCEL1_MAXSGENTRIES]; } __aligned(IOACCEL1_COMMANDLIST_ALIGNMENT); @@ -555,8 +563,8 @@ struct hpsa_tmf_struct { u8 reserved1; /* byte 3 Reserved */ u32 it_nexus; /* SCSI I-T Nexus */ u8 lun_id[8]; /* LUN ID for TMF request */ - struct vals32 Tag; /* cciss tag associated w/ request */ - struct vals32 abort_tag;/* cciss tag of SCSI cmd or task to abort */ + u64 tag; /* cciss tag associated w/ request */ + u64 abort_tag; /* cciss tag of SCSI cmd or task to abort */ u64 error_ptr; /* Error Pointer */ u32 error_len; /* Error Length */ }; diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index dedb62c21b29..e995218476ed 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -1118,17 +1118,13 @@ static int hptiop_reset(struct scsi_cmnd *scp) } static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev, - int queue_depth, int reason) + int queue_depth) { struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata; - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (queue_depth > hba->max_requests) queue_depth = hba->max_requests; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); - return queue_depth; + return scsi_change_queue_depth(sdev, queue_depth); } static ssize_t hptiop_show_version(struct device *dev, diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 598c42cba5a8..f58c6d8e0264 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -1643,19 +1643,9 @@ static int ibmvfc_queuecommand_lck(struct scsi_cmnd *cmnd, int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun); memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len); - if (scsi_populate_tag_msg(cmnd, tag)) { - vfc_cmd->task_tag = cpu_to_be64(tag[1]); - switch (tag[0]) { - case MSG_SIMPLE_TAG: - vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK; - break; - case MSG_HEAD_TAG: - vfc_cmd->iu.pri_task_attr = IBMVFC_HEAD_OF_QUEUE; - break; - case MSG_ORDERED_TAG: - vfc_cmd->iu.pri_task_attr = IBMVFC_ORDERED_TASK; - break; - }; + if (cmnd->flags & SCMD_TAGGED) { + vfc_cmd->task_tag = cpu_to_be64(cmnd->tag); + vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK; } if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev)))) @@ -2897,12 +2887,6 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev) spin_lock_irqsave(shost->host_lock, flags); if (sdev->type == TYPE_DISK) sdev->allow_restart = 1; - - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); - scsi_activate_tcq(sdev, sdev->queue_depth); - } else - scsi_deactivate_tcq(sdev, sdev->queue_depth); spin_unlock_irqrestore(shost->host_lock, flags); return 0; } @@ -2916,40 +2900,12 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev) * Return value: * actual depth set **/ -static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason) +static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth) { - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (qdepth > IBMVFC_MAX_CMDS_PER_LUN) qdepth = IBMVFC_MAX_CMDS_PER_LUN; - scsi_adjust_queue_depth(sdev, 0, qdepth); - return sdev->queue_depth; -} - -/** - * ibmvfc_change_queue_type - Change the device's queue type - * @sdev: scsi device struct - * @tag_type: type of tags to use - * - * Return value: - * actual queue type set - **/ -static int ibmvfc_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; + return scsi_change_queue_depth(sdev, qdepth); } static ssize_t ibmvfc_show_host_partition_name(struct device *dev, @@ -3133,7 +3089,7 @@ static struct scsi_host_template driver_template = { .target_alloc = ibmvfc_target_alloc, .scan_finished = ibmvfc_scan_finished, .change_queue_depth = ibmvfc_change_queue_depth, - .change_queue_type = ibmvfc_change_queue_type, + .change_queue_type = scsi_change_queue_type, .cmd_per_lun = 16, .can_queue = IBMVFC_MAX_REQUESTS_DEFAULT, .this_id = -1, @@ -3141,6 +3097,8 @@ static struct scsi_host_template driver_template = { .max_sectors = IBMVFC_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = ibmvfc_attrs, + .use_blk_tags = 1, + .track_queue_depth = 1, }; /** diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 7b23f21f22f1..acea5d6eebd0 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -1929,7 +1929,6 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev) blk_queue_rq_timeout(sdev->request_queue, 120 * HZ); } spin_unlock_irqrestore(shost->host_lock, lock_flags); - scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun); return 0; } @@ -1942,17 +1941,11 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev) * Return value: * actual depth set **/ -static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason) +static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth) { - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN) qdepth = IBMVSCSI_MAX_CMDS_PER_LUN; - - scsi_adjust_queue_depth(sdev, 0, qdepth); - return sdev->queue_depth; + return scsi_change_queue_depth(sdev, qdepth); } /* ------------------------------------------------------------ diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 2a9578c116b7..540294389355 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3942,8 +3942,9 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg, return -EIO; } - sglist->num_dma_sg = pci_map_sg(ioa_cfg->pdev, sglist->scatterlist, - sglist->num_sg, DMA_TO_DEVICE); + sglist->num_dma_sg = dma_map_sg(&ioa_cfg->pdev->dev, + sglist->scatterlist, sglist->num_sg, + DMA_TO_DEVICE); if (!sglist->num_dma_sg) { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); @@ -4327,16 +4328,12 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; }; * Return value: * actual depth set **/ -static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason) +static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; struct ipr_resource_entry *res; unsigned long lock_flags = 0; - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *)sdev->hostdata; @@ -4344,7 +4341,7 @@ static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth, qdepth = IPR_MAX_CMD_PER_ATA_LUN; spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + scsi_change_queue_depth(sdev, qdepth); return sdev->queue_depth; } @@ -4364,24 +4361,10 @@ static int ipr_change_queue_type(struct scsi_device *sdev, int tag_type) spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *)sdev->hostdata; - - if (res) { - if (ipr_is_gscsi(res) && sdev->tagged_supported) { - /* - * We don't bother quiescing the device here since the - * adapter firmware does it for us. - */ - scsi_set_tag_type(sdev, tag_type); - - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - } else + if (res && ipr_is_gscsi(res)) + tag_type = scsi_change_queue_type(sdev, tag_type); + else tag_type = 0; - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return tag_type; } @@ -4765,10 +4748,10 @@ static int ipr_slave_configure(struct scsi_device *sdev) spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); if (ap) { - scsi_adjust_queue_depth(sdev, 0, IPR_MAX_CMD_PER_ATA_LUN); + scsi_change_queue_depth(sdev, IPR_MAX_CMD_PER_ATA_LUN); ata_sas_slave_configure(sdev, ap); - } else - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + } + if (ioa_cfg->sis64) sdev_printk(KERN_INFO, sdev, "Resource path: %s\n", ipr_format_res_path(ioa_cfg, @@ -5585,7 +5568,7 @@ static int ipr_build_ioadl64(struct ipr_ioa_cfg *ioa_cfg, nseg = scsi_dma_map(scsi_cmd); if (nseg < 0) { if (printk_ratelimit()) - dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n"); + dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n"); return -1; } @@ -5636,7 +5619,7 @@ static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg, nseg = scsi_dma_map(scsi_cmd); if (nseg < 0) { - dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n"); + dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n"); return -1; } @@ -5673,35 +5656,6 @@ static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg, } /** - * ipr_get_task_attributes - Translate SPI Q-Tag to task attributes - * @scsi_cmd: scsi command struct - * - * Return value: - * task attributes - **/ -static u8 ipr_get_task_attributes(struct scsi_cmnd *scsi_cmd) -{ - u8 tag[2]; - u8 rc = IPR_FLAGS_LO_UNTAGGED_TASK; - - if (scsi_populate_tag_msg(scsi_cmd, tag)) { - switch (tag[0]) { - case MSG_SIMPLE_TAG: - rc = IPR_FLAGS_LO_SIMPLE_TASK; - break; - case MSG_HEAD_TAG: - rc = IPR_FLAGS_LO_HEAD_OF_Q_TASK; - break; - case MSG_ORDERED_TAG: - rc = IPR_FLAGS_LO_ORDERED_TASK; - break; - }; - } - - return rc; -} - -/** * ipr_erp_done - Process completion of ERP for a device * @ipr_cmd: ipr command struct * @@ -6236,7 +6190,10 @@ static int ipr_queuecommand(struct Scsi_Host *shost, ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST; } ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR; - ioarcb->cmd_pkt.flags_lo |= ipr_get_task_attributes(scsi_cmd); + if (scsi_cmd->flags & SCMD_TAGGED) + ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_SIMPLE_TASK; + else + ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_UNTAGGED_TASK; } if (scsi_cmd->cmnd[0] >= 0xC0 && @@ -6357,6 +6314,7 @@ static struct scsi_host_template driver_template = { .sdev_attrs = ipr_dev_attrs, .proc_name = IPR_NAME, .no_write_same = 1, + .use_blk_tags = 1, }; /** @@ -8431,7 +8389,7 @@ static int ipr_reset_ucode_download_done(struct ipr_cmnd *ipr_cmd) struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ipr_sglist *sglist = ioa_cfg->ucode_sglist; - pci_unmap_sg(ioa_cfg->pdev, sglist->scatterlist, + dma_unmap_sg(&ioa_cfg->pdev->dev, sglist->scatterlist, sglist->num_sg, DMA_TO_DEVICE); ipr_cmd->job_step = ipr_reset_alert; @@ -8871,7 +8829,7 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) for (i = 0; i < IPR_NUM_CMD_BLKS; i++) { if (ioa_cfg->ipr_cmnd_list[i]) - pci_pool_free(ioa_cfg->ipr_cmd_pool, + dma_pool_free(ioa_cfg->ipr_cmd_pool, ioa_cfg->ipr_cmnd_list[i], ioa_cfg->ipr_cmnd_list_dma[i]); @@ -8879,7 +8837,7 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) } if (ioa_cfg->ipr_cmd_pool) - pci_pool_destroy(ioa_cfg->ipr_cmd_pool); + dma_pool_destroy(ioa_cfg->ipr_cmd_pool); kfree(ioa_cfg->ipr_cmnd_list); kfree(ioa_cfg->ipr_cmnd_list_dma); @@ -8900,25 +8858,24 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg) int i; kfree(ioa_cfg->res_entries); - pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_misc_cbs), - ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); + dma_free_coherent(&ioa_cfg->pdev->dev, sizeof(struct ipr_misc_cbs), + ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); ipr_free_cmd_blks(ioa_cfg); for (i = 0; i < ioa_cfg->hrrq_num; i++) - pci_free_consistent(ioa_cfg->pdev, - sizeof(u32) * ioa_cfg->hrrq[i].size, - ioa_cfg->hrrq[i].host_rrq, - ioa_cfg->hrrq[i].host_rrq_dma); + dma_free_coherent(&ioa_cfg->pdev->dev, + sizeof(u32) * ioa_cfg->hrrq[i].size, + ioa_cfg->hrrq[i].host_rrq, + ioa_cfg->hrrq[i].host_rrq_dma); - pci_free_consistent(ioa_cfg->pdev, ioa_cfg->cfg_table_size, - ioa_cfg->u.cfg_table, - ioa_cfg->cfg_table_dma); + dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size, + ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma); for (i = 0; i < IPR_NUM_HCAMS; i++) { - pci_free_consistent(ioa_cfg->pdev, - sizeof(struct ipr_hostrcb), - ioa_cfg->hostrcb[i], - ioa_cfg->hostrcb_dma[i]); + dma_free_coherent(&ioa_cfg->pdev->dev, + sizeof(struct ipr_hostrcb), + ioa_cfg->hostrcb[i], + ioa_cfg->hostrcb_dma[i]); } ipr_free_dump(ioa_cfg); @@ -8979,7 +8936,7 @@ static int ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) dma_addr_t dma_addr; int i, entries_each_hrrq, hrrq_id = 0; - ioa_cfg->ipr_cmd_pool = pci_pool_create(IPR_NAME, ioa_cfg->pdev, + ioa_cfg->ipr_cmd_pool = dma_pool_create(IPR_NAME, &ioa_cfg->pdev->dev, sizeof(struct ipr_cmnd), 512, 0); if (!ioa_cfg->ipr_cmd_pool) @@ -9029,7 +8986,7 @@ static int ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) } for (i = 0; i < IPR_NUM_CMD_BLKS; i++) { - ipr_cmd = pci_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr); + ipr_cmd = dma_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr); if (!ipr_cmd) { ipr_free_cmd_blks(ioa_cfg); @@ -9100,9 +9057,10 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) ioa_cfg->res_entries[i].ioa_cfg = ioa_cfg; } - ioa_cfg->vpd_cbs = pci_alloc_consistent(ioa_cfg->pdev, - sizeof(struct ipr_misc_cbs), - &ioa_cfg->vpd_cbs_dma); + ioa_cfg->vpd_cbs = dma_alloc_coherent(&pdev->dev, + sizeof(struct ipr_misc_cbs), + &ioa_cfg->vpd_cbs_dma, + GFP_KERNEL); if (!ioa_cfg->vpd_cbs) goto out_free_res_entries; @@ -9111,13 +9069,14 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) goto out_free_vpd_cbs; for (i = 0; i < ioa_cfg->hrrq_num; i++) { - ioa_cfg->hrrq[i].host_rrq = pci_alloc_consistent(ioa_cfg->pdev, + ioa_cfg->hrrq[i].host_rrq = dma_alloc_coherent(&pdev->dev, sizeof(u32) * ioa_cfg->hrrq[i].size, - &ioa_cfg->hrrq[i].host_rrq_dma); + &ioa_cfg->hrrq[i].host_rrq_dma, + GFP_KERNEL); if (!ioa_cfg->hrrq[i].host_rrq) { while (--i > 0) - pci_free_consistent(pdev, + dma_free_coherent(&pdev->dev, sizeof(u32) * ioa_cfg->hrrq[i].size, ioa_cfg->hrrq[i].host_rrq, ioa_cfg->hrrq[i].host_rrq_dma); @@ -9126,17 +9085,19 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) ioa_cfg->hrrq[i].ioa_cfg = ioa_cfg; } - ioa_cfg->u.cfg_table = pci_alloc_consistent(ioa_cfg->pdev, - ioa_cfg->cfg_table_size, - &ioa_cfg->cfg_table_dma); + ioa_cfg->u.cfg_table = dma_alloc_coherent(&pdev->dev, + ioa_cfg->cfg_table_size, + &ioa_cfg->cfg_table_dma, + GFP_KERNEL); if (!ioa_cfg->u.cfg_table) goto out_free_host_rrq; for (i = 0; i < IPR_NUM_HCAMS; i++) { - ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev, - sizeof(struct ipr_hostrcb), - &ioa_cfg->hostrcb_dma[i]); + ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev, + sizeof(struct ipr_hostrcb), + &ioa_cfg->hostrcb_dma[i], + GFP_KERNEL); if (!ioa_cfg->hostrcb[i]) goto out_free_hostrcb_dma; @@ -9160,25 +9121,24 @@ out: out_free_hostrcb_dma: while (i-- > 0) { - pci_free_consistent(pdev, sizeof(struct ipr_hostrcb), - ioa_cfg->hostrcb[i], - ioa_cfg->hostrcb_dma[i]); + dma_free_coherent(&pdev->dev, sizeof(struct ipr_hostrcb), + ioa_cfg->hostrcb[i], + ioa_cfg->hostrcb_dma[i]); } - pci_free_consistent(pdev, ioa_cfg->cfg_table_size, - ioa_cfg->u.cfg_table, - ioa_cfg->cfg_table_dma); + dma_free_coherent(&pdev->dev, ioa_cfg->cfg_table_size, + ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma); out_free_host_rrq: for (i = 0; i < ioa_cfg->hrrq_num; i++) { - pci_free_consistent(pdev, - sizeof(u32) * ioa_cfg->hrrq[i].size, - ioa_cfg->hrrq[i].host_rrq, - ioa_cfg->hrrq[i].host_rrq_dma); + dma_free_coherent(&pdev->dev, + sizeof(u32) * ioa_cfg->hrrq[i].size, + ioa_cfg->hrrq[i].host_rrq, + ioa_cfg->hrrq[i].host_rrq_dma); } out_ipr_free_cmd_blocks: ipr_free_cmd_blks(ioa_cfg); out_free_vpd_cbs: - pci_free_consistent(pdev, sizeof(struct ipr_misc_cbs), - ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); + dma_free_coherent(&pdev->dev, sizeof(struct ipr_misc_cbs), + ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); out_free_res_entries: kfree(ioa_cfg->res_entries); goto out; @@ -9618,16 +9578,17 @@ static int ipr_probe_ioa(struct pci_dev *pdev, ipr_init_regs(ioa_cfg); if (ioa_cfg->sis64) { - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (rc < 0) { - dev_dbg(&pdev->dev, "Failed to set 64 bit PCI DMA mask\n"); - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + dev_dbg(&pdev->dev, "Failed to set 64 bit DMA mask\n"); + rc = dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(32)); } } else - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (rc < 0) { - dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); + dev_err(&pdev->dev, "Failed to set DMA mask\n"); goto cleanup_nomem; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index d0201ceb4aac..9ebdebd944e7 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1549,7 +1549,7 @@ struct ipr_ioa_cfg { struct ipr_misc_cbs *vpd_cbs; dma_addr_t vpd_cbs_dma; - struct pci_pool *ipr_cmd_pool; + struct dma_pool *ipr_cmd_pool; struct ipr_cmnd *reset_cmd; int (*reset) (struct ipr_cmnd *); diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index e5afc3884d74..e5c28435d768 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -1210,7 +1210,7 @@ ips_slave_configure(struct scsi_device * SDptr) min = ha->max_cmds / 2; if (ha->enq->ucLogDriveCount <= 2) min = ha->max_cmds - 1; - scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, min); + scsi_change_queue_depth(SDptr, min); } SDptr->skip_ms_page_8 = 1; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 2e890b1e2526..724c6265b667 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -172,6 +172,8 @@ static struct scsi_host_template isci_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = isci_host_attrs, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static struct sas_domain_function_template isci_transport_ops = { @@ -258,8 +260,6 @@ static int isci_register_sas_ha(struct isci_host *isci_host) sas_ha->sas_port = sas_ports; sas_ha->num_phys = SCI_MAX_PHYS; - sas_ha->lldd_queue_size = ISCI_CAN_QUEUE_VAL; - sas_ha->lldd_max_execute_num = 1; sas_ha->strict_wide_ports = 1; sas_register_ha(sas_ha); diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 5d6fda72d659..3f63c6318b0d 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -117,104 +117,97 @@ static inline int isci_device_io_ready(struct isci_remote_device *idev, * functions. This function is called by libsas to send a task down to * hardware. * @task: This parameter specifies the SAS task to send. - * @num: This parameter specifies the number of tasks to queue. * @gfp_flags: This parameter specifies the context of this call. * * status, zero indicates success. */ -int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) +int isci_task_execute_task(struct sas_task *task, gfp_t gfp_flags) { struct isci_host *ihost = dev_to_ihost(task->dev); struct isci_remote_device *idev; unsigned long flags; + enum sci_status status = SCI_FAILURE; bool io_ready; u16 tag; - dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num); + spin_lock_irqsave(&ihost->scic_lock, flags); + idev = isci_lookup_device(task->dev); + io_ready = isci_device_io_ready(idev, task); + tag = isci_alloc_tag(ihost); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - for_each_sas_task(num, task) { - enum sci_status status = SCI_FAILURE; + dev_dbg(&ihost->pdev->dev, + "task: %p, dev: %p idev: %p:%#lx cmd = %p\n", + task, task->dev, idev, idev ? idev->flags : 0, + task->uldd_task); - spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_lookup_device(task->dev); - io_ready = isci_device_io_ready(idev, task); - tag = isci_alloc_tag(ihost); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + if (!idev) { + isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN); + } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) { + /* Indicate QUEUE_FULL so that the scsi midlayer + * retries. + */ + isci_task_refuse(ihost, task, SAS_TASK_COMPLETE, + SAS_QUEUE_FULL); + } else { + /* There is a device and it's ready for I/O. */ + spin_lock_irqsave(&task->task_state_lock, flags); - dev_dbg(&ihost->pdev->dev, - "task: %p, num: %d dev: %p idev: %p:%#lx cmd = %p\n", - task, num, task->dev, idev, idev ? idev->flags : 0, - task->uldd_task); - - if (!idev) { - isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED, - SAS_DEVICE_UNKNOWN); - } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) { - /* Indicate QUEUE_FULL so that the scsi midlayer - * retries. - */ - isci_task_refuse(ihost, task, SAS_TASK_COMPLETE, - SAS_QUEUE_FULL); + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + /* The I/O was aborted. */ + spin_unlock_irqrestore(&task->task_state_lock, flags); + + isci_task_refuse(ihost, task, + SAS_TASK_UNDELIVERED, + SAM_STAT_TASK_ABORTED); } else { - /* There is a device and it's ready for I/O. */ - spin_lock_irqsave(&task->task_state_lock, flags); - - if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { - /* The I/O was aborted. */ - spin_unlock_irqrestore(&task->task_state_lock, - flags); - - isci_task_refuse(ihost, task, - SAS_TASK_UNDELIVERED, - SAM_STAT_TASK_ABORTED); - } else { - task->task_state_flags |= SAS_TASK_AT_INITIATOR; + task->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + /* build and send the request. */ + status = isci_request_execute(ihost, idev, task, tag); + + if (status != SCI_SUCCESS) { + spin_lock_irqsave(&task->task_state_lock, flags); + /* Did not really start this command. */ + task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; spin_unlock_irqrestore(&task->task_state_lock, flags); - /* build and send the request. */ - status = isci_request_execute(ihost, idev, task, tag); - - if (status != SCI_SUCCESS) { - - spin_lock_irqsave(&task->task_state_lock, flags); - /* Did not really start this command. */ - task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - if (test_bit(IDEV_GONE, &idev->flags)) { - - /* Indicate that the device - * is gone. - */ - isci_task_refuse(ihost, task, - SAS_TASK_UNDELIVERED, - SAS_DEVICE_UNKNOWN); - } else { - /* Indicate QUEUE_FULL so that - * the scsi midlayer retries. - * If the request failed for - * remote device reasons, it - * gets returned as - * SAS_TASK_UNDELIVERED next - * time through. - */ - isci_task_refuse(ihost, task, - SAS_TASK_COMPLETE, - SAS_QUEUE_FULL); - } + if (test_bit(IDEV_GONE, &idev->flags)) { + /* Indicate that the device + * is gone. + */ + isci_task_refuse(ihost, task, + SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN); + } else { + /* Indicate QUEUE_FULL so that + * the scsi midlayer retries. + * If the request failed for + * remote device reasons, it + * gets returned as + * SAS_TASK_UNDELIVERED next + * time through. + */ + isci_task_refuse(ihost, task, + SAS_TASK_COMPLETE, + SAS_QUEUE_FULL); } } } - if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) { - spin_lock_irqsave(&ihost->scic_lock, flags); - /* command never hit the device, so just free - * the tci and skip the sequence increment - */ - isci_tci_free(ihost, ISCI_TAG_TCI(tag)); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - } - isci_put_device(idev); } + + if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) { + spin_lock_irqsave(&ihost->scic_lock, flags); + /* command never hit the device, so just free + * the tci and skip the sequence increment + */ + isci_tci_free(ihost, ISCI_TAG_TCI(tag)); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + } + + isci_put_device(idev); return 0; } diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 9c06cbad1d26..8f4531f22ac2 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -131,7 +131,6 @@ static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf) int isci_task_execute_task( struct sas_task *task, - int num, gfp_t gfp_flags); int isci_task_abort_task( diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 427af0f24b0f..0b8af186e707 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -952,7 +952,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .module = THIS_MODULE, .name = "iSCSI Initiator over TCP/IP", .queuecommand = iscsi_queuecommand, - .change_queue_depth = iscsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1, .sg_tablesize = 4096, .max_sectors = 0xFFFF, @@ -966,6 +966,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .target_alloc = iscsi_target_alloc, .proc_name = "iscsi_tcp", .this_id = -1, + .track_queue_depth = 1, }; static struct iscsi_transport iscsi_sw_tcp_transport = { diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 1d7e76e8b447..c6795941b45d 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -2160,62 +2160,12 @@ int fc_slave_alloc(struct scsi_device *sdev) if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; - if (sdev->tagged_supported) - scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH); - else - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), - FC_FCP_DFLT_QUEUE_DEPTH); - + scsi_change_queue_depth(sdev, FC_FCP_DFLT_QUEUE_DEPTH); return 0; } EXPORT_SYMBOL(fc_slave_alloc); /** - * fc_change_queue_depth() - Change a device's queue depth - * @sdev: The SCSI device whose queue depth is to change - * @qdepth: The new queue depth - * @reason: The resason for the change - */ -int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) -{ - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, qdepth); - break; - case SCSI_QDEPTH_RAMP_UP: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - break; - default: - return -EOPNOTSUPP; - } - return sdev->queue_depth; -} -EXPORT_SYMBOL(fc_change_queue_depth); - -/** - * fc_change_queue_type() - Change a device's queue type - * @sdev: The SCSI device whose queue depth is to change - * @tag_type: Identifier for queue type - */ -int fc_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; -} -EXPORT_SYMBOL(fc_change_queue_type); - -/** * fc_fcp_destory() - Tear down the FCP layer for a given local port * @lport: The local port that no longer needs the FCP layer */ diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 0d8bc6c66650..8053f24f0349 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1771,25 +1771,6 @@ fault: } EXPORT_SYMBOL_GPL(iscsi_queuecommand); -int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason) -{ - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, depth); - break; - case SCSI_QDEPTH_RAMP_UP: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - default: - return -EOPNOTSUPP; - } - return sdev->queue_depth; -} -EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); - int iscsi_target_alloc(struct scsi_target *starget) { struct iscsi_cls_session *cls_session = starget_to_session(starget); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 766098af4eb7..577770fdee86 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -171,7 +171,6 @@ static void sas_ata_task_done(struct sas_task *task) spin_unlock_irqrestore(ap->lock, flags); qc_already_gone: - list_del_init(&task->list); sas_free_task(task); } @@ -244,12 +243,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, task); - if (sas_ha->lldd_max_execute_num < 2) - ret = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); - else - ret = sas_queue_up(task); - - /* Examine */ + ret = i->dft->lldd_execute_task(task, GFP_ATOMIC); if (ret) { SAS_DPRINTK("lldd_execute_task returned: %d\n", ret); @@ -485,7 +479,6 @@ static void sas_ata_internal_abort(struct sas_task *task) return; out: - list_del_init(&task->list); sas_free_task(task); } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 0cac7d8fd0f7..022bb6e10d98 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -96,7 +96,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ; add_timer(&task->slow_task->timer); - res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); + res = i->dft->lldd_execute_task(task, GFP_KERNEL); if (res) { del_timer(&task->slow_task->timer); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index dbc8a793fd86..362da44f2948 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -45,7 +45,6 @@ struct sas_task *sas_alloc_task(gfp_t flags) struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags); if (task) { - INIT_LIST_HEAD(&task->list); spin_lock_init(&task->task_state_lock); task->task_state_flags = SAS_TASK_STATE_PENDING; } @@ -77,7 +76,6 @@ EXPORT_SYMBOL_GPL(sas_alloc_slow_task); void sas_free_task(struct sas_task *task) { if (task) { - BUG_ON(!list_empty(&task->list)); kfree(task->slow_task); kmem_cache_free(sas_task_cache, task); } @@ -127,11 +125,6 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) spin_lock_init(&sas_ha->phy_port_lock); sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); - if (sas_ha->lldd_queue_size == 0) - sas_ha->lldd_queue_size = 1; - else if (sas_ha->lldd_queue_size == -1) - sas_ha->lldd_queue_size = 128; /* Sanity */ - set_bit(SAS_HA_REGISTERED, &sas_ha->state); spin_lock_init(&sas_ha->lock); mutex_init(&sas_ha->drain_mutex); @@ -157,15 +150,6 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) goto Undo_ports; } - if (sas_ha->lldd_max_execute_num > 1) { - error = sas_init_queue(sas_ha); - if (error) { - printk(KERN_NOTICE "couldn't start queue thread:%d, " - "running in direct mode\n", error); - sas_ha->lldd_max_execute_num = 1; - } - } - INIT_LIST_HEAD(&sas_ha->eh_done_q); INIT_LIST_HEAD(&sas_ha->eh_ata_q); @@ -201,11 +185,6 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) __sas_drain_work(sas_ha); mutex_unlock(&sas_ha->drain_mutex); - if (sas_ha->lldd_max_execute_num > 1) { - sas_shutdown_queue(sas_ha); - sas_ha->lldd_max_execute_num = 1; - } - return 0; } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 7e7ba83f0a21..9cf0bc260b0e 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -66,9 +66,7 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha); enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); -int sas_init_queue(struct sas_ha_struct *sas_ha); int sas_init_events(struct sas_ha_struct *sas_ha); -void sas_shutdown_queue(struct sas_ha_struct *sas_ha); void sas_disable_revalidation(struct sas_ha_struct *ha); void sas_enable_revalidation(struct sas_ha_struct *ha); void __sas_drain_work(struct sas_ha_struct *ha); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 24e477d2ea70..72918d227ead 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -112,7 +112,6 @@ static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) sc->result = (hs << 16) | stat; ASSIGN_SAS_TASK(sc, NULL); - list_del_init(&task->list); sas_free_task(task); } @@ -138,7 +137,6 @@ static void sas_scsi_task_done(struct sas_task *task) if (unlikely(!sc)) { SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); - list_del_init(&task->list); sas_free_task(task); return; } @@ -179,31 +177,10 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, return task; } -int sas_queue_up(struct sas_task *task) -{ - struct sas_ha_struct *sas_ha = task->dev->port->ha; - struct scsi_core *core = &sas_ha->core; - unsigned long flags; - LIST_HEAD(list); - - spin_lock_irqsave(&core->task_queue_lock, flags); - if (sas_ha->lldd_queue_size < core->task_queue_size + 1) { - spin_unlock_irqrestore(&core->task_queue_lock, flags); - return -SAS_QUEUE_FULL; - } - list_add_tail(&task->list, &core->task_queue); - core->task_queue_size += 1; - spin_unlock_irqrestore(&core->task_queue_lock, flags); - wake_up_process(core->queue_thread); - - return 0; -} - int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { struct sas_internal *i = to_sas_internal(host->transportt); struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_ha_struct *sas_ha = dev->port->ha; struct sas_task *task; int res = 0; @@ -224,12 +201,7 @@ int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) if (!task) return SCSI_MLQUEUE_HOST_BUSY; - /* Queue up, Direct Mode or Task Collector Mode. */ - if (sas_ha->lldd_max_execute_num < 2) - res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); - else - res = sas_queue_up(task); - + res = i->dft->lldd_execute_task(task, GFP_ATOMIC); if (res) goto out_free_task; return 0; @@ -323,37 +295,17 @@ enum task_disposition { TASK_IS_DONE, TASK_IS_ABORTED, TASK_IS_AT_LU, - TASK_IS_NOT_AT_HA, TASK_IS_NOT_AT_LU, TASK_ABORT_FAILED, }; static enum task_disposition sas_scsi_find_task(struct sas_task *task) { - struct sas_ha_struct *ha = task->dev->port->ha; unsigned long flags; int i, res; struct sas_internal *si = to_sas_internal(task->dev->port->ha->core.shost->transportt); - if (ha->lldd_max_execute_num > 1) { - struct scsi_core *core = &ha->core; - struct sas_task *t, *n; - - mutex_lock(&core->task_queue_flush); - spin_lock_irqsave(&core->task_queue_lock, flags); - list_for_each_entry_safe(t, n, &core->task_queue, list) - if (task == t) { - list_del_init(&t->list); - break; - } - spin_unlock_irqrestore(&core->task_queue_lock, flags); - mutex_unlock(&core->task_queue_flush); - - if (task == t) - return TASK_IS_NOT_AT_HA; - } - for (i = 0; i < 5; i++) { SAS_DPRINTK("%s: aborting task 0x%p\n", __func__, task); res = si->dft->lldd_abort_task(task); @@ -667,14 +619,6 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * cmd->eh_eflags = 0; switch (res) { - case TASK_IS_NOT_AT_HA: - SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n", - __func__, task, - cmd->retries ? "retry" : "aborted"); - if (cmd->retries) - cmd->retries--; - sas_eh_finish_cmd(cmd); - continue; case TASK_IS_DONE: SAS_DPRINTK("%s: task 0x%p is done\n", __func__, task); @@ -836,9 +780,6 @@ retry: scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: - if (ha->lldd_max_execute_num > 1) - wake_up_process(ha->core.queue_thread); - sas_eh_handle_resets(shost); /* now link into libata eh --- if we have any ata devices */ @@ -940,15 +881,12 @@ int sas_slave_configure(struct scsi_device *scsi_dev) sas_read_port_mode_page(scsi_dev); if (scsi_dev->tagged_supported) { - scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG); - scsi_activate_tcq(scsi_dev, SAS_DEF_QD); + scsi_change_queue_depth(scsi_dev, SAS_DEF_QD); } else { SAS_DPRINTK("device %llx, LUN %llx doesn't support " "TCQ\n", SAS_ADDR(dev->sas_addr), scsi_dev->lun); - scsi_dev->tagged_supported = 0; - scsi_set_tag_type(scsi_dev, 0); - scsi_deactivate_tcq(scsi_dev, 1); + scsi_change_queue_depth(scsi_dev, 1); } scsi_dev->allow_restart = 1; @@ -956,47 +894,23 @@ int sas_slave_configure(struct scsi_device *scsi_dev) return 0; } -int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason) +int sas_change_queue_depth(struct scsi_device *sdev, int depth) { struct domain_device *dev = sdev_to_domain_dev(sdev); if (dev_is_sata(dev)) - return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth, - reason); - - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - case SCSI_QDEPTH_RAMP_UP: - if (!sdev->tagged_supported) - depth = 1; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, depth); - break; - default: - return -EOPNOTSUPP; - } + return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth); - return depth; + if (!sdev->tagged_supported) + depth = 1; + return scsi_change_queue_depth(sdev, depth); } -int sas_change_queue_type(struct scsi_device *scsi_dev, int qt) +int sas_change_queue_type(struct scsi_device *scsi_dev, int type) { - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - - if (dev_is_sata(dev)) + if (dev_is_sata(sdev_to_domain_dev(scsi_dev))) return -EINVAL; - - if (!scsi_dev->tagged_supported) - return 0; - - scsi_deactivate_tcq(scsi_dev, 1); - - scsi_set_tag_type(scsi_dev, qt); - scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); - - return qt; + return scsi_change_queue_type(scsi_dev, type); } int sas_bios_param(struct scsi_device *scsi_dev, @@ -1011,121 +925,6 @@ int sas_bios_param(struct scsi_device *scsi_dev, return 0; } -/* ---------- Task Collector Thread implementation ---------- */ - -static void sas_queue(struct sas_ha_struct *sas_ha) -{ - struct scsi_core *core = &sas_ha->core; - unsigned long flags; - LIST_HEAD(q); - int can_queue; - int res; - struct sas_internal *i = to_sas_internal(core->shost->transportt); - - mutex_lock(&core->task_queue_flush); - spin_lock_irqsave(&core->task_queue_lock, flags); - while (!kthread_should_stop() && - !list_empty(&core->task_queue) && - !test_bit(SAS_HA_FROZEN, &sas_ha->state)) { - - can_queue = sas_ha->lldd_queue_size - core->task_queue_size; - if (can_queue >= 0) { - can_queue = core->task_queue_size; - list_splice_init(&core->task_queue, &q); - } else { - struct list_head *a, *n; - - can_queue = sas_ha->lldd_queue_size; - list_for_each_safe(a, n, &core->task_queue) { - list_move_tail(a, &q); - if (--can_queue == 0) - break; - } - can_queue = sas_ha->lldd_queue_size; - } - core->task_queue_size -= can_queue; - spin_unlock_irqrestore(&core->task_queue_lock, flags); - { - struct sas_task *task = list_entry(q.next, - struct sas_task, - list); - list_del_init(&q); - res = i->dft->lldd_execute_task(task, can_queue, - GFP_KERNEL); - if (unlikely(res)) - __list_add(&q, task->list.prev, &task->list); - } - spin_lock_irqsave(&core->task_queue_lock, flags); - if (res) { - list_splice_init(&q, &core->task_queue); /*at head*/ - core->task_queue_size += can_queue; - } - } - spin_unlock_irqrestore(&core->task_queue_lock, flags); - mutex_unlock(&core->task_queue_flush); -} - -/** - * sas_queue_thread -- The Task Collector thread - * @_sas_ha: pointer to struct sas_ha - */ -static int sas_queue_thread(void *_sas_ha) -{ - struct sas_ha_struct *sas_ha = _sas_ha; - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - sas_queue(sas_ha); - if (kthread_should_stop()) - break; - } - - return 0; -} - -int sas_init_queue(struct sas_ha_struct *sas_ha) -{ - struct scsi_core *core = &sas_ha->core; - - spin_lock_init(&core->task_queue_lock); - mutex_init(&core->task_queue_flush); - core->task_queue_size = 0; - INIT_LIST_HEAD(&core->task_queue); - - core->queue_thread = kthread_run(sas_queue_thread, sas_ha, - "sas_queue_%d", core->shost->host_no); - if (IS_ERR(core->queue_thread)) - return PTR_ERR(core->queue_thread); - return 0; -} - -void sas_shutdown_queue(struct sas_ha_struct *sas_ha) -{ - unsigned long flags; - struct scsi_core *core = &sas_ha->core; - struct sas_task *task, *n; - - kthread_stop(core->queue_thread); - - if (!list_empty(&core->task_queue)) - SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n", - SAS_ADDR(sas_ha->sas_addr)); - - spin_lock_irqsave(&core->task_queue_lock, flags); - list_for_each_entry_safe(task, n, &core->task_queue, list) { - struct scsi_cmnd *cmd = task->uldd_task; - - list_del_init(&task->list); - - ASSIGN_SAS_TASK(cmd, NULL); - sas_free_task(task); - cmd->result = DID_ABORT << 16; - cmd->scsi_done(cmd); - } - spin_unlock_irqrestore(&core->task_queue_lock, flags); -} - /* * Tell an upper layer that it needs to initiate an abort for a given task. * This should only ever be called by an LLDD. diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index b99399fe2548..fd85952b621d 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -243,128 +243,6 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) } /** - * lpfc_send_sdev_queuedepth_change_event - Posts a queuedepth change event - * @phba: Pointer to HBA context object. - * @vport: Pointer to vport object. - * @ndlp: Pointer to FC node associated with the target. - * @lun: Lun number of the scsi device. - * @old_val: Old value of the queue depth. - * @new_val: New value of the queue depth. - * - * This function sends an event to the mgmt application indicating - * there is a change in the scsi device queue depth. - **/ -static void -lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba, - struct lpfc_vport *vport, - struct lpfc_nodelist *ndlp, - uint64_t lun, - uint32_t old_val, - uint32_t new_val) -{ - struct lpfc_fast_path_event *fast_path_evt; - unsigned long flags; - - fast_path_evt = lpfc_alloc_fast_evt(phba); - if (!fast_path_evt) - return; - - fast_path_evt->un.queue_depth_evt.scsi_event.event_type = - FC_REG_SCSI_EVENT; - fast_path_evt->un.queue_depth_evt.scsi_event.subcategory = - LPFC_EVENT_VARQUEDEPTH; - - /* Report all luns with change in queue depth */ - fast_path_evt->un.queue_depth_evt.scsi_event.lun = lun; - if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { - memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwpn, - &ndlp->nlp_portname, sizeof(struct lpfc_name)); - memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwnn, - &ndlp->nlp_nodename, sizeof(struct lpfc_name)); - } - - fast_path_evt->un.queue_depth_evt.oldval = old_val; - fast_path_evt->un.queue_depth_evt.newval = new_val; - fast_path_evt->vport = vport; - - fast_path_evt->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT; - spin_lock_irqsave(&phba->hbalock, flags); - list_add_tail(&fast_path_evt->work_evt.evt_listp, &phba->work_list); - spin_unlock_irqrestore(&phba->hbalock, flags); - lpfc_worker_wake_up(phba); - - return; -} - -/** - * lpfc_change_queue_depth - Alter scsi device queue depth - * @sdev: Pointer the scsi device on which to change the queue depth. - * @qdepth: New queue depth to set the sdev to. - * @reason: The reason for the queue depth change. - * - * This function is called by the midlayer and the LLD to alter the queue - * depth for a scsi device. This function sets the queue depth to the new - * value and sends an event out to log the queue depth change. - **/ -static int -lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) -{ - struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata; - struct lpfc_hba *phba = vport->phba; - struct lpfc_rport_data *rdata; - unsigned long new_queue_depth, old_queue_depth; - - old_queue_depth = sdev->queue_depth; - - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - /* change request from sysfs, fall through */ - case SCSI_QDEPTH_RAMP_UP: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - break; - case SCSI_QDEPTH_QFULL: - if (scsi_track_queue_full(sdev, qdepth) == 0) - return sdev->queue_depth; - - lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, - "0711 detected queue full - lun queue " - "depth adjusted to %d.\n", sdev->queue_depth); - break; - default: - return -EOPNOTSUPP; - } - - new_queue_depth = sdev->queue_depth; - rdata = lpfc_rport_data_from_scsi_device(sdev); - if (rdata) - lpfc_send_sdev_queuedepth_change_event(phba, vport, - rdata->pnode, sdev->lun, - old_queue_depth, - new_queue_depth); - return sdev->queue_depth; -} - -/** - * lpfc_change_queue_type() - Change a device's scsi tag queuing type - * @sdev: Pointer the scsi device whose queue depth is to change - * @tag_type: Identifier for queue tag type - */ -static int -lpfc_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; -} - -/** * lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread * @phba: The Hba for which this call is being executed. * @@ -449,8 +327,7 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) else new_queue_depth = sdev->queue_depth - new_queue_depth; - lpfc_change_queue_depth(sdev, new_queue_depth, - SCSI_QDEPTH_DEFAULT); + scsi_change_queue_depth(sdev, new_queue_depth); } } lpfc_destroy_vport_work_array(phba, vports); @@ -4286,7 +4163,6 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq); int datadir = scsi_cmnd->sc_data_direction; - char tag[2]; uint8_t *ptr; bool sli4; uint32_t fcpdl; @@ -4308,20 +4184,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, memset(ptr, 0, (LPFC_FCP_CDB_LEN - scsi_cmnd->cmd_len)); } - if (scsi_populate_tag_msg(scsi_cmnd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - fcp_cmnd->fcpCntl1 = HEAD_OF_Q; - break; - case ORDERED_QUEUE_TAG: - fcp_cmnd->fcpCntl1 = ORDERED_Q; - break; - default: - fcp_cmnd->fcpCntl1 = SIMPLE_Q; - break; - } - } else - fcp_cmnd->fcpCntl1 = SIMPLE_Q; + fcp_cmnd->fcpCntl1 = SIMPLE_Q; sli4 = (phba->sli_rev == LPFC_SLI_REV4); piocbq->iocb.un.fcpi.fcpi_XRdy = 0; @@ -5632,10 +5495,7 @@ lpfc_slave_configure(struct scsi_device *sdev) struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata; struct lpfc_hba *phba = vport->phba; - if (sdev->tagged_supported) - scsi_activate_tcq(sdev, vport->cfg_lun_queue_depth); - else - scsi_deactivate_tcq(sdev, vport->cfg_lun_queue_depth); + scsi_change_queue_depth(sdev, vport->cfg_lun_queue_depth); if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { lpfc_sli_handle_fast_ring_event(phba, @@ -6018,8 +5878,10 @@ struct scsi_host_template lpfc_template = { .shost_attrs = lpfc_hba_attrs, .max_sectors = 0xFFFF, .vendor_id = LPFC_NL_VENDOR_ID, - .change_queue_depth = lpfc_change_queue_depth, - .change_queue_type = lpfc_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, + .use_blk_tags = 1, + .track_queue_depth = 1, }; struct scsi_host_template lpfc_vport_template = { @@ -6041,6 +5903,8 @@ struct scsi_host_template lpfc_vport_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = lpfc_vport_attrs, .max_sectors = 0xFFFF, - .change_queue_depth = lpfc_change_queue_depth, - .change_queue_type = lpfc_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, + .use_blk_tags = 1, + .track_queue_depth = 1, }; diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c index 6a039eb1cbce..953fd9b953c7 100644 --- a/drivers/scsi/mac_scsi.c +++ b/drivers/scsi/mac_scsi.c @@ -9,69 +9,62 @@ * Generic Generic NCR5380 driver * * Copyright 1995, Russell King - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ #include <linux/types.h> -#include <linux/stddef.h> -#include <linux/ctype.h> #include <linux/delay.h> - #include <linux/module.h> -#include <linux/signal.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/blkdev.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <asm/hwtest.h> #include <asm/io.h> -#include <asm/irq.h> - -#include <asm/macintosh.h> #include <asm/macints.h> -#include <asm/mac_via.h> +#include <asm/setup.h> -#include "scsi.h" #include <scsi/scsi_host.h> -#include "mac_scsi.h" -/* These control the behaviour of the generic 5380 core */ -#define AUTOSENSE +/* Definitions for the core NCR5380 driver. */ + #define PSEUDO_DMA -#include "NCR5380.h" +#define NCR5380_implementation_fields unsigned char *pdma_base +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance -#define RESET_BOOT -#define DRIVER_SETUP +#define NCR5380_read(reg) macscsi_read(_instance, reg) +#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value) -extern void via_scsi_clear(void); +#define NCR5380_pread macscsi_pread +#define NCR5380_pwrite macscsi_pwrite -#ifdef RESET_BOOT -static void mac_scsi_reset_boot(struct Scsi_Host *instance); -#endif +#define NCR5380_intr macscsi_intr +#define NCR5380_queue_command macscsi_queue_command +#define NCR5380_abort macscsi_abort +#define NCR5380_bus_reset macscsi_bus_reset +#define NCR5380_info macscsi_info +#define NCR5380_show_info macscsi_show_info +#define NCR5380_write_info macscsi_write_info + +#include "NCR5380.h" + +#define RESET_BOOT -static int setup_called = 0; static int setup_can_queue = -1; +module_param(setup_can_queue, int, 0); static int setup_cmd_per_lun = -1; +module_param(setup_cmd_per_lun, int, 0); static int setup_sg_tablesize = -1; +module_param(setup_sg_tablesize, int, 0); static int setup_use_pdma = -1; -#ifdef SUPPORT_TAGS +module_param(setup_use_pdma, int, 0); static int setup_use_tagged_queuing = -1; -#endif +module_param(setup_use_tagged_queuing, int, 0); static int setup_hostid = -1; +module_param(setup_hostid, int, 0); /* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms, * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more @@ -84,232 +77,48 @@ static int setup_hostid = -1; #define AFTER_RESET_DELAY (HZ/2) #endif -static volatile unsigned char *mac_scsi_regp = NULL; -static volatile unsigned char *mac_scsi_drq = NULL; -static volatile unsigned char *mac_scsi_nodrq = NULL; - - /* * NCR 5380 register access functions */ -#if 0 -/* Debug versions */ -#define CTRL(p,v) (*ctrl = (v)) - -static char macscsi_read(struct Scsi_Host *instance, int reg) +static inline char macscsi_read(struct Scsi_Host *instance, int reg) { - int iobase = instance->io_port; - int i; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - - CTRL(iobase, 0); - i = in_8(iobase + (reg<<4)); - CTRL(iobase, 0x40); - - return i; + return in_8(instance->base + (reg << 4)); } -static void macscsi_write(struct Scsi_Host *instance, int reg, int value) -{ - int iobase = instance->io_port; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - - CTRL(iobase, 0); - out_8(iobase + (reg<<4), value); - CTRL(iobase, 0x40); -} -#else - -/* Fast versions */ -static __inline__ char macscsi_read(struct Scsi_Host *instance, int reg) +static inline void macscsi_write(struct Scsi_Host *instance, int reg, int value) { - return in_8(instance->io_port + (reg<<4)); + out_8(instance->base + (reg << 4), value); } -static __inline__ void macscsi_write(struct Scsi_Host *instance, int reg, int value) +#ifndef MODULE +static int __init mac_scsi_setup(char *str) { - out_8(instance->io_port + (reg<<4), value); -} -#endif - + int ints[7]; -/* - * Function : mac_scsi_setup(char *str) - * - * Purpose : booter command line initialization of the overrides array, - * - * Inputs : str - comma delimited list of options - * - */ + (void)get_options(str, ARRAY_SIZE(ints), ints); -static int __init mac_scsi_setup(char *str) { -#ifdef DRIVER_SETUP - int ints[7]; - - (void)get_options( str, ARRAY_SIZE(ints), ints); - - if (setup_called++ || ints[0] < 1 || ints[0] > 6) { - printk(KERN_WARNING "scsi: <mac5380>" - " Usage: mac5380=<can_queue>[,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>,<use_pdma>]\n"); - printk(KERN_ALERT "scsi: <mac5380> Bad Penguin parameters?\n"); - return 0; - } - - if (ints[0] >= 1) { - if (ints[1] > 0) - /* no limits on this, just > 0 */ - setup_can_queue = ints[1]; - } - if (ints[0] >= 2) { - if (ints[2] > 0) - setup_cmd_per_lun = ints[2]; - } - if (ints[0] >= 3) { - if (ints[3] >= 0) { - setup_sg_tablesize = ints[3]; - /* Must be <= SG_ALL (255) */ - if (setup_sg_tablesize > SG_ALL) - setup_sg_tablesize = SG_ALL; - } - } - if (ints[0] >= 4) { - /* Must be between 0 and 7 */ - if (ints[4] >= 0 && ints[4] <= 7) - setup_hostid = ints[4]; - else if (ints[4] > 7) - printk(KERN_WARNING "mac_scsi_setup: invalid host ID %d !\n", ints[4] ); - } -#ifdef SUPPORT_TAGS - if (ints[0] >= 5) { - if (ints[5] >= 0) - setup_use_tagged_queuing = !!ints[5]; + if (ints[0] < 1 || ints[0] > 6) { + pr_err("Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>]]]]]\n"); + return 0; } - - if (ints[0] == 6) { - if (ints[6] >= 0) + if (ints[0] >= 1) + setup_can_queue = ints[1]; + if (ints[0] >= 2) + setup_cmd_per_lun = ints[2]; + if (ints[0] >= 3) + setup_sg_tablesize = ints[3]; + if (ints[0] >= 4) + setup_hostid = ints[4]; + if (ints[0] >= 5) + setup_use_tagged_queuing = ints[5]; + if (ints[0] >= 6) setup_use_pdma = ints[6]; - } -#else - if (ints[0] == 5) { - if (ints[5] >= 0) - setup_use_pdma = ints[5]; - } -#endif /* SUPPORT_TAGS */ - -#endif /* DRIVER_SETUP */ return 1; } __setup("mac5380=", mac_scsi_setup); - -/* - * Function : int macscsi_detect(struct scsi_host_template * tpnt) - * - * Purpose : initializes mac NCR5380 driver based on the - * command line / compile time port and irq definitions. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -int __init macscsi_detect(struct scsi_host_template * tpnt) -{ - static int called = 0; - int flags = 0; - struct Scsi_Host *instance; - - if (!MACH_IS_MAC || called) - return( 0 ); - - if (macintosh_config->scsi_type != MAC_SCSI_OLD) - return( 0 ); - - /* setup variables */ - tpnt->can_queue = - (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE; - tpnt->cmd_per_lun = - (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN; - tpnt->sg_tablesize = - (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE; - - if (setup_hostid >= 0) - tpnt->this_id = setup_hostid; - else { - /* use 7 as default */ - tpnt->this_id = 7; - } - -#ifdef SUPPORT_TAGS - if (setup_use_tagged_queuing < 0) - setup_use_tagged_queuing = USE_TAGGED_QUEUING; -#endif - - /* Once we support multiple 5380s (e.g. DuoDock) we'll do - something different here */ - instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if (instance == NULL) - return 0; - - if (macintosh_config->ident == MAC_MODEL_IIFX) { - mac_scsi_regp = via1+0x8000; - mac_scsi_drq = via1+0xE000; - mac_scsi_nodrq = via1+0xC000; - /* The IIFX should be able to do true DMA, but pseudo-dma doesn't work */ - flags = FLAG_NO_PSEUDO_DMA; - } else { - mac_scsi_regp = via1+0x10000; - mac_scsi_drq = via1+0x6000; - mac_scsi_nodrq = via1+0x12000; - } - - if (! setup_use_pdma) - flags = FLAG_NO_PSEUDO_DMA; - - instance->io_port = (unsigned long) mac_scsi_regp; - instance->irq = IRQ_MAC_SCSI; - -#ifdef RESET_BOOT - mac_scsi_reset_boot(instance); -#endif - - NCR5380_init(instance, flags); - - instance->n_io_port = 255; - - ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0; - - if (instance->irq != SCSI_IRQ_NONE) - if (request_irq(instance->irq, NCR5380_intr, 0, "ncr5380", instance)) { - printk(KERN_WARNING "scsi%d: IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = SCSI_IRQ_NONE; - } - - printk(KERN_INFO "scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port); - if (instance->irq == SCSI_IRQ_NONE) - printk (KERN_INFO "s disabled"); - else - printk (KERN_INFO " %d", instance->irq); - printk(KERN_INFO " options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - instance->can_queue, instance->cmd_per_lun, MACSCSI_PUBLIC_RELEASE); - printk(KERN_INFO "\nscsi%d:", instance->host_no); - NCR5380_print_options(instance); - printk("\n"); - called = 1; - return 1; -} - -int macscsi_release (struct Scsi_Host *shpnt) -{ - if (shpnt->irq != SCSI_IRQ_NONE) - free_irq(shpnt->irq, shpnt); - NCR5380_exit(shpnt); - - return 0; -} +#endif /* !MODULE */ #ifdef RESET_BOOT /* @@ -349,10 +158,7 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) } #endif -const char * macscsi_info (struct Scsi_Host *spnt) { - return ""; -} - +#ifdef PSEUDO_DMA /* Pseudo-DMA: (Ove Edlund) The code attempts to catch bus errors that occur if one for example @@ -422,38 +228,39 @@ __asm__ __volatile__ \ : "0"(s), "1"(d), "2"(len) \ : "d0") - -static int macscsi_pread (struct Scsi_Host *instance, - unsigned char *dst, int len) +static int macscsi_pread(struct Scsi_Host *instance, + unsigned char *dst, int len) { - unsigned char *d; - volatile unsigned char *s; - - NCR5380_local_declare(); - NCR5380_setup(instance); - - s = mac_scsi_drq+0x60; - d = dst; - -/* These conditions are derived from MacOS */ - - while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) - && !(NCR5380_read(STATUS_REG) & SR_REQ)) - ; - if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) - && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) { - printk(KERN_ERR "Error in macscsi_pread\n"); - return -1; - } - - CP_IO_TO_MEM(s, d, len); - - if (len != 0) { - printk(KERN_NOTICE "Bus error in macscsi_pread\n"); - return -1; - } - - return 0; + struct NCR5380_hostdata *hostdata = shost_priv(instance); + unsigned char *d; + unsigned char *s; + + NCR5380_local_declare(); + NCR5380_setup(instance); + + s = hostdata->pdma_base + (INPUT_DATA_REG << 4); + d = dst; + + /* These conditions are derived from MacOS */ + + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) && + !(NCR5380_read(STATUS_REG) & SR_REQ)) + ; + + if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) && + (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) { + pr_err("Error in macscsi_pread\n"); + return -1; + } + + CP_IO_TO_MEM(s, d, len); + + if (len != 0) { + pr_notice("Bus error in macscsi_pread\n"); + return -1; + } + + return 0; } @@ -515,59 +322,172 @@ __asm__ __volatile__ \ : "0"(s), "1"(d), "2"(len) \ : "d0") -static int macscsi_pwrite (struct Scsi_Host *instance, - unsigned char *src, int len) +static int macscsi_pwrite(struct Scsi_Host *instance, + unsigned char *src, int len) { - unsigned char *s; - volatile unsigned char *d; - - NCR5380_local_declare(); - NCR5380_setup(instance); - - s = src; - d = mac_scsi_drq; - -/* These conditions are derived from MacOS */ - - while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) - && (!(NCR5380_read(STATUS_REG) & SR_REQ) - || (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH))) - ; - if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) { - printk(KERN_ERR "Error in macscsi_pwrite\n"); - return -1; - } - - CP_MEM_TO_IO(s, d, len); - - if (len != 0) { - printk(KERN_NOTICE "Bus error in macscsi_pwrite\n"); - return -1; - } - - return 0; -} + struct NCR5380_hostdata *hostdata = shost_priv(instance); + unsigned char *s; + unsigned char *d; + + NCR5380_local_declare(); + NCR5380_setup(instance); + + s = src; + d = hostdata->pdma_base + (OUTPUT_DATA_REG << 4); + + /* These conditions are derived from MacOS */ + + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) && + (!(NCR5380_read(STATUS_REG) & SR_REQ) || + (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH))) + ; + + if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) { + pr_err("Error in macscsi_pwrite\n"); + return -1; + } + + CP_MEM_TO_IO(s, d, len); + + if (len != 0) { + pr_notice("Bus error in macscsi_pwrite\n"); + return -1; + } + return 0; +} +#endif #include "NCR5380.c" -static struct scsi_host_template driver_template = { - .proc_name = "Mac5380", +#define DRV_MODULE_NAME "mac_scsi" +#define PFX DRV_MODULE_NAME ": " + +static struct scsi_host_template mac_scsi_template = { + .module = THIS_MODULE, + .proc_name = DRV_MODULE_NAME, .show_info = macscsi_show_info, .write_info = macscsi_write_info, .name = "Macintosh NCR5380 SCSI", - .detect = macscsi_detect, - .release = macscsi_release, .info = macscsi_info, .queuecommand = macscsi_queue_command, .eh_abort_handler = macscsi_abort, .eh_bus_reset_handler = macscsi_bus_reset, - .can_queue = CAN_QUEUE, + .can_queue = 16, .this_id = 7, .sg_tablesize = SG_ALL, - .cmd_per_lun = CMD_PER_LUN, + .cmd_per_lun = 2, .use_clustering = DISABLE_CLUSTERING }; +static int __init mac_scsi_probe(struct platform_device *pdev) +{ + struct Scsi_Host *instance; + int error; + int host_flags = 0; + struct resource *irq, *pio_mem, *pdma_mem = NULL; + + pio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pio_mem) + return -ENODEV; + +#ifdef PSEUDO_DMA + pdma_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); +#endif + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + if (!hwreg_present((unsigned char *)pio_mem->start + + (STATUS_REG << 4))) { + pr_info(PFX "no device detected at %pap\n", &pio_mem->start); + return -ENODEV; + } + + if (setup_can_queue > 0) + mac_scsi_template.can_queue = setup_can_queue; + if (setup_cmd_per_lun > 0) + mac_scsi_template.cmd_per_lun = setup_cmd_per_lun; + if (setup_sg_tablesize >= 0) + mac_scsi_template.sg_tablesize = setup_sg_tablesize; + if (setup_hostid >= 0) + mac_scsi_template.this_id = setup_hostid & 7; + if (setup_use_pdma < 0) + setup_use_pdma = 0; + + instance = scsi_host_alloc(&mac_scsi_template, + sizeof(struct NCR5380_hostdata)); + if (!instance) + return -ENOMEM; + + instance->base = pio_mem->start; + if (irq) + instance->irq = irq->start; + else + instance->irq = NO_IRQ; + + if (pdma_mem && setup_use_pdma) { + struct NCR5380_hostdata *hostdata = shost_priv(instance); + + hostdata->pdma_base = (unsigned char *)pdma_mem->start; + } else + host_flags |= FLAG_NO_PSEUDO_DMA; + +#ifdef RESET_BOOT + mac_scsi_reset_boot(instance); +#endif + +#ifdef SUPPORT_TAGS + host_flags |= setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0; +#endif + + NCR5380_init(instance, host_flags); + + if (instance->irq != NO_IRQ) { + error = request_irq(instance->irq, macscsi_intr, IRQF_SHARED, + "NCR5380", instance); + if (error) + goto fail_irq; + } + + error = scsi_add_host(instance, NULL); + if (error) + goto fail_host; + + platform_set_drvdata(pdev, instance); + + scsi_scan_host(instance); + return 0; + +fail_host: + if (instance->irq != NO_IRQ) + free_irq(instance->irq, instance); +fail_irq: + NCR5380_exit(instance); + scsi_host_put(instance); + return error; +} + +static int __exit mac_scsi_remove(struct platform_device *pdev) +{ + struct Scsi_Host *instance = platform_get_drvdata(pdev); + + scsi_remove_host(instance); + if (instance->irq != NO_IRQ) + free_irq(instance->irq, instance); + NCR5380_exit(instance); + scsi_host_put(instance); + return 0; +} + +static struct platform_driver mac_scsi_driver = { + .remove = __exit_p(mac_scsi_remove), + .driver = { + .name = DRV_MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver_probe(mac_scsi_driver, mac_scsi_probe); -#include "scsi_module.c" +MODULE_ALIAS("platform:" DRV_MODULE_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/mac_scsi.h b/drivers/scsi/mac_scsi.h deleted file mode 100644 index 06969b06e54b..000000000000 --- a/drivers/scsi/mac_scsi.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Cumana Generic NCR5380 driver defines - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 - */ - -#ifndef MAC_NCR5380_H -#define MAC_NCR5380_H - -#define MACSCSI_PUBLIC_RELEASE 2 - -#ifndef ASM - -#ifndef CMD_PER_LUN -#define CMD_PER_LUN 2 -#endif - -#ifndef CAN_QUEUE -#define CAN_QUEUE 16 -#endif - -#ifndef SG_TABLESIZE -#define SG_TABLESIZE SG_NONE -#endif - -#ifndef USE_TAGGED_QUEUING -#define USE_TAGGED_QUEUING 0 -#endif - -#include <scsi/scsicam.h> - -#define NCR5380_implementation_fields \ - int port, ctrl - -#define NCR5380_local_declare() \ - struct Scsi_Host *_instance - -#define NCR5380_setup(instance) \ - _instance = instance - -#define NCR5380_read(reg) macscsi_read(_instance, reg) -#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value) - -#define NCR5380_pread macscsi_pread -#define NCR5380_pwrite macscsi_pwrite - -#define NCR5380_intr macscsi_intr -#define NCR5380_queue_command macscsi_queue_command -#define NCR5380_abort macscsi_abort -#define NCR5380_bus_reset macscsi_bus_reset -#define NCR5380_show_info macscsi_show_info -#define NCR5380_write_info macscsi_write_info - -#endif /* ndef ASM */ -#endif /* MAC_NCR5380_H */ - diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index ac5d94cfd52f..2485255f3414 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -1945,7 +1945,7 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) cmd->device->id, (u32)cmd->device->lun); if(list_empty(&adapter->pending_list)) - return FALSE; + return FAILED; list_for_each_safe(pos, next, &adapter->pending_list) { @@ -1968,7 +1968,7 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) (aor==SCB_ABORT) ? "ABORTING":"RESET", scb->idx); - return FALSE; + return FAILED; } else { @@ -1993,12 +1993,12 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) list_add_tail(SCSI_LIST(cmd), &adapter->completed_list); - return TRUE; + return SUCCESS; } } } - return FALSE; + return FAILED; } static inline int diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 531dce419c18..f0987f22ea70 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -332,27 +332,6 @@ static struct device_attribute *megaraid_sdev_attrs[] = { NULL, }; -/** - * megaraid_change_queue_depth - Change the device's queue depth - * @sdev: scsi device struct - * @qdepth: depth to set - * @reason: calling context - * - * Return value: - * actual depth set - */ -static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason) -{ - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - - if (qdepth > MBOX_MAX_SCSI_CMDS) - qdepth = MBOX_MAX_SCSI_CMDS; - scsi_adjust_queue_depth(sdev, 0, qdepth); - return sdev->queue_depth; -} - /* * Scsi host template for megaraid unified driver */ @@ -365,7 +344,7 @@ static struct scsi_host_template megaraid_template_g = { .eh_device_reset_handler = megaraid_reset_handler, .eh_bus_reset_handler = megaraid_reset_handler, .eh_host_reset_handler = megaraid_reset_handler, - .change_queue_depth = megaraid_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .use_clustering = ENABLE_CLUSTERING, .no_write_same = 1, .sdev_attrs = megaraid_sdev_attrs, diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index a49914de4b95..0d44d91c2fce 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1,7 +1,8 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2003-2012 LSI Corporation. + * Copyright (c) 2003-2013 LSI Corporation + * Copyright (c) 2013-2014 Avago Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,17 +15,18 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * * FILE: megaraid_sas.h * - * Authors: LSI Corporation + * Authors: Avago Technologies + * Kashyap Desai <kashyap.desai@avagotech.com> + * Sumit Saxena <sumit.saxena@avagotech.com> * - * Send feedback to: <megaraidlinux@lsi.com> + * Send feedback to: megaraidlinux.pdl@avagotech.com * - * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 - * ATTN: Linuxraid + * Mail to: Avago Technologies, 350 West Trimble Road, Building 90, + * San Jose, California 95131 */ #ifndef LSI_MEGARAID_SAS_H @@ -33,9 +35,7 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "06.805.06.00-rc1" -#define MEGASAS_RELDATE "Sep. 4, 2014" -#define MEGASAS_EXT_VERSION "Thu. Sep. 4 17:00:00 PDT 2014" +#define MEGASAS_VERSION "06.805.06.01-rc1" /* * Device IDs @@ -1931,8 +1931,7 @@ u16 get_updated_dev_handle(struct megasas_instance *instance, struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *in_info); void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map, struct LD_LOAD_BALANCE_INFO *lbInfo); -int megasas_get_ctrl_info(struct megasas_instance *instance, - struct megasas_ctrl_info *ctrl_info); +int megasas_get_ctrl_info(struct megasas_instance *instance); int megasas_set_crash_dump_params(struct megasas_instance *instance, u8 crash_buf_state); void megasas_free_host_crash_buffer(struct megasas_instance *instance); diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 5640ad1c8214..f05580e693d0 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1,7 +1,8 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2003-2012 LSI Corporation. + * Copyright (c) 2003-2013 LSI Corporation + * Copyright (c) 2013-2014 Avago Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,22 +15,20 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * FILE: megaraid_sas_base.c - * Version : 06.805.06.00-rc1 - * - * Authors: LSI Corporation + * Authors: Avago Technologies * Sreenivas Bagalkote * Sumant Patro * Bo Yang - * Adam Radford <linuxraid@lsi.com> + * Adam Radford + * Kashyap Desai <kashyap.desai@avagotech.com> + * Sumit Saxena <sumit.saxena@avagotech.com> * - * Send feedback to: <megaraidlinux@lsi.com> + * Send feedback to: megaraidlinux.pdl@avagotech.com * - * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 - * ATTN: Linuxraid + * Mail to: Avago Technologies, 350 West Trimble Road, Building 90, + * San Jose, California 95131 */ #include <linux/kernel.h> @@ -1008,7 +1007,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr)); cmd->sync_cmd = 1; - cmd->cmd_status = 0xFF; + cmd->cmd_status = ENODATA; instance->instancet->issue_dcmd(instance, cmd); @@ -1572,6 +1571,12 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) instance = (struct megasas_instance *) scmd->device->host->hostdata; + if (instance->unload == 1) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } + if (instance->issuepend_done == 0) return SCSI_MLQUEUE_HOST_BUSY; @@ -2586,20 +2591,6 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) } } -static int megasas_change_queue_depth(struct scsi_device *sdev, - int queue_depth, int reason) -{ - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - - if (queue_depth > sdev->host->can_queue) - queue_depth = sdev->host->can_queue; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), - queue_depth); - - return queue_depth; -} - static ssize_t megasas_fw_crash_buffer_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) @@ -2764,7 +2755,7 @@ static struct scsi_host_template megasas_template = { .shost_attrs = megaraid_host_attrs, .bios_param = megasas_bios_param, .use_clustering = ENABLE_CLUSTERING, - .change_queue_depth = megasas_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .no_write_same = 1, }; @@ -4028,25 +4019,83 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) return ret; } +/* + * megasas_update_ext_vd_details : Update details w.r.t Extended VD + * instance : Controller's instance +*/ +static void megasas_update_ext_vd_details(struct megasas_instance *instance) +{ + struct fusion_context *fusion; + u32 old_map_sz; + u32 new_map_sz; + + fusion = instance->ctrl_context; + /* For MFI based controllers return dummy success */ + if (!fusion) + return; + + instance->supportmax256vd = + instance->ctrl_info->adapterOperations3.supportMaxExtLDs; + /* Below is additional check to address future FW enhancement */ + if (instance->ctrl_info->max_lds > 64) + instance->supportmax256vd = 1; + + instance->drv_supported_vd_count = MEGASAS_MAX_LD_CHANNELS + * MEGASAS_MAX_DEV_PER_CHANNEL; + instance->drv_supported_pd_count = MEGASAS_MAX_PD_CHANNELS + * MEGASAS_MAX_DEV_PER_CHANNEL; + if (instance->supportmax256vd) { + instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES_EXT; + instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; + } else { + instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES; + instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; + } + dev_info(&instance->pdev->dev, "Firmware supports %d VD %d PD\n", + instance->fw_supported_vd_count, + instance->fw_supported_pd_count); + dev_info(&instance->pdev->dev, "Driver supports %d VD %d PD\n", + instance->drv_supported_vd_count, + instance->drv_supported_pd_count); + + old_map_sz = sizeof(struct MR_FW_RAID_MAP) + + (sizeof(struct MR_LD_SPAN_MAP) * + (instance->fw_supported_vd_count - 1)); + new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT); + fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP) + + (sizeof(struct MR_LD_SPAN_MAP) * + (instance->drv_supported_vd_count - 1)); + + fusion->max_map_sz = max(old_map_sz, new_map_sz); + + + if (instance->supportmax256vd) + fusion->current_map_sz = new_map_sz; + else + fusion->current_map_sz = old_map_sz; + +} + /** * megasas_get_controller_info - Returns FW's controller structure * @instance: Adapter soft state - * @ctrl_info: Controller information structure * * Issues an internal command (DCMD) to get the FW's controller structure. * This information is mainly used to find out the maximum IO transfer per * command supported by the FW. */ int -megasas_get_ctrl_info(struct megasas_instance *instance, - struct megasas_ctrl_info *ctrl_info) +megasas_get_ctrl_info(struct megasas_instance *instance) { int ret = 0; struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; struct megasas_ctrl_info *ci; + struct megasas_ctrl_info *ctrl_info; dma_addr_t ci_h = 0; + ctrl_info = instance->ctrl_info; + cmd = megasas_get_cmd(instance); if (!cmd) { @@ -4086,8 +4135,13 @@ megasas_get_ctrl_info(struct megasas_instance *instance, else ret = megasas_issue_polled(instance, cmd); - if (!ret) + if (!ret) { memcpy(ctrl_info, ci, sizeof(struct megasas_ctrl_info)); + le32_to_cpus((u32 *)&ctrl_info->properties.OnOffProperties); + le32_to_cpus((u32 *)&ctrl_info->adapterOperations2); + le32_to_cpus((u32 *)&ctrl_info->adapterOperations3); + megasas_update_ext_vd_details(instance); + } pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info), ci, ci_h); @@ -4289,7 +4343,7 @@ megasas_init_adapter_mfi(struct megasas_instance *instance) if (megasas_issue_init_mfi(instance)) goto fail_fw_init; - if (megasas_get_ctrl_info(instance, instance->ctrl_info)) { + if (megasas_get_ctrl_info(instance)) { dev_err(&instance->pdev->dev, "(%d): Could get controller info " "Fail from %s %d\n", instance->unique_id, __func__, __LINE__); @@ -4527,12 +4581,8 @@ static int megasas_init_fw(struct megasas_instance *instance) dev_info(&instance->pdev->dev, "Controller type: iMR\n"); } - /* OnOffProperties are converted into CPU arch*/ - le32_to_cpus((u32 *)&ctrl_info->properties.OnOffProperties); instance->disableOnlineCtrlReset = ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset; - /* adapterOperations2 are converted into CPU arch*/ - le32_to_cpus((u32 *)&ctrl_info->adapterOperations2); instance->mpio = ctrl_info->adapterOperations2.mpio; instance->UnevenSpanSupport = ctrl_info->adapterOperations2.supportUnevenSpans; @@ -4562,7 +4612,6 @@ static int megasas_init_fw(struct megasas_instance *instance) "requestorId %d\n", instance->requestorId); } - le32_to_cpus((u32 *)&ctrl_info->adapterOperations3); instance->crash_dump_fw_support = ctrl_info->adapterOperations3.supportCrashDump; instance->crash_dump_drv_support = @@ -4587,8 +4636,6 @@ static int megasas_init_fw(struct megasas_instance *instance) if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors)) instance->max_sectors_per_req = tmp_sectors; - kfree(ctrl_info); - /* Check for valid throttlequeuedepth module parameter */ if (instance->is_imr) { if (throttlequeuedepth > (instance->max_fw_cmds - @@ -4957,10 +5004,6 @@ static int megasas_io_attach(struct megasas_instance *instance) return -ENODEV; } - /* - * Trigger SCSI to scan our drives - */ - scsi_scan_host(host); return 0; } @@ -5083,6 +5126,8 @@ static int megasas_probe_one(struct pci_dev *pdev, goto fail_alloc_dma_buf; } fusion = instance->ctrl_context; + memset(fusion, 0, + ((1 << PAGE_SHIFT) << instance->ctrl_context_pages)); INIT_LIST_HEAD(&fusion->cmd_pool); spin_lock_init(&fusion->mpt_pool_lock); memset(fusion->load_balance_info, 0, @@ -5288,6 +5333,10 @@ retry_irq_register: goto fail_io_attach; instance->unload = 0; + /* + * Trigger SCSI to scan our drives + */ + scsi_scan_host(host); /* * Initiate AEN (Asynchronous Event Notification) @@ -6051,6 +6100,11 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, megasas_issue_blocked_cmd(instance, cmd, 0); cmd->sync_cmd = 0; + if (instance->unload == 1) { + dev_info(&instance->pdev->dev, "Driver unload is in progress " + "don't submit data to application\n"); + goto out; + } /* * copy out the kernel buffers to user buffers */ @@ -6400,16 +6454,6 @@ static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf) static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL); static ssize_t -megasas_sysfs_show_release_date(struct device_driver *dd, char *buf) -{ - return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n", - MEGASAS_RELDATE); -} - -static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, - NULL); - -static ssize_t megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf) { return sprintf(buf, "%u\n", support_poll_for_event); @@ -6712,8 +6756,7 @@ static int __init megasas_init(void) /* * Announce driver version and other information */ - printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION, - MEGASAS_EXT_VERSION); + pr_info("megasas: %s\n", MEGASAS_VERSION); spin_lock_init(&poll_aen_lock); @@ -6748,10 +6791,6 @@ static int __init megasas_init(void) &driver_attr_version); if (rval) goto err_dcf_attr_ver; - rval = driver_create_file(&megasas_pci_driver.driver, - &driver_attr_release_date); - if (rval) - goto err_dcf_rel_date; rval = driver_create_file(&megasas_pci_driver.driver, &driver_attr_support_poll_for_event); @@ -6775,12 +6814,7 @@ err_dcf_support_device_change: err_dcf_dbg_lvl: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_poll_for_event); - err_dcf_support_poll_for_event: - driver_remove_file(&megasas_pci_driver.driver, - &driver_attr_release_date); - -err_dcf_rel_date: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); err_dcf_attr_ver: pci_unregister_driver(&megasas_pci_driver); @@ -6800,8 +6834,6 @@ static void __exit megasas_exit(void) &driver_attr_support_poll_for_event); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_device_change); - driver_remove_file(&megasas_pci_driver.driver, - &driver_attr_release_date); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); pci_unregister_driver(&megasas_pci_driver); diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 685e6f391fe4..460c6a3d4ade 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -1,7 +1,8 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2012 LSI Corporation. + * Copyright (c) 2009-2013 LSI Corporation + * Copyright (c) 2013-2014 Avago Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,20 +15,21 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * * FILE: megaraid_sas_fp.c * - * Authors: LSI Corporation + * Authors: Avago Technologies * Sumant Patro * Varad Talamacki * Manoj Jose + * Kashyap Desai <kashyap.desai@avagotech.com> + * Sumit Saxena <sumit.saxena@avagotech.com> * - * Send feedback to: <megaraidlinux@lsi.com> + * Send feedback to: megaraidlinux.pdl@avagotech.com * - * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 - * ATTN: Linuxraid + * Mail to: Avago Technologies, 350 West Trimble Road, Building 90, + * San Jose, California 95131 */ #include <linux/kernel.h> @@ -183,14 +185,15 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) /* New Raid map will not set totalSize, so keep expected value * for legacy code in ValidateMapInfo */ - pDrvRaidMap->totalSize = sizeof(struct MR_FW_RAID_MAP_EXT); + pDrvRaidMap->totalSize = + cpu_to_le32(sizeof(struct MR_FW_RAID_MAP_EXT)); } else { fw_map_old = (struct MR_FW_RAID_MAP_ALL *) fusion->ld_map[(instance->map_id & 1)]; pFwRaidMap = &fw_map_old->raidMap; #if VD_EXT_DEBUG - for (i = 0; i < pFwRaidMap->ldCount; i++) { + for (i = 0; i < le16_to_cpu(pFwRaidMap->ldCount); i++) { dev_dbg(&instance->pdev->dev, "(%d) :Index 0x%x " "Target Id 0x%x Seq Num 0x%x Size 0/%llx\n", instance->unique_id, i, @@ -202,12 +205,12 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) memset(drv_map, 0, fusion->drv_map_sz); pDrvRaidMap->totalSize = pFwRaidMap->totalSize; - pDrvRaidMap->ldCount = pFwRaidMap->ldCount; + pDrvRaidMap->ldCount = (__le16)pFwRaidMap->ldCount; pDrvRaidMap->fpPdIoTimeoutSec = pFwRaidMap->fpPdIoTimeoutSec; for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++) pDrvRaidMap->ldTgtIdToLd[i] = (u8)pFwRaidMap->ldTgtIdToLd[i]; - for (i = 0; i < pDrvRaidMap->ldCount; i++) { + for (i = 0; i < le16_to_cpu(pDrvRaidMap->ldCount); i++) { pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i]; #if VD_EXT_DEBUG dev_dbg(&instance->pdev->dev, @@ -268,7 +271,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance) else expected_size = (sizeof(struct MR_FW_RAID_MAP) - sizeof(struct MR_LD_SPAN_MAP) + - (sizeof(struct MR_LD_SPAN_MAP) * le32_to_cpu(pDrvRaidMap->ldCount))); + (sizeof(struct MR_LD_SPAN_MAP) * le16_to_cpu(pDrvRaidMap->ldCount))); if (le32_to_cpu(pDrvRaidMap->totalSize) != expected_size) { dev_err(&instance->pdev->dev, "map info structure size 0x%x is not matching with ld count\n", @@ -284,7 +287,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance) mr_update_load_balance_params(drv_map, lbInfo); - num_lds = le32_to_cpu(drv_map->raidMap.ldCount); + num_lds = le16_to_cpu(drv_map->raidMap.ldCount); /*Convert Raid capability values to CPU arch */ for (ldCount = 0; ldCount < num_lds; ldCount++) { @@ -457,7 +460,7 @@ u32 mr_spanset_get_span_block(struct megasas_instance *instance, quad = &map->raidMap.ldSpanMap[ld]. spanBlock[span]. block_span_info.quad[info]; - if (le32_to_cpu(quad->diff == 0)) + if (le32_to_cpu(quad->diff) == 0) return SPAN_INVALID; if (le64_to_cpu(quad->logStart) <= row && row <= le64_to_cpu(quad->logEnd) && @@ -520,7 +523,7 @@ static u64 get_row_from_strip(struct megasas_instance *instance, span_set->span_row_data_width) * span_set->diff; for (span = 0, span_offset = 0; span < raid->spanDepth; span++) if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span]. - block_span_info.noElements >= info+1)) { + block_span_info.noElements) >= info+1) { if (strip_offset >= span_set->strip_offset[span]) span_offset++; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index f37eed682c75..71557f64bb5e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1,7 +1,8 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2012 LSI Corporation. + * Copyright (c) 2009-2013 LSI Corporation + * Copyright (c) 2013-2014 Avago Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,19 +15,20 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * * FILE: megaraid_sas_fusion.c * - * Authors: LSI Corporation + * Authors: Avago Technologies * Sumant Patro - * Adam Radford <linuxraid@lsi.com> + * Adam Radford + * Kashyap Desai <kashyap.desai@avagotech.com> + * Sumit Saxena <sumit.saxena@avagotech.com> * - * Send feedback to: <megaraidlinux@lsi.com> + * Send feedback to: megaraidlinux.pdl@avagotech.com * - * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 - * ATTN: Linuxraid + * Mail to: Avago Technologies, 350 West Trimble Road, Building 90, + * San Jose, California 95131 */ #include <linux/kernel.h> @@ -880,7 +882,7 @@ megasas_sync_map_info(struct megasas_instance *instance) map = fusion->ld_drv_map[instance->map_id & 1]; - num_lds = le32_to_cpu(map->raidMap.ldCount); + num_lds = le16_to_cpu(map->raidMap.ldCount); dcmd = &cmd->frame->dcmd; @@ -1065,48 +1067,16 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) goto fail_ioc_init; megasas_display_intel_branding(instance); - if (megasas_get_ctrl_info(instance, instance->ctrl_info)) { + if (megasas_get_ctrl_info(instance)) { dev_err(&instance->pdev->dev, "Could not get controller info. Fail from %s %d\n", __func__, __LINE__); goto fail_ioc_init; } - instance->supportmax256vd = - instance->ctrl_info->adapterOperations3.supportMaxExtLDs; - /* Below is additional check to address future FW enhancement */ - if (instance->ctrl_info->max_lds > 64) - instance->supportmax256vd = 1; - instance->drv_supported_vd_count = MEGASAS_MAX_LD_CHANNELS - * MEGASAS_MAX_DEV_PER_CHANNEL; - instance->drv_supported_pd_count = MEGASAS_MAX_PD_CHANNELS - * MEGASAS_MAX_DEV_PER_CHANNEL; - if (instance->supportmax256vd) { - instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES_EXT; - instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; - } else { - instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES; - instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; - } - dev_info(&instance->pdev->dev, "Firmware supports %d VDs %d PDs\n" - "Driver supports %d VDs %d PDs\n", - instance->fw_supported_vd_count, - instance->fw_supported_pd_count, - instance->drv_supported_vd_count, - instance->drv_supported_pd_count); - instance->flag_ieee = 1; fusion->fast_path_io = 0; - fusion->old_map_sz = - sizeof(struct MR_FW_RAID_MAP) + (sizeof(struct MR_LD_SPAN_MAP) * - (instance->fw_supported_vd_count - 1)); - fusion->new_map_sz = - sizeof(struct MR_FW_RAID_MAP_EXT); - fusion->drv_map_sz = - sizeof(struct MR_DRV_RAID_MAP) + (sizeof(struct MR_LD_SPAN_MAP) * - (instance->drv_supported_vd_count - 1)); - fusion->drv_map_pages = get_order(fusion->drv_map_sz); for (i = 0; i < 2; i++) { fusion->ld_map[i] = NULL; @@ -1121,16 +1091,10 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) fusion->drv_map_pages); goto fail_ioc_init; } + memset(fusion->ld_drv_map[i], 0, + ((1 << PAGE_SHIFT) << fusion->drv_map_pages)); } - fusion->max_map_sz = max(fusion->old_map_sz, fusion->new_map_sz); - - if (instance->supportmax256vd) - fusion->current_map_sz = fusion->new_map_sz; - else - fusion->current_map_sz = fusion->old_map_sz; - - for (i = 0; i < 2; i++) { fusion->ld_map[i] = dma_alloc_coherent(&instance->pdev->dev, fusion->max_map_sz, @@ -1173,9 +1137,10 @@ megasas_fire_cmd_fusion(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { #if defined(writeq) && defined(CONFIG_64BIT) - u64 req_data = (((u64)req_desc_hi << 32) | (u32)req_desc_lo); + u64 req_data = (((u64)le32_to_cpu(req_desc_hi) << 32) | + le32_to_cpu(req_desc_lo)); - writeq(le64_to_cpu(req_data), &(regs)->inbound_low_queue_port); + writeq(req_data, &(regs)->inbound_low_queue_port); #else unsigned long flags; @@ -1373,7 +1338,7 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len, /* Logical block reference tag */ io_request->CDB.EEDP32.PrimaryReferenceTag = cpu_to_be32(ref_tag); - io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0xffff; + io_request->CDB.EEDP32.PrimaryApplicationTagMask = cpu_to_be16(0xffff); io_request->IoFlags = cpu_to_le16(32); /* Specify 32-byte cdb */ /* Transfer length */ @@ -1769,7 +1734,7 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, /* set RAID context values */ pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ; - pRAID_Context->timeoutValue = raid->fpIoTimeoutForLd; + pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd); pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); pRAID_Context->regLockRowLBA = 0; pRAID_Context->regLockLength = 0; @@ -2254,7 +2219,7 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance, * megasas_complete_cmd */ - if (frame_hdr->flags & MFI_FRAME_DONT_POST_IN_REPLY_QUEUE) + if (frame_hdr->flags & cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE)) cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; fusion = instance->ctrl_context; @@ -2385,6 +2350,8 @@ megasas_alloc_host_crash_buffer(struct megasas_instance *instance) "memory allocation failed at index %d\n", i); break; } + memset(instance->crash_buf[i], 0, + ((1 << PAGE_SHIFT) << instance->crash_buf_pages)); } instance->drv_buf_alloc = i; } @@ -2837,11 +2804,15 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) } } - clear_bit(MEGASAS_FUSION_IN_RESET, - &instance->reset_flags); - instance->instancet->enable_intr(instance); - instance->adprecovery = MEGASAS_HBA_OPERATIONAL; - + if (megasas_get_ctrl_info(instance)) { + dev_info(&instance->pdev->dev, + "Failed from %s %d\n", + __func__, __LINE__); + instance->adprecovery = + MEGASAS_HW_CRITICAL_ERROR; + megaraid_sas_kill_hba(instance); + retval = FAILED; + } /* Reset load balance info */ memset(fusion->load_balance_info, 0, sizeof(struct LD_LOAD_BALANCE_INFO) @@ -2850,6 +2821,11 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) if (!megasas_get_map_info(instance)) megasas_sync_map_info(instance); + clear_bit(MEGASAS_FUSION_IN_RESET, + &instance->reset_flags); + instance->instancet->enable_intr(instance); + instance->adprecovery = MEGASAS_HBA_OPERATIONAL; + /* Restart SR-IOV heartbeat */ if (instance->requestorId) { if (!megasas_sriov_start_heartbeat(instance, 0)) @@ -2866,14 +2842,14 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) "successful for scsi%d.\n", instance->host->host_no); - if (instance->crash_dump_drv_support) { - if (instance->crash_dump_app_support) - megasas_set_crash_dump_params(instance, - MR_CRASH_BUF_TURN_ON); - else - megasas_set_crash_dump_params(instance, - MR_CRASH_BUF_TURN_OFF); - } + if (instance->crash_dump_drv_support && + instance->crash_dump_app_support) + megasas_set_crash_dump_params(instance, + MR_CRASH_BUF_TURN_ON); + else + megasas_set_crash_dump_params(instance, + MR_CRASH_BUF_TURN_OFF); + retval = SUCCESS; goto out; } diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 0d183d521bdd..5ab7daee11be 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -1,7 +1,8 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2012 LSI Corporation. + * Copyright (c) 2009-2013 LSI Corporation + * Copyright (c) 2013-2014 Avago Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,19 +15,20 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * * FILE: megaraid_sas_fusion.h * - * Authors: LSI Corporation + * Authors: Avago Technologies * Manoj Jose * Sumant Patro + * Kashyap Desai <kashyap.desai@avagotech.com> + * Sumit Saxena <sumit.saxena@avagotech.com> * - * Send feedback to: <megaraidlinux@lsi.com> + * Send feedback to: megaraidlinux.pdl@avagotech.com * - * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 - * ATTN: Linuxraid + * Mail to: Avago Technologies, 350 West Trimble Road, Building 90, + * San Jose, California 95131 */ #ifndef _MEGARAID_SAS_FUSION_H_ @@ -834,8 +836,6 @@ struct fusion_context { u32 max_map_sz; u32 current_map_sz; - u32 old_map_sz; - u32 new_map_sz; u32 drv_map_sz; u32 drv_map_pages; u8 fast_path_io; diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index c80ed0482649..8431eb10bbb1 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -1179,15 +1179,14 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, } /** - * _scsih_adjust_queue_depth - setting device queue depth + * _scsih_change_queue_depth - setting device queue depth * @sdev: scsi device struct * @qdepth: requested queue depth * - * - * Returns nothing + * Returns queue depth. */ -static void -_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) +static int +_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; int max_depth; @@ -1217,63 +1216,11 @@ _scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) spin_unlock_irqrestore(&ioc->sas_device_lock, flags); not_sata: - if (!sdev->tagged_supported) max_depth = 1; if (qdepth > max_depth) qdepth = max_depth; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); -} - -/** - * _scsih_change_queue_depth - setting device queue depth - * @sdev: scsi device struct - * @qdepth: requested queue depth - * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP - * (see include/scsi/scsi_host.h for definition) - * - * Returns queue depth. - */ -static int -_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) -{ - if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) - _scsih_adjust_queue_depth(sdev, qdepth); - else if (reason == SCSI_QDEPTH_QFULL) - scsi_track_queue_full(sdev, qdepth); - else - return -EOPNOTSUPP; - - if (sdev->inquiry_len > 7) - sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " - "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n", - sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags, - sdev->ordered_tags, sdev->scsi_level, - (sdev->inquiry[7] & 2) >> 1); - - return sdev->queue_depth; -} - -/** - * _scsih_change_queue_type - changing device queue tag type - * @sdev: scsi device struct - * @tag_type: requested tag type - * - * Returns queue tag type. - */ -static int -_scsih_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; + return scsi_change_queue_depth(sdev, qdepth); } /** @@ -2104,7 +2051,7 @@ _scsih_slave_configure(struct scsi_device *sdev) r_level, raid_device->handle, (unsigned long long)raid_device->wwid, raid_device->num_pds, ds); - _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); + _scsih_change_queue_depth(sdev, qdepth); /* raid transport support */ if (!ioc->is_warpdrive) _scsih_set_level(sdev, raid_device->volume_type); @@ -2169,7 +2116,7 @@ _scsih_slave_configure(struct scsi_device *sdev) _scsih_display_sata_capabilities(ioc, handle, sdev); - _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); + _scsih_change_queue_depth(sdev, qdepth); if (ssp_target) { sas_read_port_mode_page(sdev); @@ -3966,16 +3913,8 @@ _scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; /* set tags */ - if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) { - if (scmd->device->tagged_supported) { - if (scmd->device->ordered_tags) - mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; - else - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - } else - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - } else - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; + mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; + /* Make sure Device is not raid volume. * We do not expose raid functionality to upper layer for warpdrive. */ @@ -7653,7 +7592,7 @@ static struct scsi_host_template scsih_driver_template = { .scan_finished = _scsih_scan_finished, .scan_start = _scsih_scan_start, .change_queue_depth = _scsih_change_queue_depth, - .change_queue_type = _scsih_change_queue_type, + .change_queue_type = scsi_change_queue_type, .eh_abort_handler = _scsih_abort, .eh_device_reset_handler = _scsih_dev_reset, .eh_target_reset_handler = _scsih_target_reset, @@ -7667,6 +7606,7 @@ static struct scsi_host_template scsih_driver_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = mpt2sas_host_attrs, .sdev_attrs = mpt2sas_dev_attrs, + .track_queue_depth = 1, }; /** diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 857276b8880f..a2b60991efd4 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1053,9 +1053,15 @@ _scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, return found; } - -static void -_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) +/** + * _scsih_change_queue_depth - setting device queue depth + * @sdev: scsi device struct + * @qdepth: requested queue depth + * + * Returns queue depth. + */ +static int +_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; int max_depth; @@ -1090,62 +1096,10 @@ _scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) max_depth = 1; if (qdepth > max_depth) qdepth = max_depth; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + return scsi_change_queue_depth(sdev, qdepth); } /** - * _scsih_change_queue_depth - setting device queue depth - * @sdev: scsi device struct - * @qdepth: requested queue depth - * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP - * (see include/scsi/scsi_host.h for definition) - * - * Returns queue depth. - */ -static int -_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) -{ - if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) - _scsih_adjust_queue_depth(sdev, qdepth); - else if (reason == SCSI_QDEPTH_QFULL) - scsi_track_queue_full(sdev, qdepth); - else - return -EOPNOTSUPP; - - if (sdev->inquiry_len > 7) - sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " \ - "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n", - sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags, - sdev->ordered_tags, sdev->scsi_level, - (sdev->inquiry[7] & 2) >> 1); - - return sdev->queue_depth; -} - -/** - * _scsih_change_queue_type - changing device queue tag type - * @sdev: scsi device struct - * @tag_type: requested tag type - * - * Returns queue tag type. - */ -static int -_scsih_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; -} - - -/** * _scsih_target_alloc - target add routine * @starget: scsi target struct * @@ -1762,7 +1716,7 @@ _scsih_slave_configure(struct scsi_device *sdev) raid_device->num_pds, ds); - _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); + _scsih_change_queue_depth(sdev, qdepth); /* raid transport support */ _scsih_set_level(sdev, raid_device->volume_type); @@ -1828,7 +1782,7 @@ _scsih_slave_configure(struct scsi_device *sdev) _scsih_display_sata_capabilities(ioc, handle, sdev); - _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); + _scsih_change_queue_depth(sdev, qdepth); if (ssp_target) { sas_read_port_mode_page(sdev); @@ -3586,16 +3540,7 @@ _scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; /* set tags */ - if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) { - if (scmd->device->tagged_supported) { - if (scmd->device->ordered_tags) - mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; - else - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - } else - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - } else - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; + mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) && scmd->cmd_len != 32) @@ -7284,7 +7229,7 @@ static struct scsi_host_template scsih_driver_template = { .scan_finished = _scsih_scan_finished, .scan_start = _scsih_scan_start, .change_queue_depth = _scsih_change_queue_depth, - .change_queue_type = _scsih_change_queue_type, + .change_queue_type = scsi_change_queue_type, .eh_abort_handler = _scsih_abort, .eh_device_reset_handler = _scsih_dev_reset, .eh_target_reset_handler = _scsih_target_reset, @@ -7298,6 +7243,7 @@ static struct scsi_host_template scsih_driver_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = mpt3sas_host_attrs, .sdev_attrs = mpt3sas_dev_attrs, + .track_queue_depth = 1, }; /** diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index eacee48a955c..f15df3de6790 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -26,18 +26,9 @@ #include "mv_sas.h" -static int lldd_max_execute_num = 1; -module_param_named(collector, lldd_max_execute_num, int, S_IRUGO); -MODULE_PARM_DESC(collector, "\n" - "\tIf greater than one, tells the SAS Layer to run in Task Collector\n" - "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n" - "\tThe mvsas SAS LLDD supports both modes.\n" - "\tDefault: 1 (Direct Mode).\n"); - int interrupt_coalescing = 0x80; static struct scsi_transport_template *mvs_stt; -struct kmem_cache *mvs_task_list_cache; static const struct mvs_chip_info mvs_chips[] = { [chip_6320] = { 1, 2, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, }, [chip_6440] = { 1, 4, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, }, @@ -76,6 +67,8 @@ static struct scsi_host_template mvs_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = mvst_host_attrs, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static struct sas_domain_function_template mvs_transport_ops = { @@ -511,14 +504,11 @@ static void mvs_post_sas_ha_init(struct Scsi_Host *shost, sha->num_phys = nr_core * chip_info->n_phy; - sha->lldd_max_execute_num = lldd_max_execute_num; - if (mvi->flags & MVF_FLAG_SOC) can_queue = MVS_SOC_CAN_QUEUE; else can_queue = MVS_CHIP_SLOT_SZ; - sha->lldd_queue_size = can_queue; shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG); shost->can_queue = can_queue; mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE; @@ -831,16 +821,7 @@ static int __init mvs_init(void) if (!mvs_stt) return -ENOMEM; - mvs_task_list_cache = kmem_cache_create("mvs_task_list", sizeof(struct mvs_task_list), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!mvs_task_list_cache) { - rc = -ENOMEM; - mv_printk("%s: mvs_task_list_cache alloc failed! \n", __func__); - goto err_out; - } - rc = pci_register_driver(&mvs_pci_driver); - if (rc) goto err_out; @@ -855,7 +836,6 @@ static void __exit mvs_exit(void) { pci_unregister_driver(&mvs_pci_driver); sas_release_transport(mvs_stt); - kmem_cache_destroy(mvs_task_list_cache); } struct device_attribute *mvst_host_attrs[] = { diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index ac52f7c99513..85d86a5cdb60 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -852,43 +852,7 @@ prep_out: return rc; } -static struct mvs_task_list *mvs_task_alloc_list(int *num, gfp_t gfp_flags) -{ - struct mvs_task_list *first = NULL; - - for (; *num > 0; --*num) { - struct mvs_task_list *mvs_list = kmem_cache_zalloc(mvs_task_list_cache, gfp_flags); - - if (!mvs_list) - break; - - INIT_LIST_HEAD(&mvs_list->list); - if (!first) - first = mvs_list; - else - list_add_tail(&mvs_list->list, &first->list); - - } - - return first; -} - -static inline void mvs_task_free_list(struct mvs_task_list *mvs_list) -{ - LIST_HEAD(list); - struct list_head *pos, *a; - struct mvs_task_list *mlist = NULL; - - __list_add(&list, mvs_list->list.prev, &mvs_list->list); - - list_for_each_safe(pos, a, &list) { - list_del_init(pos); - mlist = list_entry(pos, struct mvs_task_list, list); - kmem_cache_free(mvs_task_list_cache, mlist); - } -} - -static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, +static int mvs_task_exec(struct sas_task *task, gfp_t gfp_flags, struct completion *completion, int is_tmf, struct mvs_tmf_task *tmf) { @@ -912,74 +876,9 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, return rc; } -static int mvs_collector_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, - struct completion *completion, int is_tmf, - struct mvs_tmf_task *tmf) +int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags) { - struct domain_device *dev = task->dev; - struct mvs_prv_info *mpi = dev->port->ha->lldd_ha; - struct mvs_info *mvi = NULL; - struct sas_task *t = task; - struct mvs_task_list *mvs_list = NULL, *a; - LIST_HEAD(q); - int pass[2] = {0}; - u32 rc = 0; - u32 n = num; - unsigned long flags = 0; - - mvs_list = mvs_task_alloc_list(&n, gfp_flags); - if (n) { - printk(KERN_ERR "%s: mvs alloc list failed.\n", __func__); - rc = -ENOMEM; - goto free_list; - } - - __list_add(&q, mvs_list->list.prev, &mvs_list->list); - - list_for_each_entry(a, &q, list) { - a->task = t; - t = list_entry(t->list.next, struct sas_task, list); - } - - list_for_each_entry(a, &q , list) { - - t = a->task; - mvi = ((struct mvs_device *)t->dev->lldd_dev)->mvi_info; - - spin_lock_irqsave(&mvi->lock, flags); - rc = mvs_task_prep(t, mvi, is_tmf, tmf, &pass[mvi->id]); - if (rc) - dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc); - spin_unlock_irqrestore(&mvi->lock, flags); - } - - if (likely(pass[0])) - MVS_CHIP_DISP->start_delivery(mpi->mvi[0], - (mpi->mvi[0]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1)); - - if (likely(pass[1])) - MVS_CHIP_DISP->start_delivery(mpi->mvi[1], - (mpi->mvi[1]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1)); - - list_del_init(&q); - -free_list: - if (mvs_list) - mvs_task_free_list(mvs_list); - - return rc; -} - -int mvs_queue_command(struct sas_task *task, const int num, - gfp_t gfp_flags) -{ - struct mvs_device *mvi_dev = task->dev->lldd_dev; - struct sas_ha_struct *sas = mvi_dev->mvi_info->sas; - - if (sas->lldd_max_execute_num < 2) - return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL); - else - return mvs_collector_task_exec(task, num, gfp_flags, NULL, 0, NULL); + return mvs_task_exec(task, gfp_flags, NULL, 0, NULL); } static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc) @@ -1411,7 +1310,7 @@ static int mvs_exec_internal_tmf_task(struct domain_device *dev, task->slow_task->timer.expires = jiffies + MVS_TASK_TIMEOUT*HZ; add_timer(&task->slow_task->timer); - res = mvs_task_exec(task, 1, GFP_KERNEL, NULL, 1, tmf); + res = mvs_task_exec(task, GFP_KERNEL, NULL, 1, tmf); if (res) { del_timer(&task->slow_task->timer); diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index d6b19dc80bee..dc409c04747a 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -65,7 +65,6 @@ extern struct mvs_tgt_initiator mvs_tgt; extern struct mvs_info *tgt_mvi; extern const struct mvs_dispatch mvs_64xx_dispatch; extern const struct mvs_dispatch mvs_94xx_dispatch; -extern struct kmem_cache *mvs_task_list_cache; #define DEV_IS_EXPANDER(type) \ ((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE)) @@ -440,12 +439,6 @@ struct mvs_task_exec_info { int n_elem; }; -struct mvs_task_list { - struct sas_task *task; - struct list_head list; -}; - - /******************** function prototype *********************/ void mvs_get_sas_addr(void *buf, u32 buflen); void mvs_tag_clear(struct mvs_info *mvi, u32 tag); @@ -462,8 +455,7 @@ void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo, u32 off_hi, u64 sas_addr); void mvs_scan_start(struct Scsi_Host *shost); int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time); -int mvs_queue_command(struct sas_task *task, const int num, - gfp_t gfp_flags); +int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags); int mvs_abort_task(struct sas_task *task); int mvs_abort_task_set(struct domain_device *dev, u8 *lun); int mvs_clear_aca(struct domain_device *dev, u8 *lun); diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index a7305ffc359d..5b93ed810f6e 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -7997,10 +7997,7 @@ static int ncr53c8xx_slave_configure(struct scsi_device *device) if (depth_to_use > MAX_TAGS) depth_to_use = MAX_TAGS; - scsi_adjust_queue_depth(device, - (device->tagged_supported ? - MSG_SIMPLE_TAG : 0), - depth_to_use); + scsi_change_queue_depth(device, depth_to_use); /* ** Since the queue depth is not tunable under Linux, diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index 92cdd4b06526..243eab3d10d0 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c @@ -540,9 +540,9 @@ static int osd_remove(struct device *dev) */ static struct scsi_driver osd_driver = { - .owner = THIS_MODULE, .gendrv = { .name = osd_name, + .owner = THIS_MODULE, .probe = osd_probe, .remove = osd_remove, } diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index dff37a250d79..5033223f6287 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -172,9 +172,9 @@ static int osst_probe(struct device *); static int osst_remove(struct device *); static struct scsi_driver osst_template = { - .owner = THIS_MODULE, .gendrv = { .name = "osst", + .owner = THIS_MODULE, .probe = osst_probe, .remove = osst_remove, } @@ -259,9 +259,10 @@ static int osst_chk_result(struct osst_tape * STp, struct osst_request * SRpnt) SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2], SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]); if (scode) printk(OSST_DEB_MSG "%s:D: Sense: %02x, ASC: %02x, ASCQ: %02x\n", - name, scode, sense[12], sense[13]); + name, scode, sense[12], sense[13]); if (cmdstatp->have_sense) - __scsi_print_sense("osst ", SRpnt->sense, SCSI_SENSE_BUFFERSIZE); + __scsi_print_sense(STp->device, name, + SRpnt->sense, SCSI_SENSE_BUFFERSIZE); } else #endif @@ -275,7 +276,8 @@ static int osst_chk_result(struct osst_tape * STp, struct osst_request * SRpnt) SRpnt->cmd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ if (cmdstatp->have_sense) { printk(KERN_WARNING "%s:W: Command with sense data:\n", name); - __scsi_print_sense("osst ", SRpnt->sense, SCSI_SENSE_BUFFERSIZE); + __scsi_print_sense(STp->device, name, + SRpnt->sense, SCSI_SENSE_BUFFERSIZE); } else { static int notyetprinted = 1; @@ -3325,19 +3327,18 @@ static int osst_write_frame(struct osst_tape * STp, struct osst_request ** aSRpn /* Lock or unlock the drive door. Don't use when struct osst_request allocated. */ static int do_door_lock(struct osst_tape * STp, int do_lock) { - int retval, cmd; + int retval; - cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK; #if DEBUG printk(OSST_DEB_MSG "%s:D: %socking drive door.\n", tape_name(STp), do_lock ? "L" : "Unl"); #endif - retval = scsi_ioctl(STp->device, cmd, NULL); - if (!retval) { + + retval = scsi_set_medium_removal(STp->device, + do_lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW); + if (!retval) STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED; - } - else { + else STp->door_locked = ST_LOCK_FAILS; - } return retval; } @@ -4967,10 +4968,10 @@ static long osst_ioctl(struct file * file, * may try and take the device offline, in which case all further * access to the device is prohibited. */ - if( !scsi_block_when_processing_errors(STp->device) ) { - retval = (-ENXIO); + retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in, + file->f_flags & O_NDELAY); + if (retval) goto out; - } cmd_type = _IOC_TYPE(cmd_in); cmd_nr = _IOC_NR(cmd_in); diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c index 80bacb5dc1d4..e81eadd08afc 100644 --- a/drivers/scsi/pas16.c +++ b/drivers/scsi/pas16.c @@ -1,6 +1,4 @@ -#define AUTOSENSE #define PSEUDO_DMA -#define FOO #define UNSAFE /* Not unsafe for PAS16 -- use it */ #define PDEBUG 0 @@ -24,47 +22,9 @@ * Media Vision * (510) 770-8600 * (800) 348-7116 - * - * and - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ /* - * Options : - * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically - * for commands that return with a CHECK CONDITION status. - * - * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512 - * bytes at a time. Since interrupts are disabled by default during - * these transfers, we might need this to give reasonable interrupt - * service time if the transfer size gets too large. - * - * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance - * increase compared to polled I/O. - * - * PARITY - enable parity checking. Not supported. - * - * SCSI2 - enable support for SCSI-II tagged queueing. Untested. - * - * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This - * parameter comes from the NCR5380 code. It is NOT unsafe with - * the PAS16 and you should use it. If you don't you will have - * a problem with dropped characters during high speed - * communications during SCSI transfers. If you really don't - * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or - * twiddle with the transfer size in the high level code. - * - * USLEEP - enable support for devices that don't disconnect. Untested. - * * The card is detected and initialized in one of several ways : * 1. Autoprobe (default) - There are many different models of * the Pro Audio Spectrum/Studio 16, and I only have one of @@ -102,13 +62,11 @@ * If you have problems with your card not being recognized, use * the LILO command line override. Try to get it recognized without * interrupts. Ie, for a board at the default 0x388 base port, - * boot: linux pas16=0x388,255 + * boot: linux pas16=0x388,0 * - * SCSI_IRQ_NONE (255) should be specified for no interrupt, + * NO_IRQ (0) should be specified for no interrupt, * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden * on the command line. - * - * (IRQ_AUTO == 254, SCSI_IRQ_NONE == 255 in NCR5380.h) */ #include <linux/module.h> @@ -123,15 +81,12 @@ #include <linux/stat.h> #include <linux/init.h> -#include "scsi.h" #include <scsi/scsi_host.h> #include "pas16.h" #define AUTOPROBE_IRQ #include "NCR5380.h" -static int pas_maxi = 0; -static int pas_wmaxi = 0; static unsigned short pas16_addr = 0; static int pas16_irq = 0; @@ -337,6 +292,7 @@ static int __init } +#ifndef MODULE /* * Function : pas16_setup(char *str, int *ints) * @@ -347,10 +303,13 @@ static int __init * */ -void __init pas16_setup(char *str, int *ints) +static int __init pas16_setup(char *str) { static int commandline_current = 0; int i; + int ints[10]; + + get_options(str, ARRAY_SIZE(ints), ints); if (ints[0] != 2) printk("pas16_setup : usage pas16=io_port,irq\n"); else @@ -364,8 +323,12 @@ void __init pas16_setup(char *str, int *ints) } ++commandline_current; } + return 1; } +__setup("pas16=", pas16_setup); +#endif + /* * Function : int pas16_detect(struct scsi_host_template * tpnt) * @@ -379,7 +342,7 @@ void __init pas16_setup(char *str, int *ints) * */ -int __init pas16_detect(struct scsi_host_template * tpnt) +static int __init pas16_detect(struct scsi_host_template *tpnt) { static int current_override = 0; static unsigned short current_base = 0; @@ -387,10 +350,6 @@ int __init pas16_detect(struct scsi_host_template * tpnt) unsigned short io_port; int count; - tpnt->proc_name = "pas16"; - tpnt->show_info = pas16_show_info; - tpnt->write_info = pas16_write_info; - if (pas16_addr != 0) { overrides[0].io_port = pas16_addr; /* @@ -452,15 +411,19 @@ int __init pas16_detect(struct scsi_host_template * tpnt) else instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS); - if (instance->irq != SCSI_IRQ_NONE) + /* Compatibility with documented NCR5380 kernel parameters */ + if (instance->irq == 255) + instance->irq = NO_IRQ; + + if (instance->irq != NO_IRQ) if (request_irq(instance->irq, pas16_intr, 0, "pas16", instance)) { printk("scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); - instance->irq = SCSI_IRQ_NONE; + instance->irq = NO_IRQ; } - if (instance->irq == SCSI_IRQ_NONE) { + if (instance->irq == NO_IRQ) { printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); /* Disable 5380 interrupts, leave drive params the same */ @@ -472,17 +435,6 @@ int __init pas16_detect(struct scsi_host_template * tpnt) printk("scsi%d : irq = %d\n", instance->host_no, instance->irq); #endif - printk("scsi%d : at 0x%04x", instance->host_no, (int) - instance->io_port); - if (instance->irq == SCSI_IRQ_NONE) - printk (" interrupts disabled"); - else - printk (" irq %d", instance->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE); - NCR5380_print_options(instance); - printk("\n"); - ++current_override; ++count; } @@ -509,8 +461,8 @@ int __init pas16_detect(struct scsi_host_template * tpnt) * and matching the H_C_S coordinates to what DOS uses. */ -int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev, - sector_t capacity, int * ip) +static int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev, + sector_t capacity, int *ip) { int size = capacity; ip[0] = 64; @@ -547,6 +499,7 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, P_DATA_REG_OFFSET); register int i = len; int ii = 0; + struct NCR5380_hostdata *hostdata = shost_priv(instance); while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) ) ++ii; @@ -559,8 +512,8 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, instance->host_no); return -1; } - if (ii > pas_maxi) - pas_maxi = ii; + if (ii > hostdata->spin_max_r) + hostdata->spin_max_r = ii; return 0; } @@ -583,6 +536,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET); register int i = len; int ii = 0; + struct NCR5380_hostdata *hostdata = shost_priv(instance); while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) ) ++ii; @@ -595,8 +549,8 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src instance->host_no); return -1; } - if (ii > pas_maxi) - pas_wmaxi = ii; + if (ii > hostdata->spin_max_w) + hostdata->spin_max_w = ii; return 0; } @@ -604,7 +558,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src static int pas16_release(struct Scsi_Host *shost) { - if (shost->irq) + if (shost->irq != NO_IRQ) free_irq(shost->irq, shost); NCR5380_exit(shost); if (shost->io_port && shost->n_io_port) @@ -617,6 +571,10 @@ static struct scsi_host_template driver_template = { .name = "Pro Audio Spectrum-16 SCSI", .detect = pas16_detect, .release = pas16_release, + .proc_name = "pas16", + .show_info = pas16_show_info, + .write_info = pas16_write_info, + .info = pas16_info, .queuecommand = pas16_queue_command, .eh_abort_handler = pas16_abort, .eh_bus_reset_handler = pas16_bus_reset, diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h index aa528f53c533..c6109c80050b 100644 --- a/drivers/scsi/pas16.h +++ b/drivers/scsi/pas16.h @@ -18,26 +18,12 @@ * Media Vision * (510) 770-8600 * (800) 348-7116 - * - * and - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ #ifndef PAS16_H #define PAS16_H -#define PAS16_PUBLIC_RELEASE 3 - #define PDEBUG_INIT 0x1 #define PDEBUG_TRANSFER 0x2 @@ -114,12 +100,6 @@ #ifndef ASM -static int pas16_abort(Scsi_Cmnd *); -static int pas16_biosparam(struct scsi_device *, struct block_device *, - sector_t, int*); -static int pas16_detect(struct scsi_host_template *); -static int pas16_queue_command(struct Scsi_Host *, struct scsi_cmnd *); -static int pas16_bus_reset(Scsi_Cmnd *); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 @@ -161,6 +141,7 @@ static int pas16_bus_reset(Scsi_Cmnd *); #define NCR5380_queue_command pas16_queue_command #define NCR5380_abort pas16_abort #define NCR5380_bus_reset pas16_bus_reset +#define NCR5380_info pas16_info #define NCR5380_show_info pas16_show_info #define NCR5380_write_info pas16_write_info diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 666bf5af06e2..329aba0083ab 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -89,6 +89,8 @@ static struct scsi_host_template pm8001_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = pm8001_host_attrs, + .use_blk_tags = 1, + .track_queue_depth = 1, }; /** @@ -599,8 +601,6 @@ static void pm8001_post_sas_ha_init(struct Scsi_Host *shost, sha->lldd_module = THIS_MODULE; sha->sas_addr = &pm8001_ha->sas_addr[0]; sha->num_phys = chip_info->n_phy; - sha->lldd_max_execute_num = 1; - sha->lldd_queue_size = PM8001_CAN_QUEUE; sha->core.shost = shost; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 76570e6a547d..b93f289b42b3 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -350,7 +350,7 @@ static int sas_find_local_port_id(struct domain_device *dev) */ #define DEV_IS_GONE(pm8001_dev) \ ((!pm8001_dev || (pm8001_dev->dev_type == SAS_PHY_UNUSED))) -static int pm8001_task_exec(struct sas_task *task, const int num, +static int pm8001_task_exec(struct sas_task *task, gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf) { struct domain_device *dev = task->dev; @@ -360,7 +360,6 @@ static int pm8001_task_exec(struct sas_task *task, const int num, struct sas_task *t = task; struct pm8001_ccb_info *ccb; u32 tag = 0xdeadbeef, rc, n_elem = 0; - u32 n = num; unsigned long flags = 0; if (!dev->port) { @@ -387,18 +386,12 @@ static int pm8001_task_exec(struct sas_task *task, const int num, spin_unlock_irqrestore(&pm8001_ha->lock, flags); t->task_done(t); spin_lock_irqsave(&pm8001_ha->lock, flags); - if (n > 1) - t = list_entry(t->list.next, - struct sas_task, list); continue; } else { struct task_status_struct *ts = &t->task_status; ts->resp = SAS_TASK_UNDELIVERED; ts->stat = SAS_PHY_DOWN; t->task_done(t); - if (n > 1) - t = list_entry(t->list.next, - struct sas_task, list); continue; } } @@ -460,9 +453,7 @@ static int pm8001_task_exec(struct sas_task *task, const int num, t->task_state_flags |= SAS_TASK_AT_INITIATOR; spin_unlock(&t->task_state_lock); pm8001_dev->running_req++; - if (n > 1) - t = list_entry(t->list.next, struct sas_task, list); - } while (--n); + } while (0); rc = 0; goto out_done; @@ -483,14 +474,11 @@ out_done: * pm8001_queue_command - register for upper layer used, all IO commands sent * to HBA are from this interface. * @task: the task to be execute. - * @num: if can_queue great than 1, the task can be queued up. for SMP task, - * we always execute one one time * @gfp_flags: gfp_flags */ -int pm8001_queue_command(struct sas_task *task, const int num, - gfp_t gfp_flags) +int pm8001_queue_command(struct sas_task *task, gfp_t gfp_flags) { - return pm8001_task_exec(task, num, gfp_flags, 0, NULL); + return pm8001_task_exec(task, gfp_flags, 0, NULL); } /** @@ -708,7 +696,7 @@ static int pm8001_exec_internal_tmf_task(struct domain_device *dev, task->slow_task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ; add_timer(&task->slow_task->timer); - res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf); + res = pm8001_task_exec(task, GFP_KERNEL, 1, tmf); if (res) { del_timer(&task->slow_task->timer); diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index f6b2ac59dae4..8dd8b7840f04 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -623,8 +623,7 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata); void pm8001_scan_start(struct Scsi_Host *shost); int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time); -int pm8001_queue_command(struct sas_task *task, const int num, - gfp_t gfp_flags); +int pm8001_queue_command(struct sas_task *task, gfp_t gfp_flags); int pm8001_abort_task(struct sas_task *task); int pm8001_abort_task_set(struct domain_device *dev, u8 *lun); int pm8001_clear_aca(struct domain_device *dev, u8 *lun); diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index bcb64eb1387f..b1b1f66b1ab7 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -249,15 +249,11 @@ static int pmcraid_slave_configure(struct scsi_device *scsi_dev) PMCRAID_VSET_MAX_SECTORS); } - if (scsi_dev->tagged_supported && - (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) { - scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); - scsi_adjust_queue_depth(scsi_dev, MSG_SIMPLE_TAG, - scsi_dev->host->cmd_per_lun); - } else { - scsi_adjust_queue_depth(scsi_dev, 0, - scsi_dev->host->cmd_per_lun); - } + /* + * We never want to report TCQ support for these types of devices. + */ + if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry)) + scsi_dev->tagged_supported = 0; return 0; } @@ -289,55 +285,18 @@ static void pmcraid_slave_destroy(struct scsi_device *scsi_dev) * pmcraid_change_queue_depth - Change the device's queue depth * @scsi_dev: scsi device struct * @depth: depth to set - * @reason: calling context * * Return value * actual depth set */ -static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth, - int reason) +static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth) { - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - if (depth > PMCRAID_MAX_CMD_PER_LUN) depth = PMCRAID_MAX_CMD_PER_LUN; - - scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), depth); - - return scsi_dev->queue_depth; + return scsi_change_queue_depth(scsi_dev, depth); } /** - * pmcraid_change_queue_type - Change the device's queue type - * @scsi_dev: scsi device struct - * @tag: type of tags to use - * - * Return value: - * actual queue type set - */ -static int pmcraid_change_queue_type(struct scsi_device *scsi_dev, int tag) -{ - struct pmcraid_resource_entry *res; - - res = (struct pmcraid_resource_entry *)scsi_dev->hostdata; - - if ((res) && scsi_dev->tagged_supported && - (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) { - scsi_set_tag_type(scsi_dev, tag); - - if (tag) - scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); - else - scsi_deactivate_tcq(scsi_dev, scsi_dev->queue_depth); - } else - tag = 0; - - return tag; -} - - -/** * pmcraid_init_cmdblk - initializes a command block * * @cmd: pointer to struct pmcraid_cmd to be initialized @@ -3175,36 +3134,6 @@ static int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd) } /** - * pmcraid_task_attributes - Translate SPI Q-Tags to task attributes - * @scsi_cmd: scsi command struct - * - * Return value - * number of tags or 0 if the task is not tagged - */ -static u8 pmcraid_task_attributes(struct scsi_cmnd *scsi_cmd) -{ - char tag[2]; - u8 rc = 0; - - if (scsi_populate_tag_msg(scsi_cmd, tag)) { - switch (tag[0]) { - case MSG_SIMPLE_TAG: - rc = TASK_TAG_SIMPLE; - break; - case MSG_HEAD_TAG: - rc = TASK_TAG_QUEUE_HEAD; - break; - case MSG_ORDERED_TAG: - rc = TASK_TAG_ORDERED; - break; - }; - } - - return rc; -} - - -/** * pmcraid_init_ioadls - initializes IOADL related fields in IOARCB * @cmd: pmcraid command struct * @sgcount: count of scatter-gather elements @@ -3559,7 +3488,9 @@ static int pmcraid_queuecommand_lck( } ioarcb->request_flags0 |= NO_LINK_DESCS; - ioarcb->request_flags1 |= pmcraid_task_attributes(scsi_cmd); + + if (scsi_cmd->flags & SCMD_TAGGED) + ioarcb->request_flags1 |= TASK_TAG_SIMPLE; if (RES_IS_GSCSI(res->cfg_entry)) ioarcb->request_flags1 |= DELAY_AFTER_RESET; @@ -4320,7 +4251,7 @@ static struct scsi_host_template pmcraid_host_template = { .slave_configure = pmcraid_slave_configure, .slave_destroy = pmcraid_slave_destroy, .change_queue_depth = pmcraid_change_queue_depth, - .change_queue_type = pmcraid_change_queue_type, + .change_queue_type = scsi_change_queue_type, .can_queue = PMCRAID_MAX_IO_CMD, .this_id = -1, .sg_tablesize = PMCRAID_MAX_IOADLS, @@ -4329,7 +4260,8 @@ static struct scsi_host_template pmcraid_host_template = { .cmd_per_lun = PMCRAID_MAX_CMD_PER_LUN, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = pmcraid_host_attrs, - .proc_name = PMCRAID_DRIVER_NAME + .proc_name = PMCRAID_DRIVER_NAME, + .use_blk_tags = 1, }; /* diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c index ef23fabe3924..b3b48b5a984c 100644 --- a/drivers/scsi/ps3rom.c +++ b/drivers/scsi/ps3rom.c @@ -220,10 +220,6 @@ static int ps3rom_queuecommand_lck(struct scsi_cmnd *cmd, unsigned char opcode; int res; -#ifdef DEBUG - scsi_print_command(cmd); -#endif - priv->curr_cmd = cmd; cmd->scsi_done = done; diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 158020522dfb..c68a66e8cfc1 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -1224,10 +1224,9 @@ qla1280_slave_configure(struct scsi_device *device) if (device->tagged_supported && (ha->bus_settings[bus].qtag_enables & (BIT_0 << target))) { - scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, - ha->bus_settings[bus].hiwat); + scsi_change_queue_depth(device, ha->bus_settings[bus].hiwat); } else { - scsi_adjust_queue_depth(device, 0, default_depth); + scsi_change_queue_depth(device, default_depth); } nv->bus[bus].target[target].parameter.enable_sync = device->sdtr; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index b1865a72ce59..7686bfe9a4a9 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -752,8 +752,6 @@ extern void qla8044_set_idc_dontreset(struct scsi_qla_host *ha); extern int qla8044_rd_direct(struct scsi_qla_host *vha, const uint32_t crb_reg); extern void qla8044_wr_direct(struct scsi_qla_host *vha, const uint32_t crb_reg, const uint32_t value); -extern inline void qla8044_set_qsnt_ready(struct scsi_qla_host *vha); -extern inline void qla8044_need_reset_handler(struct scsi_qla_host *vha); extern int qla8044_device_state_handler(struct scsi_qla_host *vha); extern void qla8044_clear_qsnt_ready(struct scsi_qla_host *vha); extern void qla8044_clear_drv_active(struct qla_hw_data *); diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index f0edb07f3198..a1ab25fca874 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -325,7 +325,6 @@ qla2x00_start_scsi(srb_t *sp) struct qla_hw_data *ha; struct req_que *req; struct rsp_que *rsp; - char tag[2]; /* Setup device pointers. */ ret = 0; @@ -404,26 +403,7 @@ qla2x00_start_scsi(srb_t *sp) /* Set target ID and LUN number*/ SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id); cmd_pkt->lun = cpu_to_le16(cmd->device->lun); - - /* Update tagged queuing modifier */ - if (scsi_populate_tag_msg(cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_HEAD_TAG); - break; - case ORDERED_QUEUE_TAG: - cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_ORDERED_TAG); - break; - default: - cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_SIMPLE_TAG); - break; - } - } else { - cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG); - } + cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG); /* Load SCSI command packet. */ memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len); @@ -1264,7 +1244,6 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, uint16_t fcp_cmnd_len; struct fcp_cmnd *fcp_cmnd; dma_addr_t crc_ctx_dma; - char tag[2]; cmd = GET_CMD_SP(sp); @@ -1356,25 +1335,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, cmd_pkt->fcp_cmnd_dseg_address[1] = cpu_to_le32( MSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF)); fcp_cmnd->task_management = 0; - - /* - * Update tagged queuing modifier if using command tag queuing - */ - if (scsi_populate_tag_msg(cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - fcp_cmnd->task_attribute = TSK_HEAD_OF_QUEUE; - break; - case ORDERED_QUEUE_TAG: - fcp_cmnd->task_attribute = TSK_ORDERED; - break; - default: - fcp_cmnd->task_attribute = TSK_SIMPLE; - break; - } - } else { - fcp_cmnd->task_attribute = TSK_SIMPLE; - } + fcp_cmnd->task_attribute = TSK_SIMPLE; cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */ @@ -1495,7 +1456,6 @@ qla24xx_start_scsi(srb_t *sp) struct scsi_cmnd *cmd = GET_CMD_SP(sp); struct scsi_qla_host *vha = sp->fcport->vha; struct qla_hw_data *ha = vha->hw; - char tag[2]; /* Setup device pointers. */ ret = 0; @@ -1578,22 +1538,7 @@ qla24xx_start_scsi(srb_t *sp) int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); - /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */ - if (scsi_populate_tag_msg(cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - cmd_pkt->task = TSK_HEAD_OF_QUEUE; - break; - case ORDERED_QUEUE_TAG: - cmd_pkt->task = TSK_ORDERED; - break; - default: - cmd_pkt->task = TSK_SIMPLE; - break; - } - } else { - cmd_pkt->task = TSK_SIMPLE; - } + cmd_pkt->task = TSK_SIMPLE; /* Load SCSI command packet. */ memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); @@ -2310,7 +2255,6 @@ qla82xx_start_scsi(srb_t *sp) struct qla_hw_data *ha = vha->hw; struct req_que *req = NULL; struct rsp_que *rsp = NULL; - char tag[2]; /* Setup device pointers. */ ret = 0; @@ -2489,22 +2433,6 @@ sufficient_dsds: else if (cmd->sc_data_direction == DMA_FROM_DEVICE) ctx->fcp_cmnd->additional_cdb_len |= 2; - /* - * Update tagged queuing modifier -- default is TSK_SIMPLE (0). - */ - if (scsi_populate_tag_msg(cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - ctx->fcp_cmnd->task_attribute = - TSK_HEAD_OF_QUEUE; - break; - case ORDERED_QUEUE_TAG: - ctx->fcp_cmnd->task_attribute = - TSK_ORDERED; - break; - } - } - /* Populate the FCP_PRIO. */ if (ha->flags.fcp_prio_enabled) ctx->fcp_cmnd->task_attribute |= @@ -2565,20 +2493,6 @@ sufficient_dsds: host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); - /* - * Update tagged queuing modifier -- default is TSK_SIMPLE (0). - */ - if (scsi_populate_tag_msg(cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - cmd_pkt->task = TSK_HEAD_OF_QUEUE; - break; - case ORDERED_QUEUE_TAG: - cmd_pkt->task = TSK_ORDERED; - break; - } - } - /* Populate the FCP_PRIO. */ if (ha->flags.fcp_prio_enabled) cmd_pkt->task |= sp->fcport->fcp_prio << 3; diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index 80867599527d..6d190b4b82a0 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -3086,7 +3086,6 @@ qlafx00_start_scsi(srb_t *sp) struct cmd_type_7_fx00 *cmd_pkt; struct cmd_type_7_fx00 lcmd_pkt; struct scsi_lun llun; - char tag[2]; /* Setup device pointers. */ ret = 0; @@ -3157,18 +3156,6 @@ qlafx00_start_scsi(srb_t *sp) host_to_adap((uint8_t *)&llun, (uint8_t *)&lcmd_pkt.lun, sizeof(lcmd_pkt.lun)); - /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */ - if (scsi_populate_tag_msg(cmd, tag)) { - switch (tag[0]) { - case HEAD_OF_QUEUE_TAG: - lcmd_pkt.task = TSK_HEAD_OF_QUEUE; - break; - case ORDERED_QUEUE_TAG: - lcmd_pkt.task = TSK_ORDERED; - break; - } - } - /* Load SCSI command packet. */ host_to_adap(cmd->cmnd, lcmd_pkt.fcp_cdb, sizeof(lcmd_pkt.fcp_cdb)); lcmd_pkt.byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c index 24a852828b5d..ed4d6b6b53e3 100644 --- a/drivers/scsi/qla2xxx/qla_nx2.c +++ b/drivers/scsi/qla2xxx/qla_nx2.c @@ -238,7 +238,7 @@ qla8044_rmw_crb_reg(struct scsi_qla_host *vha, return; } -inline void +static inline void qla8044_set_qsnt_ready(struct scsi_qla_host *vha) { uint32_t qsnt_state; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index db3dbd999cb6..6b4d9235368a 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -236,8 +236,6 @@ static int qla2xxx_eh_target_reset(struct scsi_cmnd *); static int qla2xxx_eh_bus_reset(struct scsi_cmnd *); static int qla2xxx_eh_host_reset(struct scsi_cmnd *); -static int qla2x00_change_queue_depth(struct scsi_device *, int, int); -static int qla2x00_change_queue_type(struct scsi_device *, int); static void qla2x00_clear_drv_active(struct qla_hw_data *); static void qla2x00_free_device(scsi_qla_host_t *); static void qla83xx_disable_laser(scsi_qla_host_t *vha); @@ -259,8 +257,8 @@ struct scsi_host_template qla2xxx_driver_template = { .slave_destroy = qla2xxx_slave_destroy, .scan_finished = qla2xxx_scan_finished, .scan_start = qla2xxx_scan_start, - .change_queue_depth = qla2x00_change_queue_depth, - .change_queue_type = qla2x00_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, @@ -270,6 +268,8 @@ struct scsi_host_template qla2xxx_driver_template = { .shost_attrs = qla2x00_host_attrs, .supported_mode = MODE_INITIATOR, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; @@ -1405,10 +1405,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev) if (IS_T10_PI_CAPABLE(vha->hw)) blk_queue_update_dma_alignment(sdev->request_queue, 0x7); - if (sdev->tagged_supported) - scsi_activate_tcq(sdev, req->max_q_depth); - else - scsi_deactivate_tcq(sdev, req->max_q_depth); + scsi_change_queue_depth(sdev, req->max_q_depth); return 0; } @@ -1418,76 +1415,6 @@ qla2xxx_slave_destroy(struct scsi_device *sdev) sdev->hostdata = NULL; } -static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth) -{ - fc_port_t *fcport = (struct fc_port *) sdev->hostdata; - - if (!scsi_track_queue_full(sdev, qdepth)) - return; - - ql_dbg(ql_dbg_io, fcport->vha, 0x3029, - "Queue depth adjusted-down to %d for nexus=%ld:%d:%llu.\n", - sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun); -} - -static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth) -{ - fc_port_t *fcport = sdev->hostdata; - struct scsi_qla_host *vha = fcport->vha; - struct req_que *req = NULL; - - req = vha->req; - if (!req) - return; - - if (req->max_q_depth <= sdev->queue_depth || req->max_q_depth < qdepth) - return; - - if (sdev->ordered_tags) - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, qdepth); - else - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth); - - ql_dbg(ql_dbg_io, vha, 0x302a, - "Queue depth adjusted-up to %d for nexus=%ld:%d:%llu.\n", - sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun); -} - -static int -qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) -{ - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - break; - case SCSI_QDEPTH_QFULL: - qla2x00_handle_queue_full(sdev, qdepth); - break; - case SCSI_QDEPTH_RAMP_UP: - qla2x00_adjust_sdev_qdepth_up(sdev, qdepth); - break; - default: - return -EOPNOTSUPP; - } - - return sdev->queue_depth; -} - -static int -qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; -} - /** * qla2x00_config_dma_addressing() - Configure OS DMA addressing method. * @ha: HA context diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c index 08ab6dac226d..17222eb49762 100644 --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -280,7 +280,6 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb) uint16_t req_cnt; unsigned long flags; uint32_t index; - char tag[2]; /* Get real lun and adapter */ ddb_entry = srb->ddb; @@ -352,15 +351,6 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb) /* Set tagged queueing control flags */ cmd_entry->control_flags |= CF_SIMPLE_TAG; - if (scsi_populate_tag_msg(cmd, tag)) - switch (tag[0]) { - case MSG_HEAD_TAG: - cmd_entry->control_flags |= CF_HEAD_TAG; - break; - case MSG_ORDERED_TAG: - cmd_entry->control_flags |= CF_ORDERED_TAG; - break; - } qla4xxx_advance_req_ring_ptr(ha); qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 199fcf79a051..6d25879d87c8 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -162,12 +162,8 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); static int qla4xxx_slave_alloc(struct scsi_device *device); -static int qla4xxx_slave_configure(struct scsi_device *device); -static void qla4xxx_slave_destroy(struct scsi_device *sdev); static umode_t qla4_attr_is_visible(int param_type, int param); static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type); -static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason); /* * iSCSI Flash DDB sysfs entry points @@ -204,10 +200,8 @@ static struct scsi_host_template qla4xxx_driver_template = { .eh_host_reset_handler = qla4xxx_eh_host_reset, .eh_timed_out = qla4xxx_eh_cmd_timed_out, - .slave_configure = qla4xxx_slave_configure, .slave_alloc = qla4xxx_slave_alloc, - .slave_destroy = qla4xxx_slave_destroy, - .change_queue_depth = qla4xxx_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .this_id = -1, .cmd_per_lun = 3, @@ -218,6 +212,7 @@ static struct scsi_host_template qla4xxx_driver_template = { .shost_attrs = qla4xxx_host_attrs, .host_reset = qla4xxx_host_reset, .vendor_id = SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC, + .use_blk_tags = 1, }; static struct iscsi_transport qla4xxx_iscsi_transport = { @@ -9060,35 +9055,14 @@ static int qla4xxx_slave_alloc(struct scsi_device *sdev) ddb = sess->dd_data; sdev->hostdata = ddb; - sdev->tagged_supported = 1; if (ql4xmaxqdepth != 0 && ql4xmaxqdepth <= 0xffffU) queue_depth = ql4xmaxqdepth; - scsi_activate_tcq(sdev, queue_depth); + scsi_change_queue_depth(sdev, queue_depth); return 0; } -static int qla4xxx_slave_configure(struct scsi_device *sdev) -{ - sdev->tagged_supported = 1; - return 0; -} - -static void qla4xxx_slave_destroy(struct scsi_device *sdev) -{ - scsi_deactivate_tcq(sdev, 1); -} - -static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth, - int reason) -{ - if (!ql4xqfulltracking) - return -EOPNOTSUPP; - - return iscsi_change_queue_depth(sdev, qdepth, reason); -} - /** * qla4xxx_del_from_active_array - returns an active srb * @ha: Pointer to host adapter structure. @@ -9888,6 +9862,9 @@ static int __init qla4xxx_module_init(void) { int ret; + if (ql4xqfulltracking) + qla4xxx_driver_template.track_queue_depth = 1; + /* Allocate cache for SRBs. */ srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(struct srb), 0, SLAB_HWCACHE_ALIGN, NULL); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 79c77b485a67..1ad0c36375b8 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -527,9 +527,9 @@ void scsi_log_send(struct scsi_cmnd *cmd) * * 1: nothing (match completion) * - * 2: log opcode + command of all commands + * 2: log opcode + command of all commands + cmd address * - * 3: same as 2 plus dump cmd address + * 3: same as 2 * * 4: same as 3 plus dump extra junk */ @@ -537,10 +537,8 @@ void scsi_log_send(struct scsi_cmnd *cmd) level = SCSI_LOG_LEVEL(SCSI_LOG_MLQUEUE_SHIFT, SCSI_LOG_MLQUEUE_BITS); if (level > 1) { - scmd_printk(KERN_INFO, cmd, "Send: "); - if (level > 2) - printk("0x%p ", cmd); - printk("\n"); + scmd_printk(KERN_INFO, cmd, + "Send: scmd 0x%p\n", cmd); scsi_print_command(cmd); if (level > 3) { printk(KERN_INFO "buffer = 0x%p, bufflen = %d," @@ -565,7 +563,7 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) * * 2: same as 1 but for all command completions. * - * 3: same as 2 plus dump cmd address + * 3: same as 2 * * 4: same as 3 plus dump extra junk */ @@ -574,39 +572,10 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) SCSI_LOG_MLCOMPLETE_BITS); if (((level > 0) && (cmd->result || disposition != SUCCESS)) || (level > 1)) { - scmd_printk(KERN_INFO, cmd, "Done: "); - if (level > 2) - printk("0x%p ", cmd); - /* - * Dump truncated values, so we usually fit within - * 80 chars. - */ - switch (disposition) { - case SUCCESS: - printk("SUCCESS\n"); - break; - case NEEDS_RETRY: - printk("RETRY\n"); - break; - case ADD_TO_MLQUEUE: - printk("MLQUEUE\n"); - break; - case FAILED: - printk("FAILED\n"); - break; - case TIMEOUT_ERROR: - /* - * If called via scsi_times_out. - */ - printk("TIMEOUT\n"); - break; - default: - printk("UNKNOWN\n"); - } - scsi_print_result(cmd); + scsi_print_result(cmd, "Done: ", disposition); scsi_print_command(cmd); if (status_byte(cmd->result) & CHECK_CONDITION) - scsi_print_sense("", cmd); + scsi_print_sense(cmd); if (level > 3) scmd_printk(KERN_INFO, cmd, "scsi host busy %d failed %d\n", @@ -634,87 +603,6 @@ void scsi_cmd_get_serial(struct Scsi_Host *host, struct scsi_cmnd *cmd) EXPORT_SYMBOL(scsi_cmd_get_serial); /** - * scsi_dispatch_command - Dispatch a command to the low-level driver. - * @cmd: command block we are dispatching. - * - * Return: nonzero return request was rejected and device's queue needs to be - * plugged. - */ -int scsi_dispatch_cmd(struct scsi_cmnd *cmd) -{ - struct Scsi_Host *host = cmd->device->host; - int rtn = 0; - - atomic_inc(&cmd->device->iorequest_cnt); - - /* check if the device is still usable */ - if (unlikely(cmd->device->sdev_state == SDEV_DEL)) { - /* in SDEV_DEL we error all commands. DID_NO_CONNECT - * returns an immediate error upwards, and signals - * that the device is no longer present */ - cmd->result = DID_NO_CONNECT << 16; - goto done; - } - - /* Check to see if the scsi lld made this device blocked. */ - if (unlikely(scsi_device_blocked(cmd->device))) { - /* - * in blocked state, the command is just put back on - * the device queue. The suspend state has already - * blocked the queue so future requests should not - * occur until the device transitions out of the - * suspend state. - */ - SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, - "queuecommand : device blocked\n")); - return SCSI_MLQUEUE_DEVICE_BUSY; - } - - /* Store the LUN value in cmnd, if needed. */ - if (cmd->device->lun_in_cdb) - cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) | - (cmd->device->lun << 5 & 0xe0); - - scsi_log_send(cmd); - - /* - * Before we queue this command, check if the command - * length exceeds what the host adapter can handle. - */ - if (cmd->cmd_len > cmd->device->host->max_cmd_len) { - SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, - "queuecommand : command too long. " - "cdb_size=%d host->max_cmd_len=%d\n", - cmd->cmd_len, cmd->device->host->max_cmd_len)); - cmd->result = (DID_ABORT << 16); - goto done; - } - - if (unlikely(host->shost_state == SHOST_DEL)) { - cmd->result = (DID_NO_CONNECT << 16); - goto done; - - } - - trace_scsi_dispatch_cmd_start(cmd); - rtn = host->hostt->queuecommand(host, cmd); - if (rtn) { - trace_scsi_dispatch_cmd_error(cmd, rtn); - if (rtn != SCSI_MLQUEUE_DEVICE_BUSY && - rtn != SCSI_MLQUEUE_TARGET_BUSY) - rtn = SCSI_MLQUEUE_HOST_BUSY; - - SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, - "queuecommand : request rejected\n")); - } - - return rtn; - done: - cmd->scsi_done(cmd); - return 0; -} - -/** * scsi_finish_command - cleanup and pass command back to upper layer * @cmd: the command * @@ -773,32 +661,18 @@ void scsi_finish_command(struct scsi_cmnd *cmd) } /** - * scsi_adjust_queue_depth - Let low level drivers change a device's queue depth + * scsi_change_queue_depth - change a device's queue depth * @sdev: SCSI Device in question - * @tagged: Do we use tagged queueing (non-0) or do we treat - * this device as an untagged device (0) - * @tags: Number of tags allowed if tagged queueing enabled, - * or number of commands the low level driver can - * queue up in non-tagged mode (as per cmd_per_lun). - * - * Returns: Nothing + * @depth: number of commands allowed to be queued to the driver * - * Lock Status: None held on entry - * - * Notes: Low level drivers may call this at any time and we will do - * the right thing depending on whether or not the device is - * currently active and whether or not it even has the - * command blocks built yet. + * Sets the device queue depth and returns the new value. */ -void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) +int scsi_change_queue_depth(struct scsi_device *sdev, int depth) { unsigned long flags; - /* - * refuse to set tagged depth to an unworkable size - */ - if (tags <= 0) - return; + if (depth <= 0) + goto out; spin_lock_irqsave(sdev->request_queue->queue_lock, flags); @@ -813,35 +687,17 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) */ if (!shost_use_blk_mq(sdev->host) && !sdev->host->bqt) { if (blk_queue_tagged(sdev->request_queue) && - blk_queue_resize_tags(sdev->request_queue, tags) != 0) - goto out; + blk_queue_resize_tags(sdev->request_queue, depth) != 0) + goto out_unlock; } - sdev->queue_depth = tags; - switch (tagged) { - case 0: - sdev->ordered_tags = 0; - sdev->simple_tags = 0; - break; - case MSG_ORDERED_TAG: - sdev->ordered_tags = 1; - sdev->simple_tags = 1; - break; - case MSG_SIMPLE_TAG: - sdev->ordered_tags = 0; - sdev->simple_tags = 1; - break; - default: - sdev->ordered_tags = 0; - sdev->simple_tags = 0; - sdev_printk(KERN_WARNING, sdev, - "scsi_adjust_queue_depth, bad queue type, " - "disabled\n"); - } - out: + sdev->queue_depth = depth; +out_unlock: spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); +out: + return sdev->queue_depth; } -EXPORT_SYMBOL(scsi_adjust_queue_depth); +EXPORT_SYMBOL(scsi_change_queue_depth); /** * scsi_track_queue_full - track QUEUE_FULL events to adjust queue depth @@ -885,19 +741,32 @@ int scsi_track_queue_full(struct scsi_device *sdev, int depth) return 0; if (sdev->last_queue_full_depth < 8) { /* Drop back to untagged */ - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + scsi_set_tag_type(sdev, 0); + scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun); return -1; } - - if (sdev->ordered_tags) - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth); - else - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth); - return depth; + + return scsi_change_queue_depth(sdev, depth); } EXPORT_SYMBOL(scsi_track_queue_full); /** + * scsi_change_queue_type() - Change a device's queue type + * @sdev: The SCSI device whose queue depth is to change + * @tag_type: Identifier for queue type + */ +int scsi_change_queue_type(struct scsi_device *sdev, int tag_type) +{ + if (!sdev->tagged_supported) + return 0; + + scsi_set_tag_type(sdev, tag_type); + return tag_type; + +} +EXPORT_SYMBOL(scsi_change_queue_type); + +/** * scsi_vpd_inquiry - Request a device provide us with a VPD page * @sdev: The device to ask * @buffer: Where to put the result diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 238e06f13b8a..aa4b6b80aade 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -63,8 +63,8 @@ #include "sd.h" #include "scsi_logging.h" -#define SCSI_DEBUG_VERSION "1.84" -static const char *scsi_debug_version_date = "20140706"; +#define SCSI_DEBUG_VERSION "1.85" +static const char *scsi_debug_version_date = "20141022"; #define MY_NAME "scsi_debug" @@ -75,19 +75,22 @@ static const char *scsi_debug_version_date = "20140706"; #define UNRECOVERED_READ_ERR 0x11 #define PARAMETER_LIST_LENGTH_ERR 0x1a #define INVALID_OPCODE 0x20 -#define ADDR_OUT_OF_RANGE 0x21 -#define INVALID_COMMAND_OPCODE 0x20 +#define LBA_OUT_OF_RANGE 0x21 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 #define UA_RESET_ASC 0x29 #define UA_CHANGED_ASC 0x2a +#define INSUFF_RES_ASC 0x55 +#define INSUFF_RES_ASCQ 0x3 #define POWER_ON_RESET_ASCQ 0x0 #define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */ #define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */ +#define CAPACITY_CHANGED_ASCQ 0x9 #define SAVING_PARAMS_UNSUP 0x39 #define TRANSPORT_PROBLEM 0x4b #define THRESHOLD_EXCEEDED 0x5d #define LOW_POWER_COND_ON 0x5e +#define MISCOMPARE_VERIFY_ASC 0x1d /* Additional Sense Code Qualifier (ASCQ) */ #define ACK_NAK_TO 0x3 @@ -133,6 +136,7 @@ static const char *scsi_debug_version_date = "20140706"; #define DEF_VIRTUAL_GB 0 #define DEF_VPD_USE_HOSTNO 1 #define DEF_WRITESAME_LENGTH 0xFFFF +#define DEF_STRICT 0 #define DELAY_OVERRIDDEN -9999 /* bit mask values for scsi_debug_opts */ @@ -176,11 +180,12 @@ static const char *scsi_debug_version_date = "20140706"; #define SDEBUG_UA_POR 0 /* Power on, reset, or bus device reset */ #define SDEBUG_UA_BUS_RESET 1 #define SDEBUG_UA_MODE_CHANGED 2 -#define SDEBUG_NUM_UAS 3 +#define SDEBUG_UA_CAPACITY_CHANGED 3 +#define SDEBUG_NUM_UAS 4 /* for check_readiness() */ -#define UAS_ONLY 1 -#define UAS_TUR 0 +#define UAS_ONLY 1 /* check for UAs only */ +#define UAS_TUR 0 /* if no UAs then check if media access possible */ /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this * sector on read commands: */ @@ -206,6 +211,301 @@ static const char *scsi_debug_version_date = "20140706"; #warning "Expect DEF_CMD_PER_LUN <= SCSI_DEBUG_CANQUEUE" #endif +/* SCSI opcodes (first byte of cdb) mapped onto these indexes */ +enum sdeb_opcode_index { + SDEB_I_INVALID_OPCODE = 0, + SDEB_I_INQUIRY = 1, + SDEB_I_REPORT_LUNS = 2, + SDEB_I_REQUEST_SENSE = 3, + SDEB_I_TEST_UNIT_READY = 4, + SDEB_I_MODE_SENSE = 5, /* 6, 10 */ + SDEB_I_MODE_SELECT = 6, /* 6, 10 */ + SDEB_I_LOG_SENSE = 7, + SDEB_I_READ_CAPACITY = 8, /* 10; 16 is in SA_IN(16) */ + SDEB_I_READ = 9, /* 6, 10, 12, 16 */ + SDEB_I_WRITE = 10, /* 6, 10, 12, 16 */ + SDEB_I_START_STOP = 11, + SDEB_I_SERV_ACT_IN = 12, /* 12, 16 */ + SDEB_I_SERV_ACT_OUT = 13, /* 12, 16 */ + SDEB_I_MAINT_IN = 14, + SDEB_I_MAINT_OUT = 15, + SDEB_I_VERIFY = 16, /* 10 only */ + SDEB_I_VARIABLE_LEN = 17, + SDEB_I_RESERVE = 18, /* 6, 10 */ + SDEB_I_RELEASE = 19, /* 6, 10 */ + SDEB_I_ALLOW_REMOVAL = 20, /* PREVENT ALLOW MEDIUM REMOVAL */ + SDEB_I_REZERO_UNIT = 21, /* REWIND in SSC */ + SDEB_I_ATA_PT = 22, /* 12, 16 */ + SDEB_I_SEND_DIAG = 23, + SDEB_I_UNMAP = 24, + SDEB_I_XDWRITEREAD = 25, /* 10 only */ + SDEB_I_WRITE_BUFFER = 26, + SDEB_I_WRITE_SAME = 27, /* 10, 16 */ + SDEB_I_SYNC_CACHE = 28, /* 10 only */ + SDEB_I_COMP_WRITE = 29, + SDEB_I_LAST_ELEMENT = 30, /* keep this last */ +}; + +static const unsigned char opcode_ind_arr[256] = { +/* 0x0; 0x0->0x1f: 6 byte cdbs */ + SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE, + 0, 0, 0, 0, + SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0, + 0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, + SDEB_I_RELEASE, + 0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG, + SDEB_I_ALLOW_REMOVAL, 0, +/* 0x20; 0x20->0x3f: 10 byte cdbs */ + 0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0, + SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, SDEB_I_VERIFY, + 0, 0, 0, 0, 0, SDEB_I_SYNC_CACHE, 0, 0, + 0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0, +/* 0x40; 0x40->0x5f: 10 byte cdbs */ + 0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0, + 0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, + SDEB_I_RELEASE, + 0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0, +/* 0x60; 0x60->0x7d are reserved */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, SDEB_I_VARIABLE_LEN, +/* 0x80; 0x80->0x9f: 16 byte cdbs */ + 0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0, + SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0, + 0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN, SDEB_I_SERV_ACT_OUT, +/* 0xa0; 0xa0->0xbf: 12 byte cdbs */ + SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN, + SDEB_I_MAINT_OUT, 0, 0, 0, + SDEB_I_READ, SDEB_I_SERV_ACT_OUT, SDEB_I_WRITE, SDEB_I_SERV_ACT_IN, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 0xc0; 0xc0->0xff: vendor specific */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define F_D_IN 1 +#define F_D_OUT 2 +#define F_D_OUT_MAYBE 4 /* WRITE SAME, NDOB bit */ +#define F_D_UNKN 8 +#define F_RL_WLUN_OK 0x10 +#define F_SKIP_UA 0x20 +#define F_DELAY_OVERR 0x40 +#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ +#define F_SA_HIGH 0x100 /* as used by variable length cdbs */ +#define F_INV_OP 0x200 +#define F_FAKE_RW 0x400 +#define F_M_ACCESS 0x800 /* media access */ + +#define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR) +#define FF_DIRECT_IO (F_M_ACCESS | F_FAKE_RW) +#define FF_SA (F_SA_HIGH | F_SA_LOW) + +struct sdebug_dev_info; +static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_mode_sense(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_mode_select(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_log_sense(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_readcap(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_read_dt0(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_dt0(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_start_stop(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_readcap16(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_get_lba_status(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_report_tgtpgs(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_unmap(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); + +struct opcode_info_t { + u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff + * for terminating element */ + u8 opcode; /* if num_attached > 0, preferred */ + u16 sa; /* service action */ + u32 flags; /* OR-ed set of SDEB_F_* */ + int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *); + const struct opcode_info_t *arrp; /* num_attached elements or NULL */ + u8 len_mask[16]; /* len=len_mask[0], then mask for cdb[1]... */ + /* ignore cdb bytes after position 15 */ +}; + +static const struct opcode_info_t msense_iarr[1] = { + {0, 0x1a, 0, F_D_IN, NULL, NULL, + {6, 0xe8, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t mselect_iarr[1] = { + {0, 0x15, 0, F_D_OUT, NULL, NULL, + {6, 0xf1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t read_iarr[3] = { + {0, 0x28, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(10) */ + {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, + {0, 0x8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL, /* READ(6) */ + {6, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0xa8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(12) */ + {12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, + 0xc7, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t write_iarr[3] = { + {0, 0x2a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 10 */ + {10, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, + {0, 0xa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 6 */ + {6, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0xaa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 12 */ + {12, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, + 0xc7, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t sa_in_iarr[1] = { + {0, 0x9e, 0x12, F_SA_LOW | F_D_IN, resp_get_lba_status, NULL, + {16, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0, 0xc7} }, +}; + +static const struct opcode_info_t vl_iarr[1] = { /* VARIABLE LENGTH */ + {0, 0x7f, 0xb, F_SA_HIGH | F_D_OUT | FF_DIRECT_IO, resp_write_dt0, + NULL, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0xb, 0xfa, + 0, 0xff, 0xff, 0xff, 0xff} }, /* WRITE(32) */ +}; + +static const struct opcode_info_t maint_in_iarr[2] = { + {0, 0xa3, 0xc, F_SA_LOW | F_D_IN, resp_rsup_opcodes, NULL, + {12, 0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, + 0xc7, 0, 0, 0, 0} }, + {0, 0xa3, 0xd, F_SA_LOW | F_D_IN, resp_rsup_tmfs, NULL, + {12, 0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, + 0, 0} }, +}; + +static const struct opcode_info_t write_same_iarr[1] = { + {0, 0x93, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_16, NULL, + {16, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x1f, 0xc7} }, +}; + +static const struct opcode_info_t reserve_iarr[1] = { + {0, 0x16, 0, F_D_OUT, NULL, NULL, /* RESERVE(6) */ + {6, 0x1f, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t release_iarr[1] = { + {0, 0x17, 0, F_D_OUT, NULL, NULL, /* RELEASE(6) */ + {6, 0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + + +/* This array is accessed via SDEB_I_* values. Make sure all are mapped, + * plus the terminating elements for logic that scans this table such as + * REPORT SUPPORTED OPERATION CODES. */ +static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { +/* 0 */ + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x12, 0, FF_RESPOND | F_D_IN, resp_inquiry, NULL, + {6, 0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0xa0, 0, FF_RESPOND | F_D_IN, resp_report_luns, NULL, + {12, 0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, + 0, 0} }, + {0, 0x3, 0, FF_RESPOND | F_D_IN, resp_requests, NULL, + {6, 0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x0, 0, F_M_ACCESS | F_RL_WLUN_OK, NULL, NULL,/* TEST UNIT READY */ + {6, 0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x5a, 0, F_D_IN, resp_mode_sense, msense_iarr, + {10, 0xf8, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, + 0} }, + {1, 0x55, 0, F_D_OUT, resp_mode_select, mselect_iarr, + {10, 0xf1, 0, 0, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0, 0x4d, 0, F_D_IN, resp_log_sense, NULL, + {10, 0xe3, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, + 0, 0, 0} }, + {0, 0x25, 0, F_D_IN, resp_readcap, NULL, + {10, 0xe1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {3, 0x88, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, read_iarr, + {16, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x9f, 0xc7} }, /* READ(16) */ +/* 10 */ + {3, 0x8a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, write_iarr, + {16, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x9f, 0xc7} }, /* WRITE(16) */ + {0, 0x1b, 0, 0, resp_start_stop, NULL, /* START STOP UNIT */ + {6, 0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x9e, 0x10, F_SA_LOW | F_D_IN, resp_readcap16, sa_in_iarr, + {16, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x1, 0xc7} }, /* READ CAPACITY(16) */ + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* SA OUT */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {2, 0xa3, 0xa, F_SA_LOW | F_D_IN, resp_report_tgtpgs, maint_in_iarr, + {12, 0xea, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, + 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* MAINT OUT */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* VERIFY */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x7f, 0x9, F_SA_HIGH | F_D_IN | FF_DIRECT_IO, resp_read_dt0, + vl_iarr, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0x9, 0xfe, 0, + 0xff, 0xff, 0xff, 0xff} },/* VARIABLE LENGTH, READ(32) */ + {1, 0x56, 0, F_D_OUT, NULL, reserve_iarr, /* RESERVE(10) */ + {10, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, + 0} }, + {1, 0x57, 0, F_D_OUT, NULL, release_iarr, /* RELEASE(10) */ + {10, 0x13, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, + 0} }, +/* 20 */ + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ALLOW REMOVAL */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */ + {6, 0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x1d, F_D_OUT, 0, NULL, NULL, /* SEND DIAGNOSTIC */ + {6, 0xf7, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x42, 0, F_D_OUT | FF_DIRECT_IO, resp_unmap, NULL, /* UNMAP */ + {10, 0x1, 0, 0, 0, 0, 0x1f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0, 0x53, 0, F_D_IN | F_D_OUT | FF_DIRECT_IO, resp_xdwriteread_10, + NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, + 0, 0, 0, 0, 0, 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* WRITE_BUFFER */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x41, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_10, + write_same_iarr, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, + 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0, 0x35, 0, F_DELAY_OVERR | FF_DIRECT_IO, NULL, NULL, /* SYNC_CACHE */ + {10, 0x7, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, + {0, 0x89, 0, F_D_OUT | FF_DIRECT_IO, resp_comp_write, NULL, + {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, + 0, 0xff, 0x1f, 0xc7} }, /* COMPARE AND WRITE */ + +/* 30 */ + {0xff, 0, 0, 0, NULL, NULL, /* terminating element */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +struct sdebug_scmd_extra_t { + bool inj_recovered; + bool inj_transport; + bool inj_dif; + bool inj_dix; + bool inj_short; +}; + static int scsi_debug_add_host = DEF_NUM_HOST; static int scsi_debug_ato = DEF_ATO; static int scsi_debug_delay = DEF_DELAY; @@ -245,6 +545,8 @@ static unsigned int scsi_debug_write_same_length = DEF_WRITESAME_LENGTH; static bool scsi_debug_removable = DEF_REMOVABLE; static bool scsi_debug_clustering; static bool scsi_debug_host_lock = DEF_HOST_LOCK; +static bool scsi_debug_strict = DEF_STRICT; +static bool sdebug_any_injecting_opt; static atomic_t sdebug_cmnd_count; static atomic_t sdebug_completions; @@ -277,11 +579,10 @@ struct sdebug_dev_info { unsigned int target; u64 lun; struct sdebug_host_info *sdbg_host; - u64 wlun; unsigned long uas_bm[1]; atomic_t num_in_q; - char stopped; - char used; + char stopped; /* TODO: should be atomic */ + bool used; }; struct sdebug_host_info { @@ -394,6 +695,50 @@ static void sdebug_max_tgts_luns(void) spin_unlock(&sdebug_host_list_lock); } +enum sdeb_cmd_data {SDEB_IN_DATA = 0, SDEB_IN_CDB = 1}; + +/* Set in_bit to -1 to indicate no bit position of invalid field */ +static void +mk_sense_invalid_fld(struct scsi_cmnd *scp, enum sdeb_cmd_data c_d, + int in_byte, int in_bit) +{ + unsigned char *sbuff; + u8 sks[4]; + int sl, asc; + + sbuff = scp->sense_buffer; + if (!sbuff) { + sdev_printk(KERN_ERR, scp->device, + "%s: sense_buffer is NULL\n", __func__); + return; + } + asc = c_d ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST; + memset(sbuff, 0, SCSI_SENSE_BUFFERSIZE); + scsi_build_sense_buffer(scsi_debug_dsense, sbuff, ILLEGAL_REQUEST, + asc, 0); + memset(sks, 0, sizeof(sks)); + sks[0] = 0x80; + if (c_d) + sks[0] |= 0x40; + if (in_bit >= 0) { + sks[0] |= 0x8; + sks[0] |= 0x7 & in_bit; + } + put_unaligned_be16(in_byte, sks + 1); + if (scsi_debug_dsense) { + sl = sbuff[7] + 8; + sbuff[7] = sl; + sbuff[sl] = 0x2; + sbuff[sl + 1] = 0x6; + memcpy(sbuff + sl + 4, sks, 3); + } else + memcpy(sbuff + 15, sks, 3); + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + sdev_printk(KERN_INFO, scp->device, "%s: [sense_key,asc,ascq" + "]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n", + my_name, asc, c_d ? 'C' : 'D', in_byte, in_bit); +} + static void mk_sense_buffer(struct scsi_cmnd *scp, int key, int asc, int asq) { unsigned char *sbuff; @@ -414,63 +759,10 @@ static void mk_sense_buffer(struct scsi_cmnd *scp, int key, int asc, int asq) my_name, key, asc, asq); } -static void get_data_transfer_info(unsigned char *cmd, - unsigned long long *lba, unsigned int *num, - u32 *ei_lba) +static void +mk_sense_invalid_opcode(struct scsi_cmnd *scp) { - *ei_lba = 0; - - switch (*cmd) { - case VARIABLE_LENGTH_CMD: - *lba = (u64)cmd[19] | (u64)cmd[18] << 8 | - (u64)cmd[17] << 16 | (u64)cmd[16] << 24 | - (u64)cmd[15] << 32 | (u64)cmd[14] << 40 | - (u64)cmd[13] << 48 | (u64)cmd[12] << 56; - - *ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 | - (u32)cmd[21] << 16 | (u32)cmd[20] << 24; - - *num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 | - (u32)cmd[28] << 24; - break; - - case WRITE_SAME_16: - case WRITE_16: - case READ_16: - *lba = (u64)cmd[9] | (u64)cmd[8] << 8 | - (u64)cmd[7] << 16 | (u64)cmd[6] << 24 | - (u64)cmd[5] << 32 | (u64)cmd[4] << 40 | - (u64)cmd[3] << 48 | (u64)cmd[2] << 56; - - *num = (u32)cmd[13] | (u32)cmd[12] << 8 | (u32)cmd[11] << 16 | - (u32)cmd[10] << 24; - break; - case WRITE_12: - case READ_12: - *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 | - (u32)cmd[2] << 24; - - *num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 | - (u32)cmd[6] << 24; - break; - case WRITE_SAME: - case WRITE_10: - case READ_10: - case XDWRITEREAD_10: - *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 | - (u32)cmd[2] << 24; - - *num = (u32)cmd[8] | (u32)cmd[7] << 8; - break; - case WRITE_6: - case READ_6: - *lba = (u32)cmd[3] | (u32)cmd[2] << 8 | - (u32)(cmd[1] & 0x1f) << 16; - *num = (0 == cmd[4]) ? 256 : cmd[4]; - break; - default: - break; - } + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0); } static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) @@ -520,6 +812,11 @@ static int check_readiness(struct scsi_cmnd *SCpnt, int uas_only, if (debug) cp = "mode parameters changed"; break; + case SDEBUG_UA_CAPACITY_CHANGED: + mk_sense_buffer(SCpnt, UNIT_ATTENTION, + UA_CHANGED_ASC, CAPACITY_CHANGED_ASCQ); + if (debug) + cp = "capacity data changed"; default: pr_warn("%s: unexpected unit attention code=%d\n", __func__, k); @@ -924,19 +1221,20 @@ static int inquiry_evpd_b2(unsigned char *arr) #define SDEBUG_LONG_INQ_SZ 96 #define SDEBUG_MAX_INQ_ARR_SZ 584 -static int resp_inquiry(struct scsi_cmnd *scp, int target, - struct sdebug_dev_info * devip) +static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { unsigned char pq_pdt; unsigned char * arr; unsigned char *cmd = scp->cmnd; int alloc_len, n, ret; + bool have_wlun; alloc_len = (cmd[3] << 8) + cmd[4]; arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC); if (! arr) return DID_REQUEUE << 16; - if (devip->wlun) + have_wlun = (scp->device->lun == SAM2_WLUN_REPORT_LUNS); + if (have_wlun) pq_pdt = 0x1e; /* present, wlun */ else if (scsi_debug_no_lun_0 && (0 == devip->lun)) pq_pdt = 0x7f; /* not present, no device type */ @@ -944,8 +1242,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, int target, pq_pdt = (scsi_debug_ptype & 0x1f); arr[0] = pq_pdt; if (0x2 & cmd[1]) { /* CMDDT bit set */ - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 1); kfree(arr); return check_condition_result; } else if (0x1 & cmd[1]) { /* EVPD bit set */ @@ -957,7 +1254,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, int target, (devip->channel & 0x7f); if (0 == scsi_debug_vpd_use_hostno) host_no = 0; - lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) + + lu_id_num = have_wlun ? -1 : (((host_no + 1) * 2000) + (devip->target * 1000) + devip->lun); target_dev_id = ((host_no + 1) * 2000) + (devip->target * 1000) - 3; @@ -1029,9 +1326,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, int target, arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_b2(&arr[4]); } else { - /* Illegal request, invalid field in cdb */ - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1); kfree(arr); return check_condition_result; } @@ -1077,18 +1372,20 @@ static int resp_requests(struct scsi_cmnd * scp, unsigned char * sbuff; unsigned char *cmd = scp->cmnd; unsigned char arr[SCSI_SENSE_BUFFERSIZE]; - int want_dsense; + bool dsense, want_dsense; int len = 18; memset(arr, 0, sizeof(arr)); - want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense; + dsense = !!(cmd[1] & 1); + want_dsense = dsense || scsi_debug_dsense; sbuff = scp->sense_buffer; if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) { - if (want_dsense) { + if (dsense) { arr[0] = 0x72; arr[1] = 0x0; /* NO_SENSE in sense_key */ arr[2] = THRESHOLD_EXCEEDED; arr[3] = 0xff; /* TEST set and MRIE==6 */ + len = 8; } else { arr[0] = 0x70; arr[2] = 0x0; /* NO_SENSE in sense_key */ @@ -1098,15 +1395,34 @@ static int resp_requests(struct scsi_cmnd * scp, } } else { memcpy(arr, sbuff, SCSI_SENSE_BUFFERSIZE); - if ((cmd[1] & 1) && (! scsi_debug_dsense)) { - /* DESC bit set and sense_buff in fixed format */ - memset(arr, 0, sizeof(arr)); + if (arr[0] >= 0x70 && dsense == scsi_debug_dsense) + ; /* have sense and formats match */ + else if (arr[0] <= 0x70) { + if (dsense) { + memset(arr, 0, 8); + arr[0] = 0x72; + len = 8; + } else { + memset(arr, 0, 18); + arr[0] = 0x70; + arr[7] = 0xa; + } + } else if (dsense) { + memset(arr, 0, 8); arr[0] = 0x72; arr[1] = sbuff[2]; /* sense key */ arr[2] = sbuff[12]; /* asc */ arr[3] = sbuff[13]; /* ascq */ len = 8; + } else { + memset(arr, 0, 18); + arr[0] = 0x70; + arr[2] = sbuff[1]; + arr[7] = 0xa; + arr[12] = sbuff[1]; + arr[13] = sbuff[3]; } + } mk_sense_buffer(scp, 0, NO_ADDITIONAL_SENSE, 0); return fill_from_dev_buffer(scp, arr, len); @@ -1116,15 +1432,11 @@ static int resp_start_stop(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char *cmd = scp->cmnd; - int power_cond, errsts, start; + int power_cond, start; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; power_cond = (cmd[4] & 0xf0) >> 4; if (power_cond) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, 7); return check_condition_result; } start = cmd[4] & 1; @@ -1148,11 +1460,7 @@ static int resp_readcap(struct scsi_cmnd * scp, { unsigned char arr[SDEBUG_READCAP_ARR_SZ]; unsigned int capac; - int errsts; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; /* following just in case virtual_gb changed */ sdebug_capacity = get_sdebug_capacity(); memset(arr, 0, SDEBUG_READCAP_ARR_SZ); @@ -1180,11 +1488,8 @@ static int resp_readcap16(struct scsi_cmnd * scp, unsigned char *cmd = scp->cmnd; unsigned char arr[SDEBUG_READCAP16_ARR_SZ]; unsigned long long capac; - int errsts, k, alloc_len; + int k, alloc_len; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8) + cmd[13]); /* following just in case virtual_gb changed */ @@ -1300,6 +1605,184 @@ static int resp_report_tgtpgs(struct scsi_cmnd * scp, return ret; } +static int +resp_rsup_opcodes(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + bool rctd; + u8 reporting_opts, req_opcode, sdeb_i, supp; + u16 req_sa, u; + u32 alloc_len, a_len; + int k, offset, len, errsts, count, bump, na; + const struct opcode_info_t *oip; + const struct opcode_info_t *r_oip; + u8 *arr; + u8 *cmd = scp->cmnd; + + rctd = !!(cmd[2] & 0x80); + reporting_opts = cmd[2] & 0x7; + req_opcode = cmd[3]; + req_sa = get_unaligned_be16(cmd + 4); + alloc_len = get_unaligned_be32(cmd + 6); + if (alloc_len < 4 && alloc_len > 0xffff) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1); + return check_condition_result; + } + if (alloc_len > 8192) + a_len = 8192; + else + a_len = alloc_len; + arr = kzalloc((a_len < 256) ? 320 : a_len + 64, GFP_KERNEL); + if (NULL == arr) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, + INSUFF_RES_ASCQ); + return check_condition_result; + } + switch (reporting_opts) { + case 0: /* all commands */ + /* count number of commands */ + for (count = 0, oip = opcode_info_arr; + oip->num_attached != 0xff; ++oip) { + if (F_INV_OP & oip->flags) + continue; + count += (oip->num_attached + 1); + } + bump = rctd ? 20 : 8; + put_unaligned_be32(count * bump, arr); + for (offset = 4, oip = opcode_info_arr; + oip->num_attached != 0xff && offset < a_len; ++oip) { + if (F_INV_OP & oip->flags) + continue; + na = oip->num_attached; + arr[offset] = oip->opcode; + put_unaligned_be16(oip->sa, arr + offset + 2); + if (rctd) + arr[offset + 5] |= 0x2; + if (FF_SA & oip->flags) + arr[offset + 5] |= 0x1; + put_unaligned_be16(oip->len_mask[0], arr + offset + 6); + if (rctd) + put_unaligned_be16(0xa, arr + offset + 8); + r_oip = oip; + for (k = 0, oip = oip->arrp; k < na; ++k, ++oip) { + if (F_INV_OP & oip->flags) + continue; + offset += bump; + arr[offset] = oip->opcode; + put_unaligned_be16(oip->sa, arr + offset + 2); + if (rctd) + arr[offset + 5] |= 0x2; + if (FF_SA & oip->flags) + arr[offset + 5] |= 0x1; + put_unaligned_be16(oip->len_mask[0], + arr + offset + 6); + if (rctd) + put_unaligned_be16(0xa, + arr + offset + 8); + } + oip = r_oip; + offset += bump; + } + break; + case 1: /* one command: opcode only */ + case 2: /* one command: opcode plus service action */ + case 3: /* one command: if sa==0 then opcode only else opcode+sa */ + sdeb_i = opcode_ind_arr[req_opcode]; + oip = &opcode_info_arr[sdeb_i]; + if (F_INV_OP & oip->flags) { + supp = 1; + offset = 4; + } else { + if (1 == reporting_opts) { + if (FF_SA & oip->flags) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, + 2, 2); + kfree(arr); + return check_condition_result; + } + req_sa = 0; + } else if (2 == reporting_opts && + 0 == (FF_SA & oip->flags)) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, -1); + kfree(arr); /* point at requested sa */ + return check_condition_result; + } + if (0 == (FF_SA & oip->flags) && + req_opcode == oip->opcode) + supp = 3; + else if (0 == (FF_SA & oip->flags)) { + na = oip->num_attached; + for (k = 0, oip = oip->arrp; k < na; + ++k, ++oip) { + if (req_opcode == oip->opcode) + break; + } + supp = (k >= na) ? 1 : 3; + } else if (req_sa != oip->sa) { + na = oip->num_attached; + for (k = 0, oip = oip->arrp; k < na; + ++k, ++oip) { + if (req_sa == oip->sa) + break; + } + supp = (k >= na) ? 1 : 3; + } else + supp = 3; + if (3 == supp) { + u = oip->len_mask[0]; + put_unaligned_be16(u, arr + 2); + arr[4] = oip->opcode; + for (k = 1; k < u; ++k) + arr[4 + k] = (k < 16) ? + oip->len_mask[k] : 0xff; + offset = 4 + u; + } else + offset = 4; + } + arr[1] = (rctd ? 0x80 : 0) | supp; + if (rctd) { + put_unaligned_be16(0xa, arr + offset); + offset += 12; + } + break; + default: + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 2); + kfree(arr); + return check_condition_result; + } + offset = (offset < a_len) ? offset : a_len; + len = (offset < alloc_len) ? offset : alloc_len; + errsts = fill_from_dev_buffer(scp, arr, len); + kfree(arr); + return errsts; +} + +static int +resp_rsup_tmfs(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + bool repd; + u32 alloc_len, len; + u8 arr[16]; + u8 *cmd = scp->cmnd; + + memset(arr, 0, sizeof(arr)); + repd = !!(cmd[2] & 0x80); + alloc_len = get_unaligned_be32(cmd + 6); + if (alloc_len < 4) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1); + return check_condition_result; + } + arr[0] = 0xc8; /* ATS | ATSS | LURS */ + arr[1] = 0x1; /* ITNRS */ + if (repd) { + arr[3] = 0xc; + len = 16; + } else + len = 4; + + len = (len < alloc_len) ? len : alloc_len; + return fill_from_dev_buffer(scp, arr, len); +} + /* <<Following mode page info copied from ST318451LW>> */ static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target) @@ -1459,20 +1942,18 @@ static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol) #define SDEBUG_MAX_MSENSE_SZ 256 -static int resp_mode_sense(struct scsi_cmnd * scp, int target, - struct sdebug_dev_info * devip) +static int +resp_mode_sense(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { unsigned char dbd, llbaa; int pcontrol, pcode, subpcode, bd_len; unsigned char dev_spec; - int k, alloc_len, msense_6, offset, len, errsts, target_dev_id; + int k, alloc_len, msense_6, offset, len, target_dev_id; + int target = scp->device->id; unsigned char * ap; unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; unsigned char *cmd = scp->cmnd; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; dbd = !!(cmd[1] & 0x8); pcontrol = (cmd[2] & 0xc0) >> 6; pcode = cmd[2] & 0x3f; @@ -1542,8 +2023,7 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target, if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) { /* TODO: Control Extension page */ - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1); return check_condition_result; } switch (pcode) { @@ -1569,8 +2049,7 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target, break; case 0x19: /* if spc==1 then sas phy, control+discover */ if ((subpcode > 0x2) && (subpcode < 0xff)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1); return check_condition_result; } len = 0; @@ -1602,15 +2081,13 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target, } len += resp_iec_m_pg(ap + len, pcontrol, target); } else { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1); return check_condition_result; } offset += len; break; default: - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5); return check_condition_result; } if (msense_6) @@ -1624,24 +2101,21 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target, #define SDEBUG_MAX_MSELECT_SZ 512 -static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, - struct sdebug_dev_info * devip) +static int +resp_mode_select(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { int pf, sp, ps, md_len, bd_len, off, spf, pg_len; - int param_len, res, errsts, mpage; + int param_len, res, mpage; unsigned char arr[SDEBUG_MAX_MSELECT_SZ]; unsigned char *cmd = scp->cmnd; + int mselect6 = (MODE_SELECT == cmd[0]); - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; memset(arr, 0, sizeof(arr)); pf = cmd[1] & 0x10; sp = cmd[1] & 0x1; param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]); if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, mselect6 ? 4 : 7, -1); return check_condition_result; } res = fetch_to_dev_buffer(scp, arr, param_len); @@ -1655,16 +2129,14 @@ static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2); bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]); if (md_len > 2) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_PARAM_LIST, 0); + mk_sense_invalid_fld(scp, SDEB_IN_DATA, 0, -1); return check_condition_result; } off = bd_len + (mselect6 ? 4 : 8); mpage = arr[off] & 0x3f; ps = !!(arr[off] & 0x80); if (ps) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_PARAM_LIST, 0); + mk_sense_invalid_fld(scp, SDEB_IN_DATA, off, 7); return check_condition_result; } spf = !!(arr[off] & 0x40); @@ -1701,8 +2173,7 @@ static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, default: break; } - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_PARAM_LIST, 0); + mk_sense_invalid_fld(scp, SDEB_IN_DATA, off, 5); return check_condition_result; set_mode_changed_ua: set_bit(SDEBUG_UA_MODE_CHANGED, devip->uas_bm); @@ -1737,19 +2208,15 @@ static int resp_ie_l_pg(unsigned char * arr) static int resp_log_sense(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { - int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n; + int ppc, sp, pcontrol, pcode, subpcode, alloc_len, len, n; unsigned char arr[SDEBUG_MAX_LSENSE_SZ]; unsigned char *cmd = scp->cmnd; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; memset(arr, 0, sizeof(arr)); ppc = cmd[1] & 0x2; sp = cmd[1] & 0x1; if (ppc || sp) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, ppc ? 1 : 0); return check_condition_result; } pcontrol = (cmd[2] & 0xc0) >> 6; @@ -1773,8 +2240,7 @@ static int resp_log_sense(struct scsi_cmnd * scp, arr[3] = resp_ie_l_pg(arr + 4); break; default: - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5); return check_condition_result; } } else if (0xff == subpcode) { @@ -1806,13 +2272,11 @@ static int resp_log_sense(struct scsi_cmnd * scp, arr[3] = n - 4; break; default: - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5); return check_condition_result; } } else { - mk_sense_buffer(scp, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1); return check_condition_result; } len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len); @@ -1824,11 +2288,12 @@ static int check_device_access_params(struct scsi_cmnd *scp, unsigned long long lba, unsigned int num) { if (lba + num > sdebug_capacity) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, 0); + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); return check_condition_result; } /* transfer length excessive (tie in to block limits VPD page) */ if (num > sdebug_store_sectors) { + /* needs work to find which cdb byte 'num' comes from */ mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } @@ -1836,17 +2301,17 @@ static int check_device_access_params(struct scsi_cmnd *scp, } /* Returns number of bytes copied or -1 if error. */ -static int do_device_access(struct scsi_cmnd *scmd, - unsigned long long lba, unsigned int num, int write) +static int +do_device_access(struct scsi_cmnd *scmd, u64 lba, u32 num, bool do_write) { int ret; - unsigned long long block, rest = 0; + u64 block, rest = 0; struct scsi_data_buffer *sdb; enum dma_data_direction dir; size_t (*func)(struct scatterlist *, unsigned int, void *, size_t, off_t); - if (write) { + if (do_write) { sdb = scsi_out(scmd); dir = DMA_TO_DEVICE; func = sg_pcopy_to_buffer; @@ -1880,6 +2345,38 @@ static int do_device_access(struct scsi_cmnd *scmd, return ret; } +/* If fake_store(lba,num) compares equal to arr(num), then copy top half of + * arr into fake_store(lba,num) and return true. If comparison fails then + * return false. */ +static bool +comp_write_worker(u64 lba, u32 num, const u8 *arr) +{ + bool res; + u64 block, rest = 0; + u32 store_blks = sdebug_store_sectors; + u32 lb_size = scsi_debug_sector_size; + + block = do_div(lba, store_blks); + if (block + num > store_blks) + rest = block + num - store_blks; + + res = !memcmp(fake_storep + (block * lb_size), arr, + (num - rest) * lb_size); + if (!res) + return res; + if (rest) + res = memcmp(fake_storep, arr + ((num - rest) * lb_size), + rest * lb_size); + if (!res) + return res; + arr += num * lb_size; + memcpy(fake_storep + (block * lb_size), arr, (num - rest) * lb_size); + if (rest) + memcpy(fake_storep, arr + ((num - rest) * lb_size), + rest * lb_size); + return res; +} + static __be16 dif_compute_csum(const void *buf, int len) { __be16 csum; @@ -1992,55 +2489,143 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, return 0; } -static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, u32 ei_lba) +static int +resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { + u8 *cmd = scp->cmnd; + u64 lba; + u32 num; + u32 ei_lba; unsigned long iflags; int ret; + bool check_prot; - ret = check_device_access_params(SCpnt, lba, num); - if (ret) - return ret; + switch (cmd[0]) { + case READ_16: + ei_lba = 0; + lba = get_unaligned_be64(cmd + 2); + num = get_unaligned_be32(cmd + 10); + check_prot = true; + break; + case READ_10: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = true; + break; + case READ_6: + ei_lba = 0; + lba = (u32)cmd[3] | (u32)cmd[2] << 8 | + (u32)(cmd[1] & 0x1f) << 16; + num = (0 == cmd[4]) ? 256 : cmd[4]; + check_prot = true; + break; + case READ_12: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be32(cmd + 6); + check_prot = true; + break; + case XDWRITEREAD_10: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = false; + break; + default: /* assume READ(32) */ + lba = get_unaligned_be64(cmd + 12); + ei_lba = get_unaligned_be32(cmd + 20); + num = get_unaligned_be32(cmd + 28); + check_prot = false; + break; + } + if (check_prot) { + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + (cmd[1] & 0xe0)) { + mk_sense_invalid_opcode(scp); + return check_condition_result; + } + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + sdev_printk(KERN_ERR, scp->device, "Unprotected RD " + "to DIF device\n"); + } + if (sdebug_any_injecting_opt) { + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + + if (ep->inj_short) + num /= 2; + } + + /* inline check_device_access_params() */ + if (lba + num > sdebug_capacity) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); + return check_condition_result; + } + /* transfer length excessive (tie in to block limits VPD page) */ + if (num > sdebug_store_sectors) { + /* needs work to find which cdb byte 'num' comes from */ + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && (lba <= (OPT_MEDIUM_ERR_ADDR + OPT_MEDIUM_ERR_NUM - 1)) && ((lba + num) > OPT_MEDIUM_ERR_ADDR)) { /* claim unrecoverable read error */ - mk_sense_buffer(SCpnt, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0); + mk_sense_buffer(scp, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0); /* set info field and valid bit for fixed descriptor */ - if (0x70 == (SCpnt->sense_buffer[0] & 0x7f)) { - SCpnt->sense_buffer[0] |= 0x80; /* Valid bit */ + if (0x70 == (scp->sense_buffer[0] & 0x7f)) { + scp->sense_buffer[0] |= 0x80; /* Valid bit */ ret = (lba < OPT_MEDIUM_ERR_ADDR) ? OPT_MEDIUM_ERR_ADDR : (int)lba; - SCpnt->sense_buffer[3] = (ret >> 24) & 0xff; - SCpnt->sense_buffer[4] = (ret >> 16) & 0xff; - SCpnt->sense_buffer[5] = (ret >> 8) & 0xff; - SCpnt->sense_buffer[6] = ret & 0xff; + put_unaligned_be32(ret, scp->sense_buffer + 3); } - scsi_set_resid(SCpnt, scsi_bufflen(SCpnt)); + scsi_set_resid(scp, scsi_bufflen(scp)); return check_condition_result; } read_lock_irqsave(&atomic_rw, iflags); /* DIX + T10 DIF */ - if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba); + if (scsi_debug_dix && scsi_prot_sg_count(scp)) { + int prot_ret = prot_verify_read(scp, lba, num, ei_lba); if (prot_ret) { read_unlock_irqrestore(&atomic_rw, iflags); - mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, prot_ret); + mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, prot_ret); return illegal_condition_result; } } - ret = do_device_access(SCpnt, lba, num, 0); + ret = do_device_access(scp, lba, num, false); read_unlock_irqrestore(&atomic_rw, iflags); if (ret == -1) return DID_ERROR << 16; - scsi_in(SCpnt)->resid = scsi_bufflen(SCpnt) - ret; + scsi_in(scp)->resid = scsi_bufflen(scp) - ret; + + if (sdebug_any_injecting_opt) { + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + if (ep->inj_recovered) { + mk_sense_buffer(scp, RECOVERED_ERROR, + THRESHOLD_EXCEEDED, 0); + return check_condition_result; + } else if (ep->inj_transport) { + mk_sense_buffer(scp, ABORTED_COMMAND, + TRANSPORT_PROBLEM, ACK_NAK_TO); + return check_condition_result; + } else if (ep->inj_dif) { + /* Logical block guard check failed */ + mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1); + return illegal_condition_result; + } else if (ep->inj_dix) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1); + return illegal_condition_result; + } + } return 0; } @@ -2223,31 +2808,95 @@ static void unmap_region(sector_t lba, unsigned int len) } } -static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, u32 ei_lba) +static int +resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { + u8 *cmd = scp->cmnd; + u64 lba; + u32 num; + u32 ei_lba; unsigned long iflags; int ret; + bool check_prot; - ret = check_device_access_params(SCpnt, lba, num); - if (ret) - return ret; + switch (cmd[0]) { + case WRITE_16: + ei_lba = 0; + lba = get_unaligned_be64(cmd + 2); + num = get_unaligned_be32(cmd + 10); + check_prot = true; + break; + case WRITE_10: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = true; + break; + case WRITE_6: + ei_lba = 0; + lba = (u32)cmd[3] | (u32)cmd[2] << 8 | + (u32)(cmd[1] & 0x1f) << 16; + num = (0 == cmd[4]) ? 256 : cmd[4]; + check_prot = true; + break; + case WRITE_12: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be32(cmd + 6); + check_prot = true; + break; + case 0x53: /* XDWRITEREAD(10) */ + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = false; + break; + default: /* assume WRITE(32) */ + lba = get_unaligned_be64(cmd + 12); + ei_lba = get_unaligned_be32(cmd + 20); + num = get_unaligned_be32(cmd + 28); + check_prot = false; + break; + } + if (check_prot) { + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + (cmd[1] & 0xe0)) { + mk_sense_invalid_opcode(scp); + return check_condition_result; + } + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + sdev_printk(KERN_ERR, scp->device, "Unprotected WR " + "to DIF device\n"); + } + + /* inline check_device_access_params() */ + if (lba + num > sdebug_capacity) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); + return check_condition_result; + } + /* transfer length excessive (tie in to block limits VPD page) */ + if (num > sdebug_store_sectors) { + /* needs work to find which cdb byte 'num' comes from */ + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } write_lock_irqsave(&atomic_rw, iflags); /* DIX + T10 DIF */ - if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba); + if (scsi_debug_dix && scsi_prot_sg_count(scp)) { + int prot_ret = prot_verify_write(scp, lba, num, ei_lba); if (prot_ret) { write_unlock_irqrestore(&atomic_rw, iflags); - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10, - prot_ret); + mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, prot_ret); return illegal_condition_result; } } - ret = do_device_access(SCpnt, lba, num, 1); + ret = do_device_access(scp, lba, num, true); if (scsi_debug_lbp()) map_region(lba, num); write_unlock_irqrestore(&atomic_rw, iflags); @@ -2255,30 +2904,41 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, return (DID_ERROR << 16); else if ((ret < (num * scsi_debug_sector_size)) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) - sdev_printk(KERN_INFO, SCpnt->device, + sdev_printk(KERN_INFO, scp->device, "%s: write: cdb indicated=%u, IO sent=%d bytes\n", my_name, num * scsi_debug_sector_size, ret); + if (sdebug_any_injecting_opt) { + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + + if (ep->inj_recovered) { + mk_sense_buffer(scp, RECOVERED_ERROR, + THRESHOLD_EXCEEDED, 0); + return check_condition_result; + } else if (ep->inj_dif) { + /* Logical block guard check failed */ + mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1); + return illegal_condition_result; + } else if (ep->inj_dix) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1); + return illegal_condition_result; + } + } return 0; } -static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba, - unsigned int num, u32 ei_lba, unsigned int unmap) +static int +resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, u32 ei_lba, + bool unmap, bool ndob) { unsigned long iflags; unsigned long long i; int ret; - ret = check_device_access_params(scmd, lba, num); + ret = check_device_access_params(scp, lba, num); if (ret) return ret; - if (num > scsi_debug_write_same_length) { - mk_sense_buffer(scmd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); - return check_condition_result; - } - write_lock_irqsave(&atomic_rw, iflags); if (unmap && scsi_debug_lbp()) { @@ -2286,17 +2946,22 @@ static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba, goto out; } - /* Else fetch one logical block */ - ret = fetch_to_dev_buffer(scmd, - fake_storep + (lba * scsi_debug_sector_size), - scsi_debug_sector_size); + /* if ndob then zero 1 logical block, else fetch 1 logical block */ + if (ndob) { + memset(fake_storep + (lba * scsi_debug_sector_size), 0, + scsi_debug_sector_size); + ret = 0; + } else + ret = fetch_to_dev_buffer(scp, fake_storep + + (lba * scsi_debug_sector_size), + scsi_debug_sector_size); if (-1 == ret) { write_unlock_irqrestore(&atomic_rw, iflags); return (DID_ERROR << 16); } else if ((ret < (num * scsi_debug_sector_size)) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) - sdev_printk(KERN_INFO, scmd->device, + sdev_printk(KERN_INFO, scp->device, "%s: %s: cdb indicated=%u, IO sent=%d bytes\n", my_name, "write same", num * scsi_debug_sector_size, ret); @@ -2315,13 +2980,143 @@ out: return 0; } +static int +resp_write_same_10(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + u8 *cmd = scp->cmnd; + u32 lba; + u16 num; + u32 ei_lba = 0; + bool unmap = false; + + if (cmd[1] & 0x8) { + if (scsi_debug_lbpws10 == 0) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 3); + return check_condition_result; + } else + unmap = true; + } + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + if (num > scsi_debug_write_same_length) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 7, -1); + return check_condition_result; + } + return resp_write_same(scp, lba, num, ei_lba, unmap, false); +} + +static int +resp_write_same_16(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + u8 *cmd = scp->cmnd; + u64 lba; + u32 num; + u32 ei_lba = 0; + bool unmap = false; + bool ndob = false; + + if (cmd[1] & 0x8) { /* UNMAP */ + if (scsi_debug_lbpws == 0) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 3); + return check_condition_result; + } else + unmap = true; + } + if (cmd[1] & 0x1) /* NDOB (no data-out buffer, assumes zeroes) */ + ndob = true; + lba = get_unaligned_be64(cmd + 2); + num = get_unaligned_be32(cmd + 10); + if (num > scsi_debug_write_same_length) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 10, -1); + return check_condition_result; + } + return resp_write_same(scp, lba, num, ei_lba, unmap, ndob); +} + +static int +resp_comp_write(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + u8 *cmd = scp->cmnd; + u8 *arr; + u8 *fake_storep_hold; + u64 lba; + u32 dnum; + u32 lb_size = scsi_debug_sector_size; + u8 num; + unsigned long iflags; + int ret; + + lba = get_unaligned_be32(cmd + 2); + num = cmd[13]; /* 1 to a maximum of 255 logical blocks */ + if (0 == num) + return 0; /* degenerate case, not an error */ + dnum = 2 * num; + arr = kzalloc(dnum * lb_size, GFP_ATOMIC); + if (NULL == arr) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, + INSUFF_RES_ASCQ); + return check_condition_result; + } + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + (cmd[1] & 0xe0)) { + mk_sense_invalid_opcode(scp); + return check_condition_result; + } + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + sdev_printk(KERN_ERR, scp->device, "Unprotected WR " + "to DIF device\n"); + + /* inline check_device_access_params() */ + if (lba + num > sdebug_capacity) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); + return check_condition_result; + } + /* transfer length excessive (tie in to block limits VPD page) */ + if (num > sdebug_store_sectors) { + /* needs work to find which cdb byte 'num' comes from */ + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } + + write_lock_irqsave(&atomic_rw, iflags); + + /* trick do_device_access() to fetch both compare and write buffers + * from data-in into arr. Safe (atomic) since write_lock held. */ + fake_storep_hold = fake_storep; + fake_storep = arr; + ret = do_device_access(scp, 0, dnum, true); + fake_storep = fake_storep_hold; + if (ret == -1) { + write_unlock_irqrestore(&atomic_rw, iflags); + kfree(arr); + return DID_ERROR << 16; + } else if ((ret < (dnum * lb_size)) && + (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) + sdev_printk(KERN_INFO, scp->device, "%s: compare_write: cdb " + "indicated=%u, IO sent=%d bytes\n", my_name, + dnum * lb_size, ret); + if (!comp_write_worker(lba, num, arr)) { + write_unlock_irqrestore(&atomic_rw, iflags); + kfree(arr); + mk_sense_buffer(scp, MISCOMPARE, MISCOMPARE_VERIFY_ASC, 0); + return check_condition_result; + } + if (scsi_debug_lbp()) + map_region(lba, num); + write_unlock_irqrestore(&atomic_rw, iflags); + return 0; +} + struct unmap_block_desc { __be64 lba; __be32 blocks; __be32 __reserved; }; -static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip) +static int +resp_unmap(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { unsigned char *buf; struct unmap_block_desc *desc; @@ -2329,20 +3124,26 @@ static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip) int ret; unsigned long iflags; - ret = check_readiness(scmd, UAS_ONLY, devip); - if (ret) - return ret; - payload_len = get_unaligned_be16(&scmd->cmnd[7]); - BUG_ON(scsi_bufflen(scmd) != payload_len); + if (!scsi_debug_lbp()) + return 0; /* fib and say its done */ + payload_len = get_unaligned_be16(scp->cmnd + 7); + BUG_ON(scsi_bufflen(scp) != payload_len); descriptors = (payload_len - 8) / 16; + if (descriptors > scsi_debug_unmap_max_desc) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 7, -1); + return check_condition_result; + } - buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC); - if (!buf) + buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC); + if (!buf) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, + INSUFF_RES_ASCQ); return check_condition_result; + } - scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd)); + scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp)); BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2); BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16); @@ -2355,7 +3156,7 @@ static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip) unsigned long long lba = get_unaligned_be64(&desc[i].lba); unsigned int num = get_unaligned_be32(&desc[i].blocks); - ret = check_device_access_params(scmd, lba, num); + ret = check_device_access_params(scp, lba, num); if (ret) goto out; @@ -2373,37 +3174,44 @@ out: #define SDEBUG_GET_LBA_STATUS_LEN 32 -static int resp_get_lba_status(struct scsi_cmnd * scmd, - struct sdebug_dev_info * devip) +static int +resp_get_lba_status(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { - unsigned long long lba; - unsigned int alloc_len, mapped, num; - unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN]; + u8 *cmd = scp->cmnd; + u64 lba; + u32 alloc_len, mapped, num; + u8 arr[SDEBUG_GET_LBA_STATUS_LEN]; int ret; - ret = check_readiness(scmd, UAS_ONLY, devip); - if (ret) - return ret; - - lba = get_unaligned_be64(&scmd->cmnd[2]); - alloc_len = get_unaligned_be32(&scmd->cmnd[10]); + lba = get_unaligned_be64(cmd + 2); + alloc_len = get_unaligned_be32(cmd + 10); if (alloc_len < 24) return 0; - ret = check_device_access_params(scmd, lba, 1); + ret = check_device_access_params(scp, lba, 1); if (ret) return ret; - mapped = map_state(lba, &num); + if (scsi_debug_lbp()) + mapped = map_state(lba, &num); + else { + mapped = 1; + /* following just in case virtual_gb changed */ + sdebug_capacity = get_sdebug_capacity(); + if (sdebug_capacity - lba <= 0xffffffff) + num = sdebug_capacity - lba; + else + num = 0xffffffff; + } memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN); - put_unaligned_be32(20, &arr[0]); /* Parameter Data Length */ - put_unaligned_be64(lba, &arr[8]); /* LBA */ - put_unaligned_be32(num, &arr[16]); /* Number of blocks */ - arr[20] = !mapped; /* mapped = 0, unmapped = 1 */ + put_unaligned_be32(20, arr); /* Parameter Data Length */ + put_unaligned_be64(lba, arr + 8); /* LBA */ + put_unaligned_be32(num, arr + 16); /* Number of blocks */ + arr[20] = !mapped; /* prov_stat=0: mapped; 1: dealloc */ - return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN); + return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN); } #define SDEBUG_RLUN_ARR_SZ 256 @@ -2412,8 +3220,8 @@ static int resp_report_luns(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned int alloc_len; - int lun_cnt, i, upper, num, n; - u64 wlun, lun; + int lun_cnt, i, upper, num, n, want_wlun, shortish; + u64 lun; unsigned char *cmd = scp->cmnd; int select_report = (int)cmd[2]; struct scsi_lun *one_lun; @@ -2421,9 +3229,9 @@ static int resp_report_luns(struct scsi_cmnd * scp, unsigned char * max_addr; alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); - if ((alloc_len < 4) || (select_report > 2)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); + shortish = (alloc_len < 4); + if (shortish || (select_report > 2)) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, shortish ? 6 : 2, -1); return check_condition_result; } /* can produce response with up to 16k luns (lun 0 to lun 16383) */ @@ -2433,14 +3241,14 @@ static int resp_report_luns(struct scsi_cmnd * scp, lun_cnt = 0; else if (scsi_debug_no_lun_0 && (lun_cnt > 0)) --lun_cnt; - wlun = (select_report > 0) ? 1 : 0; - num = lun_cnt + wlun; + want_wlun = (select_report > 0) ? 1 : 0; + num = lun_cnt + want_wlun; arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff; arr[3] = (sizeof(struct scsi_lun) * num) & 0xff; n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) / sizeof(struct scsi_lun)), num); if (n < num) { - wlun = 0; + want_wlun = 0; lun_cnt = n; } one_lun = (struct scsi_lun *) &arr[8]; @@ -2454,7 +3262,7 @@ static int resp_report_luns(struct scsi_cmnd * scp, (upper | (SAM2_LUN_ADDRESS_METHOD << 6)); one_lun[i].scsi_lun[1] = lun & 0xff; } - if (wlun) { + if (want_wlun) { one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff; one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff; i++; @@ -2476,8 +3284,8 @@ static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba, /* better not to use temporary buffer. */ buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC); if (!buf) { - mk_sense_buffer(scp, NOT_READY, - LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, + INSUFF_RES_ASCQ); return check_condition_result; } @@ -2500,6 +3308,32 @@ static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba, return 0; } +static int +resp_xdwriteread_10(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + u8 *cmd = scp->cmnd; + u64 lba; + u32 num; + int errsts; + + if (!scsi_bidi_cmnd(scp)) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, + INSUFF_RES_ASCQ); + return check_condition_result; + } + errsts = resp_read_dt0(scp, devip); + if (errsts) + return errsts; + if (!(cmd[1] & 0x4)) { /* DISABLE_WRITE is not set */ + errsts = resp_write_dt0(scp, devip); + if (errsts) + return errsts; + } + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + return resp_xdwriteread(scp, lba, num, devip); +} + /* When timer or tasklet goes off this function is called. */ static void sdebug_q_cmd_complete(unsigned long indx) { @@ -2672,10 +3506,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) open_devip->sdbg_host = sdbg_host; atomic_set(&open_devip->num_in_q, 0); set_bit(SDEBUG_UA_POR, open_devip->uas_bm); - open_devip->used = 1; - if (sdev->lun == SAM2_WLUN_REPORT_LUNS) - open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff; - + open_devip->used = true; return open_devip; } @@ -2701,10 +3532,6 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp) if (NULL == devip) return 1; /* no resources, will be marked offline */ sdp->hostdata = devip; - sdp->tagged_supported = 1; - if (sdp->host->cmd_per_lun) - scsi_adjust_queue_depth(sdp, DEF_TAGGED_QUEUING, - DEF_CMD_PER_LUN); blk_queue_max_segment_size(sdp->request_queue, -1U); if (scsi_debug_no_uld) sdp->no_uld_attach = 1; @@ -2721,7 +3548,7 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp) sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot available for re-use */ - devip->used = 0; + devip->used = false; sdp->hostdata = NULL; } } @@ -3166,6 +3993,7 @@ module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR); module_param_named(removable, scsi_debug_removable, bool, S_IRUGO | S_IWUSR); module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO); module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO); +module_param_named(strict, scsi_debug_strict, bool, S_IRUGO | S_IWUSR); module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO); module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO); module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO); @@ -3185,7 +4013,7 @@ MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)"); MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)"); MODULE_PARM_DESC(clustering, "when set enables larger transfers (def=0)"); MODULE_PARM_DESC(delay, "response delay (def=1 jiffy); 0:imm, -1,-2:tiny"); -MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)"); +MODULE_PARM_DESC(dev_size_mb, "size in MiB of ram shared by devs(def=8)"); MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)"); MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)"); MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)"); @@ -3212,11 +4040,12 @@ MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])"); MODULE_PARM_DESC(removable, "claim to have removable media (def=0)"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=6[SPC-4])"); MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)"); +MODULE_PARM_DESC(strict, "stricter checks: reserved field in cdb (def=0)"); MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)"); MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=1)"); MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0xffffffff)"); MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=256)"); -MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)"); +MODULE_PARM_DESC(virtual_gb, "virtual gigabyte (GiB) size (def=0 -> use dev_size_mb)"); MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)"); @@ -3382,6 +4211,16 @@ static ssize_t opts_store(struct device_driver *ddp, const char *buf, return -EINVAL; opts_done: scsi_debug_opts = opts; + if (SCSI_DEBUG_OPT_RECOVERED_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_DIF_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_DIX_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & opts) + sdebug_any_injecting_opt = true; atomic_set(&sdebug_cmnd_count, 0); atomic_set(&sdebug_a_tsf, 0); return count; @@ -3589,12 +4428,25 @@ static ssize_t virtual_gb_store(struct device_driver *ddp, const char *buf, size_t count) { int n; + bool changed; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + changed = (scsi_debug_virtual_gb != n); scsi_debug_virtual_gb = n; - sdebug_capacity = get_sdebug_capacity(); - + if (changed) { + struct sdebug_host_info *sdhp; + struct sdebug_dev_info *dp; + + list_for_each_entry(sdhp, &sdebug_host_list, + host_list) { + list_for_each_entry(dp, &sdhp->dev_info_list, + dev_list) { + set_bit(SDEBUG_UA_CAPACITY_CHANGED, + dp->uas_bm); + } + } + } return count; } return -EINVAL; @@ -3740,6 +4592,23 @@ static ssize_t host_lock_store(struct device_driver *ddp, const char *buf, } static DRIVER_ATTR_RW(host_lock); +static ssize_t strict_show(struct device_driver *ddp, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", !!scsi_debug_strict); +} +static ssize_t strict_store(struct device_driver *ddp, const char *buf, + size_t count) +{ + int n; + + if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + scsi_debug_strict = (n > 0); + return count; + } + return -EINVAL; +} +static DRIVER_ATTR_RW(strict); + /* Note: The following array creates attribute files in the /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these @@ -3775,6 +4644,7 @@ static struct attribute *sdebug_drv_attrs[] = { &driver_attr_removable.attr, &driver_attr_host_lock.attr, &driver_attr_ndelay.attr, + &driver_attr_strict.attr, NULL, }; ATTRIBUTE_GROUPS(sdebug_drv); @@ -4087,396 +4957,9 @@ static void sdebug_remove_adapter(void) } static int -scsi_debug_queuecommand(struct scsi_cmnd *SCpnt) -{ - unsigned char *cmd = SCpnt->cmnd; - int len, k; - unsigned int num; - unsigned long long lba; - u32 ei_lba; - int errsts = 0; - int target = SCpnt->device->id; - struct sdebug_dev_info *devip = NULL; - int inj_recovered = 0; - int inj_transport = 0; - int inj_dif = 0; - int inj_dix = 0; - int inj_short = 0; - int delay_override = 0; - int unmap = 0; - - scsi_set_resid(SCpnt, 0); - if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && - !(SCSI_DEBUG_OPT_NO_CDB_NOISE & scsi_debug_opts)) { - char b[120]; - int n; - - len = SCpnt->cmd_len; - if (len > 32) - strcpy(b, "too long, over 32 bytes"); - else { - for (k = 0, n = 0; k < len; ++k) - n += scnprintf(b + n, sizeof(b) - n, "%02x ", - (unsigned int)cmd[k]); - } - sdev_printk(KERN_INFO, SCpnt->device, "%s: cmd %s\n", my_name, - b); - } - - if ((SCpnt->device->lun >= scsi_debug_max_luns) && - (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS)) - return schedule_resp(SCpnt, NULL, DID_NO_CONNECT << 16, 0); - devip = devInfoReg(SCpnt->device); - if (NULL == devip) - return schedule_resp(SCpnt, NULL, DID_NO_CONNECT << 16, 0); - - if ((scsi_debug_every_nth != 0) && - (atomic_inc_return(&sdebug_cmnd_count) >= - abs(scsi_debug_every_nth))) { - atomic_set(&sdebug_cmnd_count, 0); - if (scsi_debug_every_nth < -1) - scsi_debug_every_nth = -1; - if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) - return 0; /* ignore command causing timeout */ - else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts && - scsi_medium_access_command(SCpnt)) - return 0; /* time out reads and writes */ - else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) - inj_recovered = 1; /* to reads and writes below */ - else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts) - inj_transport = 1; /* to reads and writes below */ - else if (SCSI_DEBUG_OPT_DIF_ERR & scsi_debug_opts) - inj_dif = 1; /* to reads and writes below */ - else if (SCSI_DEBUG_OPT_DIX_ERR & scsi_debug_opts) - inj_dix = 1; /* to reads and writes below */ - else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & scsi_debug_opts) - inj_short = 1; - } - - if (devip->wlun) { - switch (*cmd) { - case INQUIRY: - case REQUEST_SENSE: - case TEST_UNIT_READY: - case REPORT_LUNS: - break; /* only allowable wlun commands */ - default: - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: Opcode: 0x%x " - "not supported for wlun\n", *cmd); - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_OPCODE, 0); - errsts = check_condition_result; - return schedule_resp(SCpnt, devip, errsts, 0); - } - } - - switch (*cmd) { - case INQUIRY: /* mandatory, ignore unit attention */ - delay_override = 1; - errsts = resp_inquiry(SCpnt, target, devip); - break; - case REQUEST_SENSE: /* mandatory, ignore unit attention */ - delay_override = 1; - errsts = resp_requests(SCpnt, devip); - break; - case REZERO_UNIT: /* actually this is REWIND for SSC */ - case START_STOP: - errsts = resp_start_stop(SCpnt, devip); - break; - case ALLOW_MEDIUM_REMOVAL: - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - if (errsts) - break; - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: Medium removal %s\n", - cmd[4] ? "inhibited" : "enabled"); - break; - case SEND_DIAGNOSTIC: /* mandatory */ - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - break; - case TEST_UNIT_READY: /* mandatory */ - /* delay_override = 1; */ - errsts = check_readiness(SCpnt, UAS_TUR, devip); - break; - case RESERVE: - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - break; - case RESERVE_10: - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - break; - case RELEASE: - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - break; - case RELEASE_10: - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - break; - case READ_CAPACITY: - errsts = resp_readcap(SCpnt, devip); - break; - case SERVICE_ACTION_IN: - if (cmd[1] == SAI_READ_CAPACITY_16) - errsts = resp_readcap16(SCpnt, devip); - else if (cmd[1] == SAI_GET_LBA_STATUS) { - - if (scsi_debug_lbp() == 0) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_COMMAND_OPCODE, 0); - errsts = check_condition_result; - } else - errsts = resp_get_lba_status(SCpnt, devip); - } else { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_OPCODE, 0); - errsts = check_condition_result; - } - break; - case MAINTENANCE_IN: - if (MI_REPORT_TARGET_PGS != cmd[1]) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_OPCODE, 0); - errsts = check_condition_result; - break; - } - errsts = resp_report_tgtpgs(SCpnt, devip); - break; - case READ_16: - case READ_12: - case READ_10: - /* READ{10,12,16} and DIF Type 2 are natural enemies */ - if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && - cmd[1] & 0xe0) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_COMMAND_OPCODE, 0); - errsts = check_condition_result; - break; - } - - if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || - scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && - (cmd[1] & 0xe0) == 0) - printk(KERN_ERR "Unprotected RD/WR to DIF device\n"); - - /* fall through */ - case READ_6: -read: - errsts = check_readiness(SCpnt, UAS_TUR, devip); - if (errsts) - break; - if (scsi_debug_fake_rw) - break; - get_data_transfer_info(cmd, &lba, &num, &ei_lba); - - if (inj_short) - num /= 2; - - errsts = resp_read(SCpnt, lba, num, ei_lba); - if (inj_recovered && (0 == errsts)) { - mk_sense_buffer(SCpnt, RECOVERED_ERROR, - THRESHOLD_EXCEEDED, 0); - errsts = check_condition_result; - } else if (inj_transport && (0 == errsts)) { - mk_sense_buffer(SCpnt, ABORTED_COMMAND, - TRANSPORT_PROBLEM, ACK_NAK_TO); - errsts = check_condition_result; - } else if (inj_dif && (0 == errsts)) { - /* Logical block guard check failed */ - mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, 1); - errsts = illegal_condition_result; - } else if (inj_dix && (0 == errsts)) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10, 1); - errsts = illegal_condition_result; - } - break; - case REPORT_LUNS: /* mandatory, ignore unit attention */ - delay_override = 1; - errsts = resp_report_luns(SCpnt, devip); - break; - case VERIFY: /* 10 byte SBC-2 command */ - errsts = check_readiness(SCpnt, UAS_TUR, devip); - break; - case WRITE_16: - case WRITE_12: - case WRITE_10: - /* WRITE{10,12,16} and DIF Type 2 are natural enemies */ - if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && - cmd[1] & 0xe0) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_COMMAND_OPCODE, 0); - errsts = check_condition_result; - break; - } - - if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || - scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && - (cmd[1] & 0xe0) == 0) - printk(KERN_ERR "Unprotected RD/WR to DIF device\n"); - - /* fall through */ - case WRITE_6: -write: - errsts = check_readiness(SCpnt, UAS_TUR, devip); - if (errsts) - break; - if (scsi_debug_fake_rw) - break; - get_data_transfer_info(cmd, &lba, &num, &ei_lba); - errsts = resp_write(SCpnt, lba, num, ei_lba); - if (inj_recovered && (0 == errsts)) { - mk_sense_buffer(SCpnt, RECOVERED_ERROR, - THRESHOLD_EXCEEDED, 0); - errsts = check_condition_result; - } else if (inj_dif && (0 == errsts)) { - mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, 1); - errsts = illegal_condition_result; - } else if (inj_dix && (0 == errsts)) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10, 1); - errsts = illegal_condition_result; - } - break; - case WRITE_SAME_16: - case WRITE_SAME: - if (cmd[1] & 0x8) { - if ((*cmd == WRITE_SAME_16 && scsi_debug_lbpws == 0) || - (*cmd == WRITE_SAME && scsi_debug_lbpws10 == 0)) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); - errsts = check_condition_result; - } else - unmap = 1; - } - if (errsts) - break; - errsts = check_readiness(SCpnt, UAS_TUR, devip); - if (errsts) - break; - if (scsi_debug_fake_rw) - break; - get_data_transfer_info(cmd, &lba, &num, &ei_lba); - errsts = resp_write_same(SCpnt, lba, num, ei_lba, unmap); - break; - case UNMAP: - errsts = check_readiness(SCpnt, UAS_TUR, devip); - if (errsts) - break; - if (scsi_debug_fake_rw) - break; - - if (scsi_debug_unmap_max_desc == 0 || scsi_debug_lbpu == 0) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_COMMAND_OPCODE, 0); - errsts = check_condition_result; - } else - errsts = resp_unmap(SCpnt, devip); - break; - case MODE_SENSE: - case MODE_SENSE_10: - errsts = resp_mode_sense(SCpnt, target, devip); - break; - case MODE_SELECT: - errsts = resp_mode_select(SCpnt, 1, devip); - break; - case MODE_SELECT_10: - errsts = resp_mode_select(SCpnt, 0, devip); - break; - case LOG_SENSE: - errsts = resp_log_sense(SCpnt, devip); - break; - case SYNCHRONIZE_CACHE: - delay_override = 1; - errsts = check_readiness(SCpnt, UAS_TUR, devip); - break; - case WRITE_BUFFER: - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - break; - case XDWRITEREAD_10: - if (!scsi_bidi_cmnd(SCpnt)) { - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); - errsts = check_condition_result; - break; - } - - errsts = check_readiness(SCpnt, UAS_TUR, devip); - if (errsts) - break; - if (scsi_debug_fake_rw) - break; - get_data_transfer_info(cmd, &lba, &num, &ei_lba); - errsts = resp_read(SCpnt, lba, num, ei_lba); - if (errsts) - break; - errsts = resp_write(SCpnt, lba, num, ei_lba); - if (errsts) - break; - errsts = resp_xdwriteread(SCpnt, lba, num, devip); - break; - case VARIABLE_LENGTH_CMD: - if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION) { - - if ((cmd[10] & 0xe0) == 0) - printk(KERN_ERR - "Unprotected RD/WR to DIF device\n"); - - if (cmd[9] == READ_32) { - BUG_ON(SCpnt->cmd_len < 32); - goto read; - } - - if (cmd[9] == WRITE_32) { - BUG_ON(SCpnt->cmd_len < 32); - goto write; - } - } - - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB, 0); - errsts = check_condition_result; - break; - case 0x85: - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - sdev_printk(KERN_INFO, SCpnt->device, - "%s: ATA PASS-THROUGH(16) not supported\n", my_name); - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, - INVALID_OPCODE, 0); - errsts = check_condition_result; - break; - default: - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - sdev_printk(KERN_INFO, SCpnt->device, - "%s: Opcode: 0x%x not supported\n", - my_name, *cmd); - errsts = check_readiness(SCpnt, UAS_ONLY, devip); - if (errsts) - break; /* Unit attention takes precedence */ - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, INVALID_OPCODE, 0); - errsts = check_condition_result; - break; - } - return schedule_resp(SCpnt, devip, errsts, - (delay_override ? 0 : scsi_debug_delay)); -} - -static int -sdebug_queuecommand_lock_or_not(struct Scsi_Host *shost, struct scsi_cmnd *cmd) -{ - if (scsi_debug_host_lock) { - unsigned long iflags; - int rc; - - spin_lock_irqsave(shost->host_lock, iflags); - rc = scsi_debug_queuecommand(cmd); - spin_unlock_irqrestore(shost->host_lock, iflags); - return rc; - } else - return scsi_debug_queuecommand(cmd); -} - -static int -sdebug_change_qdepth(struct scsi_device *sdev, int qdepth, int reason) +sdebug_change_qdepth(struct scsi_device *sdev, int qdepth) { int num_in_q = 0; - int bad = 0; unsigned long iflags; struct sdebug_dev_info *devip; @@ -4488,43 +4971,18 @@ sdebug_change_qdepth(struct scsi_device *sdev, int qdepth, int reason) } num_in_q = atomic_read(&devip->num_in_q); spin_unlock_irqrestore(&queued_arr_lock, iflags); - if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) { - if (qdepth < 1) - qdepth = 1; - /* allow to exceed max host queued_arr elements for testing */ - if (qdepth > SCSI_DEBUG_CANQUEUE + 10) - qdepth = SCSI_DEBUG_CANQUEUE + 10; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - } else if (reason == SCSI_QDEPTH_QFULL) - scsi_track_queue_full(sdev, qdepth); - else - bad = 1; - if (bad) - sdev_printk(KERN_WARNING, sdev, - "%s: unknown reason=0x%x\n", __func__, reason); + + if (qdepth < 1) + qdepth = 1; + /* allow to exceed max host queued_arr elements for testing */ + if (qdepth > SCSI_DEBUG_CANQUEUE + 10) + qdepth = SCSI_DEBUG_CANQUEUE + 10; + scsi_change_queue_depth(sdev, qdepth); + if (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts) { - if (SCSI_QDEPTH_QFULL == reason) - sdev_printk(KERN_INFO, sdev, - "%s: -> %d, num_in_q=%d, reason: queue full\n", - __func__, qdepth, num_in_q); - else { - const char *cp; - - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - cp = "default (sysfs ?)"; - break; - case SCSI_QDEPTH_RAMP_UP: - cp = "ramp up"; - break; - default: - cp = "unknown"; - break; - } - sdev_printk(KERN_INFO, sdev, - "%s: qdepth=%d, num_in_q=%d, reason: %s\n", - __func__, qdepth, num_in_q, cp); - } + sdev_printk(KERN_INFO, sdev, + "%s: qdepth=%d, num_in_q=%d\n", + __func__, qdepth, num_in_q); } return sdev->queue_depth; } @@ -4532,14 +4990,7 @@ sdebug_change_qdepth(struct scsi_device *sdev, int qdepth, int reason) static int sdebug_change_qtype(struct scsi_device *sdev, int qtype) { - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, qtype); - if (qtype) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - qtype = 0; + qtype = scsi_change_queue_type(sdev, qtype); if (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts) { const char *cp; @@ -4562,6 +5013,193 @@ sdebug_change_qtype(struct scsi_device *sdev, int qtype) return qtype; } +static int +check_inject(struct scsi_cmnd *scp) +{ + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + + memset(ep, 0, sizeof(struct sdebug_scmd_extra_t)); + + if (atomic_inc_return(&sdebug_cmnd_count) >= + abs(scsi_debug_every_nth)) { + atomic_set(&sdebug_cmnd_count, 0); + if (scsi_debug_every_nth < -1) + scsi_debug_every_nth = -1; + if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) + return 1; /* ignore command causing timeout */ + else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts && + scsi_medium_access_command(scp)) + return 1; /* time out reads and writes */ + if (sdebug_any_injecting_opt) { + int opts = scsi_debug_opts; + + if (SCSI_DEBUG_OPT_RECOVERED_ERR & opts) + ep->inj_recovered = true; + else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & opts) + ep->inj_transport = true; + else if (SCSI_DEBUG_OPT_DIF_ERR & opts) + ep->inj_dif = true; + else if (SCSI_DEBUG_OPT_DIX_ERR & opts) + ep->inj_dix = true; + else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & opts) + ep->inj_short = true; + } + } + return 0; +} + +static int +scsi_debug_queuecommand(struct scsi_cmnd *scp) +{ + u8 sdeb_i; + struct scsi_device *sdp = scp->device; + const struct opcode_info_t *oip; + const struct opcode_info_t *r_oip; + struct sdebug_dev_info *devip; + u8 *cmd = scp->cmnd; + int (*r_pfp)(struct scsi_cmnd *, struct sdebug_dev_info *); + int k, na; + int errsts = 0; + int errsts_no_connect = DID_NO_CONNECT << 16; + u32 flags; + u16 sa; + u8 opcode = cmd[0]; + bool has_wlun_rl; + bool debug = !!(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts); + + scsi_set_resid(scp, 0); + if (debug && !(SCSI_DEBUG_OPT_NO_CDB_NOISE & scsi_debug_opts)) { + char b[120]; + int n, len, sb; + + len = scp->cmd_len; + sb = (int)sizeof(b); + if (len > 32) + strcpy(b, "too long, over 32 bytes"); + else { + for (k = 0, n = 0; k < len && n < sb; ++k) + n += scnprintf(b + n, sb - n, "%02x ", + (u32)cmd[k]); + } + sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, b); + } + has_wlun_rl = (sdp->lun == SAM2_WLUN_REPORT_LUNS); + if ((sdp->lun >= scsi_debug_max_luns) && !has_wlun_rl) + return schedule_resp(scp, NULL, errsts_no_connect, 0); + + sdeb_i = opcode_ind_arr[opcode]; /* fully mapped */ + oip = &opcode_info_arr[sdeb_i]; /* safe if table consistent */ + devip = (struct sdebug_dev_info *)sdp->hostdata; + if (!devip) { + devip = devInfoReg(sdp); + if (NULL == devip) + return schedule_resp(scp, NULL, errsts_no_connect, 0); + } + na = oip->num_attached; + r_pfp = oip->pfp; + if (na) { /* multiple commands with this opcode */ + r_oip = oip; + if (FF_SA & r_oip->flags) { + if (F_SA_LOW & oip->flags) + sa = 0x1f & cmd[1]; + else + sa = get_unaligned_be16(cmd + 8); + for (k = 0; k <= na; oip = r_oip->arrp + k++) { + if (opcode == oip->opcode && sa == oip->sa) + break; + } + } else { /* since no service action only check opcode */ + for (k = 0; k <= na; oip = r_oip->arrp + k++) { + if (opcode == oip->opcode) + break; + } + } + if (k > na) { + if (F_SA_LOW & r_oip->flags) + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 4); + else if (F_SA_HIGH & r_oip->flags) + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, 7); + else + mk_sense_invalid_opcode(scp); + goto check_cond; + } + } /* else (when na==0) we assume the oip is a match */ + flags = oip->flags; + if (F_INV_OP & flags) { + mk_sense_invalid_opcode(scp); + goto check_cond; + } + if (has_wlun_rl && !(F_RL_WLUN_OK & flags)) { + if (debug) + sdev_printk(KERN_INFO, sdp, "scsi_debug: Opcode: " + "0x%x not supported for wlun\n", opcode); + mk_sense_invalid_opcode(scp); + goto check_cond; + } + if (scsi_debug_strict) { /* check cdb against mask */ + u8 rem; + int j; + + for (k = 1; k < oip->len_mask[0] && k < 16; ++k) { + rem = ~oip->len_mask[k] & cmd[k]; + if (rem) { + for (j = 7; j >= 0; --j, rem <<= 1) { + if (0x80 & rem) + break; + } + mk_sense_invalid_fld(scp, SDEB_IN_CDB, k, j); + goto check_cond; + } + } + } + if (!(F_SKIP_UA & flags) && + SDEBUG_NUM_UAS != find_first_bit(devip->uas_bm, SDEBUG_NUM_UAS)) { + errsts = check_readiness(scp, UAS_ONLY, devip); + if (errsts) + goto check_cond; + } + if ((F_M_ACCESS & flags) && devip->stopped) { + mk_sense_buffer(scp, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x2); + if (debug) + sdev_printk(KERN_INFO, sdp, "%s reports: Not ready: " + "%s\n", my_name, "initializing command " + "required"); + errsts = check_condition_result; + goto fini; + } + if (scsi_debug_fake_rw && (F_FAKE_RW & flags)) + goto fini; + if (scsi_debug_every_nth) { + if (check_inject(scp)) + return 0; /* ignore command: make trouble */ + } + if (oip->pfp) /* if this command has a resp_* function, call it */ + errsts = oip->pfp(scp, devip); + else if (r_pfp) /* if leaf function ptr NULL, try the root's */ + errsts = r_pfp(scp, devip); + +fini: + return schedule_resp(scp, devip, errsts, + ((F_DELAY_OVERR & flags) ? 0 : scsi_debug_delay)); +check_cond: + return schedule_resp(scp, devip, check_condition_result, 0); +} + +static int +sdebug_queuecommand_lock_or_not(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +{ + if (scsi_debug_host_lock) { + unsigned long iflags; + int rc; + + spin_lock_irqsave(shost->host_lock, iflags); + rc = scsi_debug_queuecommand(cmd); + spin_unlock_irqrestore(shost->host_lock, iflags); + return rc; + } else + return scsi_debug_queuecommand(cmd); +} + static struct scsi_host_template sdebug_driver_template = { .show_info = scsi_debug_show_info, .write_info = scsi_debug_write_info, @@ -4587,13 +5225,16 @@ static struct scsi_host_template sdebug_driver_template = { .max_sectors = -1U, .use_clustering = DISABLE_CLUSTERING, .module = THIS_MODULE, + .track_queue_depth = 1, + .cmd_size = sizeof(struct sdebug_scmd_extra_t), }; static int sdebug_driver_probe(struct device * dev) { - int error = 0; - struct sdebug_host_info *sdbg_host; - struct Scsi_Host *hpnt; + int error = 0; + int opts; + struct sdebug_host_info *sdbg_host; + struct Scsi_Host *hpnt; int host_prot; sdbg_host = to_sdebug_host(dev); @@ -4603,7 +5244,7 @@ static int sdebug_driver_probe(struct device * dev) sdebug_driver_template.use_clustering = ENABLE_CLUSTERING; hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host)); if (NULL == hpnt) { - printk(KERN_ERR "%s: scsi_register failed\n", __func__); + pr_err("%s: scsi_host_alloc failed\n", __func__); error = -ENODEV; return error; } @@ -4660,6 +5301,18 @@ static int sdebug_driver_probe(struct device * dev) else scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_CRC); + opts = scsi_debug_opts; + if (SCSI_DEBUG_OPT_RECOVERED_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_DIF_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_DIX_ERR & opts) + sdebug_any_injecting_opt = true; + else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & opts) + sdebug_any_injecting_opt = true; + error = scsi_add_host(hpnt, &sdbg_host->dev); if (error) { printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 49014a143c6a..c1d04d4d3c6c 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -202,6 +202,7 @@ static struct { {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, {"INSITE", "I325VM", NULL, BLIST_KEY}, + {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC}, {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index bc5ff6ff9c79..e42fff6e8c10 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -36,6 +36,7 @@ #include <scsi/scsi_transport.h> #include <scsi/scsi_host.h> #include <scsi/scsi_ioctl.h> +#include <scsi/sg.h> #include "scsi_priv.h" #include "scsi_logging.h" @@ -157,8 +158,9 @@ scmd_eh_abort_handler(struct work_struct *work) } else { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "scmd %p abort failed, rtn %d\n", - scmd, rtn)); + "scmd %p abort %s\n", scmd, + (rtn == FAST_IO_FAIL) ? + "not send" : "failed")); } } @@ -355,7 +357,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, if (cmd_cancel || cmd_failed) { SCSI_LOG_ERROR_RECOVERY(3, - sdev_printk(KERN_INFO, sdev, + shost_printk(KERN_INFO, shost, "%s: cmds failed: %d, cancel: %d\n", __func__, cmd_failed, cmd_cancel)); @@ -608,7 +610,7 @@ static void scsi_handle_queue_ramp_up(struct scsi_device *sdev) struct scsi_host_template *sht = sdev->host->hostt; struct scsi_device *tmp_sdev; - if (!sht->change_queue_depth || + if (!sht->track_queue_depth || sdev->queue_depth >= sdev->max_queue_depth) return; @@ -629,12 +631,8 @@ static void scsi_handle_queue_ramp_up(struct scsi_device *sdev) tmp_sdev->id != sdev->id || tmp_sdev->queue_depth == sdev->max_queue_depth) continue; - /* - * call back into LLD to increase queue_depth by one - * with ramp up reason code. - */ - sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1, - SCSI_QDEPTH_RAMP_UP); + + scsi_change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1); sdev->last_queue_ramp_up = jiffies; } } @@ -644,7 +642,7 @@ static void scsi_handle_queue_full(struct scsi_device *sdev) struct scsi_host_template *sht = sdev->host->hostt; struct scsi_device *tmp_sdev; - if (!sht->change_queue_depth) + if (!sht->track_queue_depth) return; shost_for_each_device(tmp_sdev, sdev->host) { @@ -656,8 +654,7 @@ static void scsi_handle_queue_full(struct scsi_device *sdev) * the device when we got the queue full so we start * from the highest possible value and work our way down. */ - sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1, - SCSI_QDEPTH_QFULL); + scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1); } } @@ -869,7 +866,24 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd) return rtn; } -static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt, struct scsi_cmnd *scmd) +/** + * scsi_try_to_abort_cmd - Ask host to abort a SCSI command + * @scmd: SCSI cmd used to send a target reset + * + * Return value: + * SUCCESS, FAILED, or FAST_IO_FAIL + * + * Notes: + * SUCCESS does not necessarily indicate that the command + * has been aborted; it only indicates that the LLDDs + * has cleared all references to that command. + * LLDDs should return FAILED only if an abort was required + * but could not be executed. LLDDs should return FAST_IO_FAIL + * if the device is temporarily unavailable (eg due to a + * link down on FibreChannel) + */ +static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt, + struct scsi_cmnd *scmd) { if (!hostt->eh_abort_handler) return FAILED; @@ -1156,9 +1170,9 @@ int scsi_eh_get_sense(struct list_head *work_q, shost = scmd->device->host; if (scsi_host_eh_past_deadline(shost)) { SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "skip %s, past eh deadline\n", - __func__)); + scmd_printk(KERN_INFO, scmd, + "%s: skip request sense, past eh deadline\n", + current->comm)); break; } if (status_byte(scmd->result) != CHECK_CONDITION) @@ -1180,7 +1194,7 @@ int scsi_eh_get_sense(struct list_head *work_q, SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, "sense requested for %p result %x\n", scmd, scmd->result)); - SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense("bh", scmd)); + SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense(scmd)); rtn = scsi_decide_disposition(scmd); @@ -1265,9 +1279,9 @@ static int scsi_eh_test_devices(struct list_head *cmd_list, /* Push items back onto work_q */ list_splice_init(cmd_list, work_q); SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, sdev->host, - "skip %s, past eh deadline", - __func__)); + sdev_printk(KERN_INFO, sdev, + "%s: skip test device, past eh deadline", + current->comm)); break; } } @@ -1318,21 +1332,20 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, if (scsi_host_eh_past_deadline(shost)) { list_splice_init(&check_list, work_q); SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "skip %s, past eh deadline\n", - __func__)); + scmd_printk(KERN_INFO, scmd, + "%s: skip aborting cmd, past eh deadline\n", + current->comm)); return list_empty(work_q); } SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "%s: aborting cmd: 0x%p\n", - current->comm, scmd)); + scmd_printk(KERN_INFO, scmd, + "%s: aborting cmd\n", current->comm)); rtn = scsi_try_to_abort_cmd(shost->hostt, scmd); if (rtn == FAILED) { SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "%s: aborting cmd failed: 0x%p\n", - current->comm, scmd)); + scmd_printk(KERN_INFO, scmd, + "%s: aborting cmd failed\n", + current->comm)); list_splice_init(&check_list, work_q); return list_empty(work_q); } @@ -1390,9 +1403,9 @@ static int scsi_eh_stu(struct Scsi_Host *shost, shost_for_each_device(sdev, shost) { if (scsi_host_eh_past_deadline(shost)) { SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "skip %s, past eh deadline\n", - __func__)); + sdev_printk(KERN_INFO, sdev, + "%s: skip START_UNIT, past eh deadline\n", + current->comm)); break; } stu_scmd = NULL; @@ -1407,9 +1420,9 @@ static int scsi_eh_stu(struct Scsi_Host *shost, continue; SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "%s: Sending START_UNIT to sdev: 0x%p\n", - current->comm, sdev)); + sdev_printk(KERN_INFO, sdev, + "%s: Sending START_UNIT\n", + current->comm)); if (!scsi_eh_try_stu(stu_scmd)) { if (!scsi_device_online(sdev) || @@ -1423,9 +1436,9 @@ static int scsi_eh_stu(struct Scsi_Host *shost, } } else { SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "%s: START_UNIT failed to sdev:" - " 0x%p\n", current->comm, sdev)); + sdev_printk(KERN_INFO, sdev, + "%s: START_UNIT failed\n", + current->comm)); } } @@ -1456,9 +1469,9 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, shost_for_each_device(sdev, shost) { if (scsi_host_eh_past_deadline(shost)) { SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "skip %s, past eh deadline\n", - __func__)); + sdev_printk(KERN_INFO, sdev, + "%s: skip BDR, past eh deadline\n", + current->comm)); break; } bdr_scmd = NULL; @@ -1472,9 +1485,8 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, continue; SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "%s: Sending BDR sdev: 0x%p\n", - current->comm, sdev)); + sdev_printk(KERN_INFO, sdev, + "%s: Sending BDR\n", current->comm)); rtn = scsi_try_bus_device_reset(bdr_scmd); if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { if (!scsi_device_online(sdev) || @@ -1490,9 +1502,8 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, } } else { SCSI_LOG_ERROR_RECOVERY(3, - shost_printk(KERN_INFO, shost, - "%s: BDR failed sdev: 0x%p\n", - current->comm, sdev)); + sdev_printk(KERN_INFO, sdev, + "%s: BDR failed\n", current->comm)); } } @@ -1528,8 +1539,8 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, list_splice_init(&tmp_list, work_q); SCSI_LOG_ERROR_RECOVERY(3, shost_printk(KERN_INFO, shost, - "skip %s, past eh deadline\n", - __func__)); + "%s: Skip target reset, past eh deadline\n", + current->comm)); return list_empty(work_q); } @@ -1591,8 +1602,8 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, list_splice_init(&check_list, work_q); SCSI_LOG_ERROR_RECOVERY(3, shost_printk(KERN_INFO, shost, - "skip %s, past eh deadline\n", - __func__)); + "%s: skip BRST, past eh deadline\n", + current->comm)); return list_empty(work_q); } @@ -2193,9 +2204,9 @@ int scsi_error_handler(void *data) */ if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) { SCSI_LOG_ERROR_RECOVERY(1, - printk(KERN_ERR "Error handler scsi_eh_%d " - "unable to autoresume\n", - shost->host_no)); + shost_printk(KERN_ERR, shost, + "scsi_eh_%d: unable to autoresume\n", + shost->host_no)); continue; } @@ -2296,42 +2307,34 @@ scsi_reset_provider_done_command(struct scsi_cmnd *scmd) { } -/* - * Function: scsi_reset_provider - * - * Purpose: Send requested reset to a bus or device at any phase. - * - * Arguments: device - device to send reset to - * flag - reset type (see scsi.h) - * - * Returns: SUCCESS/FAILURE. - * - * Notes: This is used by the SCSI Generic driver to provide - * Bus/Device reset capability. +/** + * scsi_ioctl_reset: explicitly reset a host/bus/target/device + * @dev: scsi_device to operate on + * @arg: reset type (see sg.h) */ int -scsi_reset_provider(struct scsi_device *dev, int flag) +scsi_ioctl_reset(struct scsi_device *dev, int __user *arg) { struct scsi_cmnd *scmd; struct Scsi_Host *shost = dev->host; struct request req; unsigned long flags; - int rtn; + int error = 0, rtn, val; - if (scsi_autopm_get_host(shost) < 0) - return FAILED; + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; - if (!get_device(&dev->sdev_gendev)) { - rtn = FAILED; - goto out_put_autopm_host; - } + error = get_user(val, arg); + if (error) + return error; + if (scsi_autopm_get_host(shost) < 0) + return -EIO; + + error = -EIO; scmd = scsi_get_command(dev, GFP_KERNEL); - if (!scmd) { - rtn = FAILED; - put_device(&dev->sdev_gendev); + if (!scmd) goto out_put_autopm_host; - } blk_rq_init(NULL, &req); scmd->request = &req; @@ -2349,29 +2352,37 @@ scsi_reset_provider(struct scsi_device *dev, int flag) shost->tmf_in_progress = 1; spin_unlock_irqrestore(shost->host_lock, flags); - switch (flag) { - case SCSI_TRY_RESET_DEVICE: + switch (val & ~SG_SCSI_RESET_NO_ESCALATE) { + case SG_SCSI_RESET_NOTHING: + rtn = SUCCESS; + break; + case SG_SCSI_RESET_DEVICE: rtn = scsi_try_bus_device_reset(scmd); - if (rtn == SUCCESS) + if (rtn == SUCCESS || (val & SG_SCSI_RESET_NO_ESCALATE)) break; /* FALLTHROUGH */ - case SCSI_TRY_RESET_TARGET: + case SG_SCSI_RESET_TARGET: rtn = scsi_try_target_reset(scmd); - if (rtn == SUCCESS) + if (rtn == SUCCESS || (val & SG_SCSI_RESET_NO_ESCALATE)) break; /* FALLTHROUGH */ - case SCSI_TRY_RESET_BUS: + case SG_SCSI_RESET_BUS: rtn = scsi_try_bus_reset(scmd); - if (rtn == SUCCESS) + if (rtn == SUCCESS || (val & SG_SCSI_RESET_NO_ESCALATE)) break; /* FALLTHROUGH */ - case SCSI_TRY_RESET_HOST: + case SG_SCSI_RESET_HOST: rtn = scsi_try_host_reset(scmd); - break; + if (rtn == SUCCESS) + break; default: + /* FALLTHROUGH */ rtn = FAILED; + break; } + error = (rtn == SUCCESS) ? 0 : -EIO; + spin_lock_irqsave(shost->host_lock, flags); shost->tmf_in_progress = 0; spin_unlock_irqrestore(shost->host_lock, flags); @@ -2385,15 +2396,15 @@ scsi_reset_provider(struct scsi_device *dev, int flag) "waking up host to restart after TMF\n")); wake_up(&shost->host_wait); - scsi_run_host_queues(shost); - scsi_next_command(scmd); + scsi_put_command(scmd); + out_put_autopm_host: scsi_autopm_put_host(shost); - return rtn; + return error; } -EXPORT_SYMBOL(scsi_reset_provider); +EXPORT_SYMBOL(scsi_ioctl_reset); /** * scsi_normalize_sense - normalize main elements from either fixed or @@ -2412,20 +2423,20 @@ EXPORT_SYMBOL(scsi_reset_provider); * responded to a SCSI command with the CHECK_CONDITION status. * * Return value: - * 1 if valid sense data information found, else 0; + * true if valid sense data information found, else false; */ -int scsi_normalize_sense(const u8 *sense_buffer, int sb_len, - struct scsi_sense_hdr *sshdr) +bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, + struct scsi_sense_hdr *sshdr) { if (!sense_buffer || !sb_len) - return 0; + return false; memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); sshdr->response_code = (sense_buffer[0] & 0x7f); if (!scsi_sense_valid(sshdr)) - return 0; + return false; if (sshdr->response_code >= 0x72) { /* @@ -2455,12 +2466,12 @@ int scsi_normalize_sense(const u8 *sense_buffer, int sb_len, } } - return 1; + return true; } EXPORT_SYMBOL(scsi_normalize_sense); -int scsi_command_normalize_sense(struct scsi_cmnd *cmd, - struct scsi_sense_hdr *sshdr) +bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd, + struct scsi_sense_hdr *sshdr) { return scsi_normalize_sense(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE, sshdr); diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 1aaaf43c6803..c4f7b56fa6f6 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -126,7 +126,7 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, sdev_printk(KERN_INFO, sdev, "ioctl_internal_command return code = %x\n", result); - scsi_print_sense_hdr(" ", &sshdr); + scsi_print_sense_hdr(sdev, NULL, &sshdr); break; } } @@ -200,19 +200,6 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) { char scsi_cmd[MAX_COMMAND_SIZE]; - /* No idea how this happens.... */ - if (!sdev) - return -ENXIO; - - /* - * If we are in the middle of error recovery, don't let anyone - * else try and use this device. Also, if error recovery fails, it - * may try and take the device offline, in which case all further - * access to the device is prohibited. - */ - if (!scsi_block_when_processing_errors(sdev)) - return -ENODEV; - /* Check for deprecated ioctls ... all the ioctls which don't * follow the new unique numbering scheme are deprecated */ switch (cmd) { @@ -273,6 +260,8 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) START_STOP_TIMEOUT, NORMAL_RETRIES); case SCSI_IOCTL_GET_PCI: return scsi_ioctl_get_pci(sdev, arg); + case SG_SCSI_RESET: + return scsi_ioctl_reset(sdev, arg); default: if (sdev->host->hostt->ioctl) return sdev->host->hostt->ioctl(sdev, cmd, arg); @@ -281,55 +270,20 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) } EXPORT_SYMBOL(scsi_ioctl); -/** - * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET - * @sdev: scsi device receiving ioctl - * @cmd: Must be SC_SCSI_RESET - * @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,BUS,HOST} - * @ndelay: file mode O_NDELAY flag +/* + * We can process a reset even when a device isn't fully operable. */ -int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, - void __user *arg, int ndelay) +int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev, int cmd, + bool ndelay) { - int val, result; - - /* The first set of iocts may be executed even if we're doing - * error processing, as long as the device was opened - * non-blocking */ - if (ndelay) { + if (cmd == SG_SCSI_RESET && ndelay) { if (scsi_host_in_recovery(sdev->host)) + return -EAGAIN; + } else { + if (!scsi_block_when_processing_errors(sdev)) return -ENODEV; - } else if (!scsi_block_when_processing_errors(sdev)) - return -ENODEV; - - switch (cmd) { - case SG_SCSI_RESET: - result = get_user(val, (int __user *)arg); - if (result) - return result; - if (val == SG_SCSI_RESET_NOTHING) - return 0; - switch (val) { - case SG_SCSI_RESET_DEVICE: - val = SCSI_TRY_RESET_DEVICE; - break; - case SG_SCSI_RESET_TARGET: - val = SCSI_TRY_RESET_TARGET; - break; - case SG_SCSI_RESET_BUS: - val = SCSI_TRY_RESET_BUS; - break; - case SG_SCSI_RESET_HOST: - val = SCSI_TRY_RESET_HOST; - break; - default: - return -EINVAL; - } - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - return (scsi_reset_provider(sdev, val) == - SUCCESS) ? 0 : -EIO; } - return -ENODEV; + + return 0; } -EXPORT_SYMBOL(scsi_nonblockable_ioctl); +EXPORT_SYMBOL_GPL(scsi_ioctl_block_when_processing_errors); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 50a6e1ac8d9c..7e3d954c9cac 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -22,6 +22,7 @@ #include <linux/hardirq.h> #include <linux/scatterlist.h> #include <linux/blk-mq.h> +#include <linux/ratelimit.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -47,7 +48,7 @@ struct scsi_host_sg_pool { mempool_t *pool; }; -#define SP(x) { x, "sgpool-" __stringify(x) } +#define SP(x) { .size = x, "sgpool-" __stringify(x) } #if (SCSI_MAX_SG_SEGMENTS < 32) #error SCSI_MAX_SG_SEGMENTS is too small (must be 32 or greater) #endif @@ -542,17 +543,6 @@ static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd) put_device(&sdev->sdev_gendev); } -void scsi_next_command(struct scsi_cmnd *cmd) -{ - struct scsi_device *sdev = cmd->device; - struct request_queue *q = sdev->request_queue; - - scsi_put_command(cmd); - scsi_run_queue(q); - - put_device(&sdev->sdev_gendev); -} - void scsi_run_host_queues(struct Scsi_Host *shost) { struct scsi_device *sdev; @@ -598,10 +588,10 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb, bool mq) __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, mq, scsi_sg_free); } -static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, - gfp_t gfp_mask, bool mq) +static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, bool mq) { struct scatterlist *first_chunk = NULL; + gfp_t gfp_mask = mq ? GFP_NOIO : GFP_ATOMIC; int ret; BUG_ON(!nents); @@ -730,8 +720,6 @@ static bool scsi_end_request(struct request *req, int error, kblockd_schedule_work(&sdev->requeue_work); else blk_mq_start_stopped_hw_queues(q, true); - - put_device(&sdev->sdev_gendev); } else { unsigned long flags; @@ -743,9 +731,12 @@ static bool scsi_end_request(struct request *req, int error, spin_unlock_irqrestore(q->queue_lock, flags); scsi_release_buffers(cmd); - scsi_next_command(cmd); + + scsi_put_command(cmd); + scsi_run_queue(q); } + put_device(&sdev->sdev_gendev); return false; } @@ -831,8 +822,8 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) struct request *req = cmd->request; int error = 0; struct scsi_sense_hdr sshdr; - int sense_valid = 0; - int sense_deferred = 0; + bool sense_valid = false; + int sense_deferred = 0, level = 0; enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY, ACTION_DELAYED_RETRY} action; unsigned long wait_for = (cmd->allowed + 1) * req->timeout; @@ -912,7 +903,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) if ((sshdr.asc == 0x0) && (sshdr.ascq == 0x1d)) ; else if (!(req->cmd_flags & REQ_QUIET)) - scsi_print_sense("", cmd); + scsi_print_sense(cmd); result = 0; /* BLOCK_PC may have set error */ error = 0; @@ -1039,10 +1030,24 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) case ACTION_FAIL: /* Give up and fail the remainder of the request */ if (!(req->cmd_flags & REQ_QUIET)) { - scsi_print_result(cmd); - if (driver_byte(result) & DRIVER_SENSE) - scsi_print_sense("", cmd); - scsi_print_command(cmd); + static DEFINE_RATELIMIT_STATE(_rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + + if (unlikely(scsi_logging_level)) + level = SCSI_LOG_LEVEL(SCSI_LOG_MLCOMPLETE_SHIFT, + SCSI_LOG_MLCOMPLETE_BITS); + + /* + * if logging is enabled the failure will be printed + * in scsi_log_completion(), so avoid duplicate messages + */ + if (!level && __ratelimit(&_rs)) { + scsi_print_result(cmd, NULL, FAILED); + if (driver_byte(result) & DRIVER_SENSE) + scsi_print_sense(cmd); + scsi_print_command(cmd); + } } if (!scsi_end_request(req, error, blk_rq_err_bytes(req), 0)) return; @@ -1072,8 +1077,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) } } -static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, - gfp_t gfp_mask) +static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb) { int count; @@ -1081,7 +1085,7 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, * If sg table allocation fails, requeue request later. */ if (unlikely(scsi_alloc_sgtable(sdb, req->nr_phys_segments, - gfp_mask, req->mq_ctx != NULL))) + req->mq_ctx != NULL))) return BLKPREP_DEFER; /* @@ -1106,7 +1110,7 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, * BLKPREP_DEFER if the failure is retryable * BLKPREP_KILL if the failure is fatal */ -int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) +int scsi_init_io(struct scsi_cmnd *cmd) { struct scsi_device *sdev = cmd->device; struct request *rq = cmd->request; @@ -1115,7 +1119,7 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) BUG_ON(!rq->nr_phys_segments); - error = scsi_init_sgtable(rq, &cmd->sdb, gfp_mask); + error = scsi_init_sgtable(rq, &cmd->sdb); if (error) goto err_exit; @@ -1131,8 +1135,7 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) rq->next_rq->special = bidi_sdb; } - error = scsi_init_sgtable(rq->next_rq, rq->next_rq->special, - GFP_ATOMIC); + error = scsi_init_sgtable(rq->next_rq, rq->next_rq->special); if (error) goto err_exit; } @@ -1144,7 +1147,7 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) BUG_ON(prot_sdb == NULL); ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio); - if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask, is_mq)) { + if (scsi_alloc_sgtable(prot_sdb, ivecs, is_mq)) { error = BLKPREP_DEFER; goto err_exit; } @@ -1213,7 +1216,7 @@ static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) * submit a request without an attached bio. */ if (req->bio) { - int ret = scsi_init_io(cmd, GFP_ATOMIC); + int ret = scsi_init_io(cmd); if (unlikely(ret)) return ret; } else { @@ -1638,6 +1641,87 @@ static void scsi_softirq_done(struct request *rq) } /** + * scsi_dispatch_command - Dispatch a command to the low-level driver. + * @cmd: command block we are dispatching. + * + * Return: nonzero return request was rejected and device's queue needs to be + * plugged. + */ +static int scsi_dispatch_cmd(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *host = cmd->device->host; + int rtn = 0; + + atomic_inc(&cmd->device->iorequest_cnt); + + /* check if the device is still usable */ + if (unlikely(cmd->device->sdev_state == SDEV_DEL)) { + /* in SDEV_DEL we error all commands. DID_NO_CONNECT + * returns an immediate error upwards, and signals + * that the device is no longer present */ + cmd->result = DID_NO_CONNECT << 16; + goto done; + } + + /* Check to see if the scsi lld made this device blocked. */ + if (unlikely(scsi_device_blocked(cmd->device))) { + /* + * in blocked state, the command is just put back on + * the device queue. The suspend state has already + * blocked the queue so future requests should not + * occur until the device transitions out of the + * suspend state. + */ + SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, + "queuecommand : device blocked\n")); + return SCSI_MLQUEUE_DEVICE_BUSY; + } + + /* Store the LUN value in cmnd, if needed. */ + if (cmd->device->lun_in_cdb) + cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) | + (cmd->device->lun << 5 & 0xe0); + + scsi_log_send(cmd); + + /* + * Before we queue this command, check if the command + * length exceeds what the host adapter can handle. + */ + if (cmd->cmd_len > cmd->device->host->max_cmd_len) { + SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, + "queuecommand : command too long. " + "cdb_size=%d host->max_cmd_len=%d\n", + cmd->cmd_len, cmd->device->host->max_cmd_len)); + cmd->result = (DID_ABORT << 16); + goto done; + } + + if (unlikely(host->shost_state == SHOST_DEL)) { + cmd->result = (DID_NO_CONNECT << 16); + goto done; + + } + + trace_scsi_dispatch_cmd_start(cmd); + rtn = host->hostt->queuecommand(host, cmd); + if (rtn) { + trace_scsi_dispatch_cmd_error(cmd, rtn); + if (rtn != SCSI_MLQUEUE_DEVICE_BUSY && + rtn != SCSI_MLQUEUE_TARGET_BUSY) + rtn = SCSI_MLQUEUE_HOST_BUSY; + + SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, + "queuecommand : request rejected\n")); + } + + return rtn; + done: + cmd->scsi_done(cmd); + return 0; +} + +/** * scsi_done - Invoke completion on finished SCSI command. * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives * ownership back to SCSI Core -- i.e. the LLDD has finished with it. @@ -1725,7 +1809,7 @@ static void scsi_request_fn(struct request_queue *q) * we add the dev to the starved list so it eventually gets * a run when a tag is freed. */ - if (blk_queue_tagged(q) && !blk_rq_tagged(req)) { + if (blk_queue_tagged(q) && !(req->cmd_flags & REQ_QUEUED)) { spin_lock_irq(shost->host_lock); if (list_empty(&sdev->starved_entry)) list_add_tail(&sdev->starved_entry, @@ -1739,6 +1823,11 @@ static void scsi_request_fn(struct request_queue *q) if (!scsi_host_queue_ready(q, shost, sdev)) goto host_not_ready; + + if (sdev->simple_tags) + cmd->flags |= SCMD_TAGGED; + else + cmd->flags &= ~SCMD_TAGGED; /* * Finally, initialize any error handling parameters, and set up @@ -1893,10 +1982,10 @@ static int scsi_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req, blk_mq_start_request(req); } - if (blk_queue_tagged(q)) - req->cmd_flags |= REQ_QUEUED; + if (sdev->simple_tags) + cmd->flags |= SCMD_TAGGED; else - req->cmd_flags &= ~REQ_QUEUED; + cmd->flags &= ~SCMD_TAGGED; scsi_init_cmd_errh(cmd); cmd->scsi_done = scsi_mq_done; @@ -2091,7 +2180,7 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost) memset(&shost->tag_set, 0, sizeof(shost->tag_set)); shost->tag_set.ops = &scsi_mq_ops; - shost->tag_set.nr_hw_queues = 1; + shost->tag_set.nr_hw_queues = shost->nr_hw_queues ? : 1; shost->tag_set.queue_depth = shost->can_queue; shost->tag_set.cmd_size = cmd_size; shost->tag_set.numa_node = NUMA_NO_NODE; diff --git a/drivers/scsi/scsi_logging.h b/drivers/scsi/scsi_logging.h index 1f65139e14f8..7fe64a847143 100644 --- a/drivers/scsi/scsi_logging.h +++ b/drivers/scsi/scsi_logging.h @@ -51,6 +51,7 @@ do { \ } while (0); \ } while (0) #else +#define SCSI_LOG_LEVEL(SHIFT, BITS) 0 #define SCSI_CHECK_LOGGING(SHIFT, BITS, LEVEL, CMD) #endif /* CONFIG_SCSI_LOGGING */ diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 12b8e1bee7f0..2dc4a83fb84c 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -29,7 +29,6 @@ extern int scsi_init_hosts(void); extern void scsi_exit_hosts(void); /* scsi.c */ -extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd); extern int scsi_setup_command_freelist(struct Scsi_Host *shost); extern void scsi_destroy_command_freelist(struct Scsi_Host *shost); #ifdef CONFIG_SCSI_LOGGING @@ -84,7 +83,6 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd); extern int scsi_maybe_unblock_host(struct scsi_device *sdev); extern void scsi_device_unbusy(struct scsi_device *sdev); extern void scsi_queue_insert(struct scsi_cmnd *cmd, int reason); -extern void scsi_next_command(struct scsi_cmnd *cmd); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_run_host_queues(struct Scsi_Host *shost); extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index ba3f1e8d0d57..983aed10ff2f 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -286,7 +286,13 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, } WARN_ON_ONCE(!blk_get_queue(sdev->request_queue)); sdev->request_queue->queuedata = sdev; - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + + if (!shost_use_blk_mq(sdev->host) && + (shost->bqt || shost->hostt->use_blk_tags)) { + blk_queue_init_tags(sdev->request_queue, + sdev->host->cmd_per_lun, shost->bqt); + } + scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun); scsi_sysfs_device_initialize(sdev); @@ -874,8 +880,10 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, (inq_result[3] & 0x0f) == 1 ? " CCS" : ""); if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && - !(*bflags & BLIST_NOTQ)) + !(*bflags & BLIST_NOTQ)) { sdev->tagged_supported = 1; + sdev->simple_tags = 1; + } /* * Some devices (Texel CD ROM drives) have handshaking problems @@ -1214,9 +1222,9 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, sparse_lun = 0; /* - * If less than SCSI_1_CSS, and no special lun scaning, stop + * If less than SCSI_1_CCS, and no special lun scanning, stop * scanning; this matches 2.4 behaviour, but could just be a bug - * (to continue scanning a SCSI_1_CSS device). + * (to continue scanning a SCSI_1_CCS device). * * This test is broken. We might not have any device on lun0 for * a sparselun device, and if that's the case then how would we @@ -1585,16 +1593,15 @@ EXPORT_SYMBOL(scsi_add_device); void scsi_rescan_device(struct device *dev) { - struct scsi_driver *drv; - if (!dev->driver) return; - drv = to_scsi_driver(dev->driver); - if (try_module_get(drv->owner)) { + if (try_module_get(dev->driver->owner)) { + struct scsi_driver *drv = to_scsi_driver(dev->driver); + if (drv->rescan) drv->rescan(dev); - module_put(drv->owner); + module_put(dev->driver->owner); } } EXPORT_SYMBOL(scsi_rescan_device); @@ -1727,7 +1734,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) || - ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun))) + ((lun != SCAN_WILD_CARD) && (lun >= shost->max_lun))) return -EINVAL; mutex_lock(&shost->scan_mutex); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index f4cb7b3e9e23..1cb64a8e18c9 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -727,9 +727,7 @@ show_queue_type_field(struct device *dev, struct device_attribute *attr, struct scsi_device *sdev = to_scsi_device(dev); const char *name = "none"; - if (sdev->ordered_tags) - name = "ordered"; - else if (sdev->simple_tags) + if (sdev->simple_tags) name = "simple"; return snprintf(buf, 20, "%s\n", name); @@ -747,9 +745,12 @@ store_queue_type_field(struct device *dev, struct device_attribute *attr, if (!sdev->tagged_supported || !sht->change_queue_type) return -EINVAL; - if (strncmp(buf, "ordered", 7) == 0) - tag_type = MSG_ORDERED_TAG; - else if (strncmp(buf, "simple", 6) == 0) + /* + * We're never issueing order tags these days, but allow the value + * for backwards compatibility. + */ + if (strncmp(buf, "ordered", 7) == 0 || + strncmp(buf, "simple", 6) == 0) tag_type = MSG_SIMPLE_TAG; else if (strncmp(buf, "none", 4) != 0) return -EINVAL; @@ -876,11 +877,10 @@ sdev_store_queue_depth(struct device *dev, struct device_attribute *attr, depth = simple_strtoul(buf, NULL, 0); - if (depth < 1) + if (depth < 1 || depth > sht->can_queue) return -EINVAL; - retval = sht->change_queue_depth(sdev, depth, - SCSI_QDEPTH_DEFAULT); + retval = sht->change_queue_depth(sdev, depth); if (retval < 0) return retval; diff --git a/drivers/scsi/scsi_trace.c b/drivers/scsi/scsi_trace.c index 503594e5f76d..82af28b90294 100644 --- a/drivers/scsi/scsi_trace.c +++ b/drivers/scsi/scsi_trace.c @@ -278,7 +278,7 @@ scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len) return scsi_trace_rw16(p, cdb, len); case UNMAP: return scsi_trace_unmap(p, cdb, len); - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: return scsi_trace_service_action_in(p, cdb, len); case VARIABLE_LENGTH_CMD: return scsi_trace_varlen(p, cdb, len); diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index cf08071a9b6e..fa2aece76cc2 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -32,6 +32,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_eh.h> +#include <scsi/scsi_tcq.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_spi.h> @@ -1207,6 +1208,28 @@ int spi_populate_ppr_msg(unsigned char *msg, int period, int offset, } EXPORT_SYMBOL_GPL(spi_populate_ppr_msg); +/** + * spi_populate_tag_msg - place a tag message in a buffer + * @msg: pointer to the area to place the tag + * @cmd: pointer to the scsi command for the tag + * + * Notes: + * designed to create the correct type of tag message for the + * particular request. Returns the size of the tag message. + * May return 0 if TCQ is disabled for this device. + **/ +int spi_populate_tag_msg(unsigned char *msg, struct scsi_cmnd *cmd) +{ + if (cmd->flags & SCMD_TAGGED) { + *msg++ = MSG_SIMPLE_TAG; + *msg++ = cmd->request->tag; + return 2; + } + + return 0; +} +EXPORT_SYMBOL_GPL(spi_populate_tag_msg); + #ifdef CONFIG_SCSI_CONSTANTS static const char * const one_byte_msgs[] = { /* 0x00 */ "Task Complete", NULL /* Extended Message */, "Save Pointers", diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c index 92d24d6dcb39..910f4a7a3924 100644 --- a/drivers/scsi/scsicam.c +++ b/drivers/scsi/scsicam.c @@ -163,8 +163,8 @@ int scsi_partsize(unsigned char *buf, unsigned long capacity, end_head * end_sector + end_sector; /* This is the actual _sector_ number at the end */ - logical_end = get_unaligned(&largest->start_sect) - + get_unaligned(&largest->nr_sects); + logical_end = get_unaligned_le32(&largest->start_sect) + + get_unaligned_le32(&largest->nr_sects); /* This is for >1023 cylinders */ ext_cyl = (logical_end - (end_head * end_sector + end_sector)) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index cfba74cd8e8b..fedab3c21ddf 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -116,7 +116,7 @@ static int sd_eh_action(struct scsi_cmnd *, int); static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer); static void scsi_disk_release(struct device *cdev); static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *); -static void sd_print_result(struct scsi_disk *, int); +static void sd_print_result(const struct scsi_disk *, const char *, int); static DEFINE_SPINLOCK(sd_index_lock); static DEFINE_IDA(sd_index_ida); @@ -510,9 +510,9 @@ static const struct dev_pm_ops sd_pm_ops = { }; static struct scsi_driver sd_template = { - .owner = THIS_MODULE, .gendrv = { .name = "sd", + .owner = THIS_MODULE, .probe = sd_probe, .remove = sd_remove, .shutdown = sd_shutdown, @@ -656,7 +656,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) unsigned int logical_block_size = sdkp->device->sector_size; unsigned int max_blocks = 0; - q->limits.discard_zeroes_data = sdkp->lbprz; + q->limits.discard_zeroes_data = 0; q->limits.discard_alignment = sdkp->unmap_alignment * logical_block_size; q->limits.discard_granularity = @@ -680,11 +680,13 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) case SD_LBP_WS16: max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)SD_MAX_WS16_BLOCKS); + q->limits.discard_zeroes_data = sdkp->lbprz; break; case SD_LBP_WS10: max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)SD_MAX_WS10_BLOCKS); + q->limits.discard_zeroes_data = sdkp->lbprz; break; case SD_LBP_ZERO: @@ -784,7 +786,7 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) * amount of blocks described by the request. */ blk_add_request_payload(rq, page, len); - ret = scsi_init_io(cmd, GFP_ATOMIC); + ret = scsi_init_io(cmd); rq->__data_len = nr_bytes; out: @@ -878,7 +880,7 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) * knows how much to actually write. */ rq->__data_len = sdp->sector_size; - ret = scsi_init_io(cmd, GFP_ATOMIC); + ret = scsi_init_io(cmd); rq->__data_len = nr_bytes; return ret; } @@ -912,7 +914,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) int ret; unsigned char protect; - ret = scsi_init_io(SCpnt, GFP_ATOMIC); + ret = scsi_init_io(SCpnt); if (ret != BLKPREP_OK) goto out; SCpnt = rq->special; @@ -1334,9 +1336,9 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, * may try and take the device offline, in which case all further * access to the device is prohibited. */ - error = scsi_nonblockable_ioctl(sdp, cmd, p, - (mode & FMODE_NDELAY) != 0); - if (!scsi_block_when_processing_errors(sdp) || !error) + error = scsi_ioctl_block_when_processing_errors(sdp, cmd, + (mode & FMODE_NDELAY) != 0); + if (error) goto out; /* @@ -1492,7 +1494,7 @@ static int sd_sync_cache(struct scsi_disk *sdkp) } if (res) { - sd_print_result(sdkp, res); + sd_print_result(sdkp, "Synchronize Cache(10) failed", res); if (driver_byte(res) & DRIVER_SENSE) sd_print_sense_hdr(sdkp, &sshdr); @@ -1541,31 +1543,19 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device; - int ret; - - ret = scsi_verify_blk_ioctl(bdev, cmd); - if (ret < 0) - return ret; + int error; - /* - * If we are in the middle of error recovery, don't let anyone - * else try and use this device. Also, if error recovery fails, it - * may try and take the device offline, in which case all further - * access to the device is prohibited. - */ - if (!scsi_block_when_processing_errors(sdev)) - return -ENODEV; + error = scsi_ioctl_block_when_processing_errors(sdev, cmd, + (mode & FMODE_NDELAY) != 0); + if (error) + return error; - if (sdev->host->hostt->compat_ioctl) { - ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); - - return ret; - } - /* * Let the static ioctl translation table take care of it. */ - return -ENOIOCTLCMD; + if (!sdev->host->hostt->compat_ioctl) + return -ENOIOCTLCMD; + return sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); } #endif @@ -1713,17 +1703,6 @@ static int sd_done(struct scsi_cmnd *SCpnt) if (sense_valid) sense_deferred = scsi_sense_is_deferred(&sshdr); } -#ifdef CONFIG_SCSI_LOGGING - SCSI_LOG_HLCOMPLETE(1, scsi_print_result(SCpnt)); - if (sense_valid) { - SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt, - "sd_done: sb[respc,sk,asc," - "ascq]=%x,%x,%x,%x\n", - sshdr.response_code, - sshdr.sense_key, sshdr.asc, - sshdr.ascq)); - } -#endif sdkp->medium_access_timed_out = 0; if (driver_byte(result) != DRIVER_SENSE && @@ -1743,7 +1722,6 @@ static int sd_done(struct scsi_cmnd *SCpnt) * unknown amount of data was transferred so treat it as an * error. */ - scsi_print_sense("sd", SCpnt); SCpnt->result = 0; memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); break; @@ -1779,6 +1757,10 @@ static int sd_done(struct scsi_cmnd *SCpnt) break; } out: + SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt, + "sd_done: completed %d of %d bytes\n", + good_bytes, scsi_bufflen(SCpnt))); + if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt)) sd_dif_complete(SCpnt, good_bytes); @@ -1834,12 +1816,12 @@ sd_spinup_disk(struct scsi_disk *sdkp) /* no sense, TUR either succeeded or failed * with a status error */ if(!spintime && !scsi_status_is_good(the_result)) { - sd_printk(KERN_NOTICE, sdkp, "Unit Not Ready\n"); - sd_print_result(sdkp, the_result); + sd_print_result(sdkp, "Test Unit Ready failed", + the_result); } break; } - + /* * The device does not want the automatic start to be issued. */ @@ -1955,7 +1937,6 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, struct scsi_sense_hdr *sshdr, int sense_valid, int the_result) { - sd_print_result(sdkp, the_result); if (driver_byte(the_result) & DRIVER_SENSE) sd_print_sense_hdr(sdkp, sshdr); else @@ -2001,7 +1982,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, do { memset(cmd, 0, 16); - cmd[0] = SERVICE_ACTION_IN; + cmd[0] = SERVICE_ACTION_IN_16; cmd[1] = SAI_READ_CAPACITY_16; cmd[13] = RC16_LEN; memset(buffer, 0, RC16_LEN); @@ -2036,7 +2017,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, } while (the_result && retries); if (the_result) { - sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY(16) failed\n"); + sd_print_result(sdkp, "Read Capacity(16) failed", the_result); read_capacity_error(sdkp, sdp, &sshdr, sense_valid, the_result); return -EINVAL; } @@ -2118,7 +2099,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, } while (the_result && retries); if (the_result) { - sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY failed\n"); + sd_print_result(sdkp, "Read Capacity(10) failed", the_result); read_capacity_error(sdkp, sdp, &sshdr, sense_valid, the_result); return -EINVAL; } @@ -2643,12 +2624,12 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) } else { /* LBP VPD page tells us what to use */ - if (sdkp->lbpu && sdkp->max_unmap_blocks) - sd_config_discard(sdkp, SD_LBP_UNMAP); - else if (sdkp->lbpws) + if (sdkp->lbpws) sd_config_discard(sdkp, SD_LBP_WS16); else if (sdkp->lbpws10) sd_config_discard(sdkp, SD_LBP_WS10); + else if (sdkp->lbpu && sdkp->max_unmap_blocks) + sd_config_discard(sdkp, SD_LBP_UNMAP); else sd_config_discard(sdkp, SD_LBP_DISABLE); } @@ -3142,8 +3123,7 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start) res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM); if (res) { - sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n"); - sd_print_result(sdkp, res); + sd_print_result(sdkp, "Start/Stop Unit failed", res); if (driver_byte(res) & DRIVER_SENSE) sd_print_sense_hdr(sdkp, &sshdr); if (scsi_sense_valid(&sshdr) && @@ -3337,15 +3317,27 @@ module_exit(exit_sd); static void sd_print_sense_hdr(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { - sd_printk(KERN_INFO, sdkp, " "); - scsi_show_sense_hdr(sshdr); - sd_printk(KERN_INFO, sdkp, " "); - scsi_show_extd_sense(sshdr->asc, sshdr->ascq); + scsi_show_sense_hdr(sdkp->device, + sdkp->disk ? sdkp->disk->disk_name : NULL, sshdr); + scsi_show_extd_sense(sdkp->device, + sdkp->disk ? sdkp->disk->disk_name : NULL, + sshdr->asc, sshdr->ascq); } -static void sd_print_result(struct scsi_disk *sdkp, int result) +static void sd_print_result(const struct scsi_disk *sdkp, const char *msg, + int result) { - sd_printk(KERN_INFO, sdkp, " "); - scsi_show_result(result); + const char *hb_string = scsi_hostbyte_string(result); + const char *db_string = scsi_driverbyte_string(result); + + if (hb_string || db_string) + sd_printk(KERN_INFO, sdkp, + "%s: Result: hostbyte=%s driverbyte=%s\n", msg, + hb_string ? hb_string : "invalid", + db_string ? db_string : "invalid"); + else + sd_printk(KERN_INFO, sdkp, + "%s: Result: hostbyte=0x%02x driverbyte=0x%02x\n", + msg, host_byte(result), driver_byte(result)); } diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 467377884b63..63ba5ca7f9a1 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -103,9 +103,9 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk) #define sd_printk(prefix, sdsk, fmt, a...) \ (sdsk)->disk ? \ - sdev_printk(prefix, (sdsk)->device, "[%s] " fmt, \ - (sdsk)->disk->disk_name, ##a) : \ - sdev_printk(prefix, (sdsk)->device, fmt, ##a) + sdev_prefix_printk(prefix, (sdsk)->device, \ + (sdsk)->disk->disk_name, fmt, ##a) : \ + sdev_printk(prefix, (sdsk)->device, fmt, ##a) #define sd_first_printk(prefix, sdsk, fmt, a...) \ do { \ diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 80bfece1a2de..b7e79e7646ad 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -693,9 +693,9 @@ static struct class_interface ses_interface = { }; static struct scsi_driver ses_template = { - .owner = THIS_MODULE, .gendrv = { .name = "ses", + .owner = THIS_MODULE, .probe = ses_probe, .remove = ses_remove, }, diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 60354449d9ed..b14f64cb9724 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -219,8 +219,8 @@ static void sg_device_destroy(struct kref *kref); #define SZ_SG_REQ_INFO sizeof(sg_req_info_t) #define sg_printk(prefix, sdp, fmt, a...) \ - sdev_printk(prefix, (sdp)->device, "[%s] " fmt, \ - (sdp)->disk->disk_name, ##a) + sdev_prefix_printk(prefix, (sdp)->device, \ + (sdp)->disk->disk_name, fmt, ##a) static int sg_allow_access(struct file *filp, unsigned char *cmd) { @@ -1071,39 +1071,6 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (atomic_read(&sdp->detaching)) return -ENODEV; return put_user(sdp->device->host->hostt->emulated, ip); - case SG_SCSI_RESET: - if (atomic_read(&sdp->detaching)) - return -ENODEV; - if (filp->f_flags & O_NONBLOCK) { - if (scsi_host_in_recovery(sdp->device->host)) - return -EBUSY; - } else if (!scsi_block_when_processing_errors(sdp->device)) - return -EBUSY; - result = get_user(val, ip); - if (result) - return result; - if (SG_SCSI_RESET_NOTHING == val) - return 0; - switch (val) { - case SG_SCSI_RESET_DEVICE: - val = SCSI_TRY_RESET_DEVICE; - break; - case SG_SCSI_RESET_TARGET: - val = SCSI_TRY_RESET_TARGET; - break; - case SG_SCSI_RESET_BUS: - val = SCSI_TRY_RESET_BUS; - break; - case SG_SCSI_RESET_HOST: - val = SCSI_TRY_RESET_HOST; - break; - default: - return -EINVAL; - } - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - return (scsi_reset_provider(sdp->device, val) == - SUCCESS) ? 0 : -EIO; case SCSI_IOCTL_SEND_COMMAND: if (atomic_read(&sdp->detaching)) return -ENODEV; @@ -1123,13 +1090,6 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return result; sdp->sgdebug = (char) val; return 0; - case SCSI_IOCTL_GET_IDLUN: - case SCSI_IOCTL_GET_BUS_NUMBER: - case SCSI_IOCTL_PROBE_HOST: - case SG_GET_TRANSFORM: - if (atomic_read(&sdp->detaching)) - return -ENODEV; - return scsi_ioctl(sdp->device, cmd_in, p); case BLKSECTGET: return put_user(max_sectors_bytes(sdp->device->request_queue), ip); @@ -1145,11 +1105,25 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return blk_trace_startstop(sdp->device->request_queue, 0); case BLKTRACETEARDOWN: return blk_trace_remove(sdp->device->request_queue); + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_PROBE_HOST: + case SG_GET_TRANSFORM: + case SG_SCSI_RESET: + if (atomic_read(&sdp->detaching)) + return -ENODEV; + break; default: if (read_only) return -EPERM; /* don't know so take safe approach */ - return scsi_ioctl(sdp->device, cmd_in, p); + break; } + + result = scsi_ioctl_block_when_processing_errors(sdp->device, + cmd_in, filp->f_flags & O_NDELAY); + if (result) + return result; + return scsi_ioctl(sdp->device, cmd_in, p); } #ifdef CONFIG_COMPAT @@ -1360,7 +1334,7 @@ sg_rq_end_io(struct request *rq, int uptodate) if ((sdp->sgdebug > 0) && ((CHECK_CONDITION == srp->header.masked_status) || (COMMAND_TERMINATED == srp->header.masked_status))) - __scsi_print_sense(__func__, sense, + __scsi_print_sense(sdp->device, __func__, sense, SCSI_SENSE_BUFFERSIZE); /* Following if statement is a patch supplied by Eric Youngdale */ diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 2de44cc58b1a..8bd54a64efd6 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -88,9 +88,9 @@ static struct dev_pm_ops sr_pm_ops = { }; static struct scsi_driver sr_template = { - .owner = THIS_MODULE, .gendrv = { .name = "sr", + .owner = THIS_MODULE, .probe = sr_probe, .remove = sr_remove, .pm = &sr_pm_ops, @@ -387,7 +387,7 @@ static int sr_init_command(struct scsi_cmnd *SCpnt) struct request *rq = SCpnt->request; int ret; - ret = scsi_init_io(SCpnt, GFP_ATOMIC); + ret = scsi_init_io(SCpnt); if (ret != BLKPREP_OK) goto out; SCpnt = rq->special; @@ -549,6 +549,11 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, mutex_lock(&sr_mutex); + ret = scsi_ioctl_block_when_processing_errors(sdev, cmd, + (mode & FMODE_NDELAY) != 0); + if (ret) + goto out; + /* * Send SCSI addressing ioctls directly to mid level, send other * ioctls to cdrom/block level. @@ -564,16 +569,6 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, if (ret != -ENOSYS) goto out; - /* - * ENODEV means that we didn't recognise the ioctl, or that we - * cannot execute it in the current device state. In either - * case fall through to scsi_ioctl, which will return ENDOEV again - * if it doesn't recognise the ioctl - */ - ret = scsi_nonblockable_ioctl(sdev, cmd, argp, - (mode & FMODE_NDELAY) != 0); - if (ret != -ENODEV) - goto out; ret = scsi_ioctl(sdev, cmd, argp); out: diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index 1d1f6f416c59..1de33719ad8e 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -57,8 +57,7 @@ typedef struct scsi_cd { } Scsi_CD; #define sr_printk(prefix, cd, fmt, a...) \ - sdev_printk(prefix, (cd)->device, "[%s] " fmt, \ - (cd)->cdi.name, ##a) + sdev_prefix_printk(prefix, (cd)->device, (cd)->cdi.name, fmt, ##a) int sr_do_ioctl(Scsi_CD *, struct packet_command *); diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index 6389fcff12ec..fb929fac22ba 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -246,7 +246,7 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) "CDROM not ready. Make sure there " "is a disc in the drive.\n"); #ifdef DEBUG - scsi_print_sense_hdr("sr", &sshdr); + scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr); #endif err = -ENOMEDIUM; break; @@ -257,15 +257,15 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) /* sense: Invalid command operation code */ err = -EDRIVE_CANT_DO_THIS; #ifdef DEBUG - __scsi_print_command(cgc->cmd); - scsi_print_sense_hdr("sr", &sshdr); + __scsi_print_command(cgc->cmd, CDROM_PACKET_SIZE); + scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr); #endif break; default: sr_printk(KERN_ERR, cd, "CDROM (ioctl) error, command: "); - __scsi_print_command(cgc->cmd); - scsi_print_sense_hdr("sr", &sshdr); + __scsi_print_command(cgc->cmd, CDROM_PACKET_SIZE); + scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr); err = -EIO; } } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 4daa372ed381..128d3b55bdd9 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -56,7 +56,8 @@ static const char *verstr = "20101219"; /* The driver prints some debugging information on the console if DEBUG is defined and non-zero. */ -#define DEBUG 0 +#define DEBUG 1 +#define NO_DEBUG 0 #define ST_DEB_MSG KERN_NOTICE #if DEBUG @@ -80,6 +81,7 @@ static int max_sg_segs; static int try_direct_io = TRY_DIRECT_IO; static int try_rdio = 1; static int try_wdio = 1; +static int debug_flag; static struct class st_sysfs_class; static const struct attribute_group *st_dev_groups[]; @@ -100,6 +102,9 @@ module_param_named(max_sg_segs, max_sg_segs, int, 0); MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)"); module_param_named(try_direct_io, try_direct_io, int, 0); MODULE_PARM_DESC(try_direct_io, "Try direct I/O between user buffer and tape drive (1)"); +module_param_named(debug_flag, debug_flag, int, 0); +MODULE_PARM_DESC(debug_flag, "Enable DEBUG, same as setting debugging=1"); + /* Extra parameters for testing */ module_param_named(try_rdio, try_rdio, int, 0); @@ -124,6 +129,9 @@ static struct st_dev_parm { }, { "try_direct_io", &try_direct_io + }, + { + "debug_flag", &debug_flag } }; #endif @@ -194,9 +202,9 @@ static int do_create_sysfs_files(void); static void do_remove_sysfs_files(void); static struct scsi_driver st_template = { - .owner = THIS_MODULE, .gendrv = { .name = "st", + .owner = THIS_MODULE, .probe = st_probe, .remove = st_remove, }, @@ -306,8 +314,7 @@ static inline char *tape_name(struct scsi_tape *tape) } #define st_printk(prefix, t, fmt, a...) \ - sdev_printk(prefix, (t)->device, "%s: " fmt, \ - tape_name(t), ##a) + sdev_prefix_printk(prefix, (t)->device, tape_name(t), fmt, ##a) #ifdef DEBUG #define DEBC_printk(t, fmt, a...) \ if (debugging) { st_printk(ST_DEB_MSG, t, fmt, ##a ); } @@ -374,7 +381,8 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2], SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]); if (cmdstatp->have_sense) - __scsi_print_sense(name, SRpnt->sense, SCSI_SENSE_BUFFERSIZE); + __scsi_print_sense(STp->device, name, + SRpnt->sense, SCSI_SENSE_BUFFERSIZE); } ) /* end DEB */ if (!debugging) { /* Abnormal conditions for tape */ if (!cmdstatp->have_sense) @@ -390,7 +398,8 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) SRpnt->cmd[0] != MODE_SENSE && SRpnt->cmd[0] != TEST_UNIT_READY) { - __scsi_print_sense(name, SRpnt->sense, SCSI_SENSE_BUFFERSIZE); + __scsi_print_sense(STp->device, name, + SRpnt->sense, SCSI_SENSE_BUFFERSIZE); } } @@ -852,17 +861,16 @@ static int set_mode_densblk(struct scsi_tape * STp, struct st_modedef * STm) /* Lock or unlock the drive door. Don't use when st_request allocated. */ static int do_door_lock(struct scsi_tape * STp, int do_lock) { - int retval, cmd; + int retval; - cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK; DEBC_printk(STp, "%socking drive door.\n", do_lock ? "L" : "Unl"); - retval = scsi_ioctl(STp->device, cmd, NULL); - if (!retval) { + + retval = scsi_set_medium_removal(STp->device, + do_lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW); + if (!retval) STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED; - } - else { + else STp->door_locked = ST_LOCK_FAILS; - } return retval; } @@ -3367,11 +3375,10 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) * may try and take the device offline, in which case all further * access to the device is prohibited. */ - retval = scsi_nonblockable_ioctl(STp->device, cmd_in, p, - file->f_flags & O_NDELAY); - if (!scsi_block_when_processing_errors(STp->device) || retval != -ENODEV) + retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in, + file->f_flags & O_NDELAY); + if (retval) goto out; - retval = 0; cmd_type = _IOC_TYPE(cmd_in); cmd_nr = _IOC_NR(cmd_in); @@ -4309,6 +4316,12 @@ static int __init init_st(void) printk(KERN_INFO "st: Version %s, fixed bufsize %d, s/g segs %d\n", verstr, st_fixed_buffer_size, st_max_sg_segs); + debugging = (debug_flag > 0) ? debug_flag : NO_DEBUG; + if (debugging) { + printk(KERN_INFO "st: Debugging enabled debug_flag = %d\n", + debugging); + } + err = class_register(&st_sysfs_class); if (err) { pr_err("Unable register sysfs class for SCSI tapes\n"); diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 1aa4befcfbd0..98a62bc15069 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -544,33 +544,15 @@ stex_ss_send_cmd(struct st_hba *hba, struct req_msg *req, u16 tag) } static int -stex_slave_alloc(struct scsi_device *sdev) -{ - /* Cheat: usually extracted from Inquiry data */ - sdev->tagged_supported = 1; - - scsi_activate_tcq(sdev, sdev->host->can_queue); - - return 0; -} - -static int stex_slave_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); - sdev->tagged_supported = 1; return 0; } -static void -stex_slave_destroy(struct scsi_device *sdev) -{ - scsi_deactivate_tcq(sdev, 1); -} - static int stex_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) { @@ -1162,9 +1144,7 @@ static int stex_abort(struct scsi_cmnd *cmd) int result = SUCCESS; unsigned long flags; - printk(KERN_INFO DRV_NAME - "(%s): aborting command\n", pci_name(hba->pdev)); - scsi_print_command(cmd); + scmd_printk(KERN_INFO, cmd, "aborting command\n"); base = hba->mmio_base; spin_lock_irqsave(host->host_lock, flags); @@ -1352,9 +1332,8 @@ static int stex_reset(struct scsi_cmnd *cmd) hba = (struct st_hba *) &cmd->device->host->hostdata[0]; - printk(KERN_INFO DRV_NAME - "(%s): resetting host\n", pci_name(hba->pdev)); - scsi_print_command(cmd); + shost_printk(KERN_INFO, cmd->device->host, + "resetting host\n"); return stex_do_reset(hba) ? FAILED : SUCCESS; } @@ -1391,12 +1370,11 @@ static struct scsi_host_template driver_template = { .proc_name = DRV_NAME, .bios_param = stex_biosparam, .queuecommand = stex_queuecommand, - .slave_alloc = stex_slave_alloc, .slave_configure = stex_slave_config, - .slave_destroy = stex_slave_destroy, .eh_abort_handler = stex_abort, .eh_host_reset_handler = stex_reset, .this_id = -1, + .use_blk_tags = 1, }; static struct pci_device_id stex_pci_tbl[] = { diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 733e5f759518..e3ba251fb6e7 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1097,7 +1097,8 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) if (scmnd->result) { if (scsi_normalize_sense(scmnd->sense_buffer, SCSI_SENSE_BUFFERSIZE, &sense_hdr)) - scsi_print_sense_hdr("storvsc", &sense_hdr); + scsi_print_sense_hdr(scmnd->device, "storvsc", + &sense_hdr); } if (vm_srb->srb_status != SRB_STATUS_SUCCESS) @@ -1428,8 +1429,7 @@ static void storvsc_device_destroy(struct scsi_device *sdevice) static int storvsc_device_configure(struct scsi_device *sdevice) { - scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, - STORVSC_MAX_IO_REQUESTS); + scsi_change_queue_depth(sdevice, STORVSC_MAX_IO_REQUESTS); blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c deleted file mode 100644 index 1a2367a1b1f2..000000000000 --- a/drivers/scsi/sun3_NCR5380.c +++ /dev/null @@ -1,2933 +0,0 @@ -/* sun3_NCR5380.c -- adapted from atari_NCR5380.c for the sun3 by - Sam Creasey. */ -/* - * NCR 5380 generic driver routines. These should make it *trivial* - * to implement 5380 SCSI drivers under Linux with a non-trantor - * architecture. - * - * Note that these routines also work with NR53c400 family chips. - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 666-5836 - * - * DISTRIBUTION RELEASE 6. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 - */ - -/* - * ++roman: To port the 5380 driver to the Atari, I had to do some changes in - * this file, too: - * - * - Some of the debug statements were incorrect (undefined variables and the - * like). I fixed that. - * - * - In information_transfer(), I think a #ifdef was wrong. Looking at the - * possible DMA transfer size should also happen for REAL_DMA. I added this - * in the #if statement. - * - * - When using real DMA, information_transfer() should return in a DATAOUT - * phase after starting the DMA. It has nothing more to do. - * - * - The interrupt service routine should run main after end of DMA, too (not - * only after RESELECTION interrupts). Additionally, it should _not_ test - * for more interrupts after running main, since a DMA process may have - * been started and interrupts are turned on now. The new int could happen - * inside the execution of NCR5380_intr(), leading to recursive - * calls. - * - * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA - * and USLEEP, because these were messing up readability and will never be - * needed for Atari SCSI. - * - * - I've revised the NCR5380_main() calling scheme (relax the 'main_running' - * stuff), and 'main' is executed in a bottom half if awoken by an - * interrupt. - * - * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..." - * constructs. In my eyes, this made the source rather unreadable, so I - * finally replaced that by the *_PRINTK() macros. - * - */ -#include <scsi/scsi_dbg.h> -#include <scsi/scsi_transport_spi.h> - -/* - * Further development / testing that should be done : - * 1. Test linked command handling code after Eric is ready with - * the high level code. - */ - -#if (NDEBUG & NDEBUG_LISTS) -#define LIST(x,y) \ - { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \ - if ((x)==(y)) udelay(5); } -#define REMOVE(w,x,y,z) \ - { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \ - (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \ - if ((x)==(y)) udelay(5); } -#else -#define LIST(x,y) -#define REMOVE(w,x,y,z) -#endif - -#ifndef notyet -#undef LINKED -#endif - -/* - * Design - * Issues : - * - * The other Linux SCSI drivers were written when Linux was Intel PC-only, - * and specifically for each board rather than each chip. This makes their - * adaptation to platforms like the Mac (Some of which use NCR5380's) - * more difficult than it has to be. - * - * Also, many of the SCSI drivers were written before the command queuing - * routines were implemented, meaning their implementations of queued - * commands were hacked on rather than designed in from the start. - * - * When I designed the Linux SCSI drivers I figured that - * while having two different SCSI boards in a system might be useful - * for debugging things, two of the same type wouldn't be used. - * Well, I was wrong and a number of users have mailed me about running - * multiple high-performance SCSI boards in a server. - * - * Finally, when I get questions from users, I have no idea what - * revision of my driver they are running. - * - * This driver attempts to address these problems : - * This is a generic 5380 driver. To use it on a different platform, - * one simply writes appropriate system specific macros (ie, data - * transfer - some PC's will use the I/O bus, 68K's must use - * memory mapped) and drops this file in their 'C' wrapper. - * - * As far as command queueing, two queues are maintained for - * each 5380 in the system - commands that haven't been issued yet, - * and commands that are currently executing. This means that an - * unlimited number of commands may be queued, letting - * more commands propagate from the higher driver levels giving higher - * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported, - * allowing multiple commands to propagate all the way to a SCSI-II device - * while a command is already executing. - * - * To solve the multiple-boards-in-the-same-system problem, - * there is a separate instance structure for each instance - * of a 5380 in the system. So, multiple NCR5380 drivers will - * be able to coexist with appropriate changes to the high level - * SCSI code. - * - * A NCR5380_PUBLIC_REVISION macro is provided, with the release - * number (updated for each public release) printed by the - * NCR5380_print_options command, which should be called from the - * wrapper detect function, so that I know what release of the driver - * users are using. - * - * Issues specific to the NCR5380 : - * - * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead - * piece of hardware that requires you to sit in a loop polling for - * the REQ signal as long as you are connected. Some devices are - * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect - * while doing long seek operations. - * - * The workaround for this is to keep track of devices that have - * disconnected. If the device hasn't disconnected, for commands that - * should disconnect, we do something like - * - * while (!REQ is asserted) { sleep for N usecs; poll for M usecs } - * - * Some tweaking of N and M needs to be done. An algorithm based - * on "time to data" would give the best results as long as short time - * to datas (ie, on the same track) were considered, however these - * broken devices are the exception rather than the rule and I'd rather - * spend my time optimizing for the normal case. - * - * Architecture : - * - * At the heart of the design is a coroutine, NCR5380_main, - * which is started when not running by the interrupt handler, - * timer, and queue command function. It attempts to establish - * I_T_L or I_T_L_Q nexuses by removing the commands from the - * issue queue and calling NCR5380_select() if a nexus - * is not established. - * - * Once a nexus is established, the NCR5380_information_transfer() - * phase goes through the various phases as instructed by the target. - * if the target goes into MSG IN and sends a DISCONNECT message, - * the command structure is placed into the per instance disconnected - * queue, and NCR5380_main tries to find more work. If USLEEP - * was defined, and the target is idle for too long, the system - * will try to sleep. - * - * If a command has disconnected, eventually an interrupt will trigger, - * calling NCR5380_intr() which will in turn call NCR5380_reselect - * to reestablish a nexus. This will run main if necessary. - * - * On command termination, the done function will be called as - * appropriate. - * - * SCSI pointers are maintained in the SCp field of SCSI command - * structures, being initialized after the command is connected - * in NCR5380_select, and set as appropriate in NCR5380_information_transfer. - * Note that in violation of the standard, an implicit SAVE POINTERS operation - * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS. - */ - -/* - * Using this file : - * This file a skeleton Linux SCSI driver for the NCR 5380 series - * of chips. To use it, you write an architecture specific functions - * and macros and include this file in your driver. - * - * These macros control options : - * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically - * for commands that return with a CHECK CONDITION status. - * - * LINKED - if defined, linked commands are supported. - * - * REAL_DMA - if defined, REAL DMA is used during the data transfer phases. - * - * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible - * - * These macros MUST be defined : - * - * NCR5380_read(register) - read from the specified register - * - * NCR5380_write(register, value) - write to the specific register - * - * Either real DMA *or* pseudo DMA may be implemented - * REAL functions : - * NCR5380_REAL_DMA should be defined if real DMA is to be used. - * Note that the DMA setup functions should return the number of bytes - * that they were able to program the controller for. - * - * Also note that generic i386/PC versions of these macros are - * available as NCR5380_i386_dma_write_setup, - * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual. - * - * NCR5380_dma_write_setup(instance, src, count) - initialize - * NCR5380_dma_read_setup(instance, dst, count) - initialize - * NCR5380_dma_residual(instance); - residual count - * - * PSEUDO functions : - * NCR5380_pwrite(instance, src, count) - * NCR5380_pread(instance, dst, count); - * - * If nothing specific to this implementation needs doing (ie, with external - * hardware), you must also define - * - * NCR5380_queue_command - * NCR5380_reset - * NCR5380_abort - * NCR5380_proc_info - * - * to be the global entry points into the specific driver, ie - * #define NCR5380_queue_command t128_queue_command. - * - * If this is not done, the routines will be defined as static functions - * with the NCR5380* names and the user must provide a globally - * accessible wrapper function. - * - * The generic driver is initialized by calling NCR5380_init(instance), - * after setting the appropriate host specific fields and ID. If the - * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance, - * possible) function may be used. Before the specific driver initialization - * code finishes, NCR5380_print_options should be called. - */ - -static struct Scsi_Host *first_instance = NULL; -static struct scsi_host_template *the_template = NULL; - -/* Macros ease life... :-) */ -#define SETUP_HOSTDATA(in) \ - struct NCR5380_hostdata *hostdata = \ - (struct NCR5380_hostdata *)(in)->hostdata -#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata) - -#define NEXT(cmd) ((struct scsi_cmnd *)(cmd)->host_scribble) -#define SET_NEXT(cmd, next) ((cmd)->host_scribble = (void *)(next)) -#define NEXTADDR(cmd) ((struct scsi_cmnd **)&((cmd)->host_scribble)) - -#define HOSTNO instance->host_no -#define H_NO(cmd) (cmd)->device->host->host_no - -#define SGADDR(buffer) (void *)(((unsigned long)sg_virt(((buffer))))) - -#ifdef SUPPORT_TAGS - -/* - * Functions for handling tagged queuing - * ===================================== - * - * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes: - * - * Using consecutive numbers for the tags is no good idea in my eyes. There - * could be wrong re-usings if the counter (8 bit!) wraps and some early - * command has been preempted for a long time. My solution: a bitfield for - * remembering used tags. - * - * There's also the problem that each target has a certain queue size, but we - * cannot know it in advance :-( We just see a QUEUE_FULL status being - * returned. So, in this case, the driver internal queue size assumption is - * reduced to the number of active tags if QUEUE_FULL is returned by the - * target. The command is returned to the mid-level, but with status changed - * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL - * correctly. - * - * We're also not allowed running tagged commands as long as an untagged - * command is active. And REQUEST SENSE commands after a contingent allegiance - * condition _must_ be untagged. To keep track whether an untagged command has - * been issued, the host->busy array is still employed, as it is without - * support for tagged queuing. - * - * One could suspect that there are possible race conditions between - * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the - * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(), - * which already guaranteed to be running at most once. It is also the only - * place where tags/LUNs are allocated. So no other allocation can slip - * between that pair, there could only happen a reselection, which can free a - * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes - * important: the tag bit must be cleared before 'nr_allocated' is decreased. - */ - -/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */ -#undef TAG_NONE -#define TAG_NONE 0xff - -/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */ -#if (MAX_TAGS % 32) != 0 -#error "MAX_TAGS must be a multiple of 32!" -#endif - -typedef struct { - char allocated[MAX_TAGS/8]; - int nr_allocated; - int queue_size; -} TAG_ALLOC; - -static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */ - - -static void __init init_tags( void ) -{ - int target, lun; - TAG_ALLOC *ta; - - if (!setup_use_tagged_queuing) - return; - - for( target = 0; target < 8; ++target ) { - for( lun = 0; lun < 8; ++lun ) { - ta = &TagAlloc[target][lun]; - memset( &ta->allocated, 0, MAX_TAGS/8 ); - ta->nr_allocated = 0; - /* At the beginning, assume the maximum queue size we could - * support (MAX_TAGS). This value will be decreased if the target - * returns QUEUE_FULL status. - */ - ta->queue_size = MAX_TAGS; - } - } -} - - -/* Check if we can issue a command to this LUN: First see if the LUN is marked - * busy by an untagged command. If the command should use tagged queuing, also - * check that there is a free tag and the target's queue won't overflow. This - * function should be called with interrupts disabled to avoid race - * conditions. - */ - -static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged) -{ - u8 lun = cmd->device->lun; - SETUP_HOSTDATA(cmd->device->host); - - if (hostdata->busy[cmd->device->id] & (1 << lun)) - return( 1 ); - if (!should_be_tagged || - !setup_use_tagged_queuing || !cmd->device->tagged_supported) - return( 0 ); - if (TagAlloc[cmd->device->id][lun].nr_allocated >= - TagAlloc[cmd->device->id][lun].queue_size ) { - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n", - H_NO(cmd), cmd->device->id, lun ); - return( 1 ); - } - return( 0 ); -} - - -/* Allocate a tag for a command (there are no checks anymore, check_lun_busy() - * must be called before!), or reserve the LUN in 'busy' if the command is - * untagged. - */ - -static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged) -{ - u8 lun = cmd->device->lun; - SETUP_HOSTDATA(cmd->device->host); - - /* If we or the target don't support tagged queuing, allocate the LUN for - * an untagged command. - */ - if (!should_be_tagged || - !setup_use_tagged_queuing || !cmd->device->tagged_supported) { - cmd->tag = TAG_NONE; - hostdata->busy[cmd->device->id] |= (1 << lun); - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged " - "command\n", H_NO(cmd), cmd->device->id, lun ); - } - else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; - - cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS ); - set_bit( cmd->tag, &ta->allocated ); - ta->nr_allocated++; - dprintk(NDEBUG_TAGS, "scsi%d: using tag %d for target %d lun %d " - "(now %d tags in use)\n", - H_NO(cmd), cmd->tag, cmd->device->id, lun, - ta->nr_allocated ); - } -} - - -/* Mark the tag of command 'cmd' as free, or in case of an untagged command, - * unlock the LUN. - */ - -static void cmd_free_tag(struct scsi_cmnd *cmd) -{ - u8 lun = cmd->device->lun; - SETUP_HOSTDATA(cmd->device->host); - - if (cmd->tag == TAG_NONE) { - hostdata->busy[cmd->device->id] &= ~(1 << lun); - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d untagged cmd finished\n", - H_NO(cmd), cmd->device->id, lun ); - } - else if (cmd->tag >= MAX_TAGS) { - printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n", - H_NO(cmd), cmd->tag ); - } - else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; - clear_bit( cmd->tag, &ta->allocated ); - ta->nr_allocated--; - dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n", - H_NO(cmd), cmd->tag, cmd->device->id, lun ); - } -} - - -static void free_all_tags( void ) -{ - int target, lun; - TAG_ALLOC *ta; - - if (!setup_use_tagged_queuing) - return; - - for( target = 0; target < 8; ++target ) { - for( lun = 0; lun < 8; ++lun ) { - ta = &TagAlloc[target][lun]; - memset( &ta->allocated, 0, MAX_TAGS/8 ); - ta->nr_allocated = 0; - } - } -} - -#endif /* SUPPORT_TAGS */ - - -/* - * Function : void initialize_SCp(struct scsi_cmnd *cmd) - * - * Purpose : initialize the saved data pointers for cmd to point to the - * start of the buffer. - * - * Inputs : cmd - struct scsi_cmnd structure to have pointers reset. - */ - -static __inline__ void initialize_SCp(struct scsi_cmnd *cmd) -{ - /* - * Initialize the Scsi Pointer field so that all of the commands in the - * various queues are valid. - */ - - if (scsi_bufflen(cmd)) { - cmd->SCp.buffer = scsi_sglist(cmd); - cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1; - cmd->SCp.ptr = (char *) SGADDR(cmd->SCp.buffer); - cmd->SCp.this_residual = cmd->SCp.buffer->length; - } else { - cmd->SCp.buffer = NULL; - cmd->SCp.buffers_residual = 0; - cmd->SCp.ptr = NULL; - cmd->SCp.this_residual = 0; - } - -} - -#include <linux/delay.h> - -#if NDEBUG -static struct { - unsigned char mask; - const char * name;} -signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" }, - { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" }, - { SR_SEL, "SEL" }, {0, NULL}}, -basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}}, -icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"}, - {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"}, - {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"}, - {0, NULL}}, -mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"}, - {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR, - "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"}, - {MR_MONITOR_BSY, "MODE MONITOR BSY"}, - {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, - {0, NULL}}; - -/* - * Function : void NCR5380_print(struct Scsi_Host *instance) - * - * Purpose : print the SCSI bus signals for debugging purposes - * - * Input : instance - which NCR5380 - */ - -static void NCR5380_print(struct Scsi_Host *instance) { - unsigned char status, data, basr, mr, icr, i; - unsigned long flags; - - local_irq_save(flags); - data = NCR5380_read(CURRENT_SCSI_DATA_REG); - status = NCR5380_read(STATUS_REG); - mr = NCR5380_read(MODE_REG); - icr = NCR5380_read(INITIATOR_COMMAND_REG); - basr = NCR5380_read(BUS_AND_STATUS_REG); - local_irq_restore(flags); - printk("STATUS_REG: %02x ", status); - for (i = 0; signals[i].mask ; ++i) - if (status & signals[i].mask) - printk(",%s", signals[i].name); - printk("\nBASR: %02x ", basr); - for (i = 0; basrs[i].mask ; ++i) - if (basr & basrs[i].mask) - printk(",%s", basrs[i].name); - printk("\nICR: %02x ", icr); - for (i = 0; icrs[i].mask; ++i) - if (icr & icrs[i].mask) - printk(",%s", icrs[i].name); - printk("\nMODE: %02x ", mr); - for (i = 0; mrs[i].mask; ++i) - if (mr & mrs[i].mask) - printk(",%s", mrs[i].name); - printk("\n"); -} - -static struct { - unsigned char value; - const char *name; -} phases[] = { - {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"}, - {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"}, - {PHASE_UNKNOWN, "UNKNOWN"}}; - -/* - * Function : void NCR5380_print_phase(struct Scsi_Host *instance) - * - * Purpose : print the current SCSI phase for debugging purposes - * - * Input : instance - which NCR5380 - */ - -static void NCR5380_print_phase(struct Scsi_Host *instance) -{ - unsigned char status; - int i; - - status = NCR5380_read(STATUS_REG); - if (!(status & SR_REQ)) - printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO); - else { - for (i = 0; (phases[i].value != PHASE_UNKNOWN) && - (phases[i].value != (status & PHASE_MASK)); ++i); - printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name); - } -} - -#endif - -/* - * ++roman: New scheme of calling NCR5380_main() - * - * If we're not in an interrupt, we can call our main directly, it cannot be - * already running. Else, we queue it on a task queue, if not 'main_running' - * tells us that a lower level is already executing it. This way, - * 'main_running' needs not be protected in a special way. - * - * queue_main() is a utility function for putting our main onto the task - * queue, if main_running is false. It should be called only from a - * interrupt or bottom half. - */ - -#include <linux/gfp.h> -#include <linux/workqueue.h> -#include <linux/interrupt.h> - -static volatile int main_running = 0; -static DECLARE_WORK(NCR5380_tqueue, NCR5380_main); - -static __inline__ void queue_main(void) -{ - if (!main_running) { - /* If in interrupt and NCR5380_main() not already running, - queue it on the 'immediate' task queue, to be processed - immediately after the current interrupt processing has - finished. */ - schedule_work(&NCR5380_tqueue); - } - /* else: nothing to do: the running NCR5380_main() will pick up - any newly queued command. */ -} - - -static inline void NCR5380_all_init (void) -{ - static int done = 0; - if (!done) { - dprintk(NDEBUG_INIT, "scsi : NCR5380_all_init()\n"); - done = 1; - } -} - - -/* - * Function : void NCR58380_print_options (struct Scsi_Host *instance) - * - * Purpose : called by probe code indicating the NCR5380 driver - * options that were selected. - * - * Inputs : instance, pointer to this instance. Unused. - */ - -static void __init NCR5380_print_options (struct Scsi_Host *instance) -{ - printk(" generic options" -#ifdef AUTOSENSE - " AUTOSENSE" -#endif -#ifdef REAL_DMA - " REAL DMA" -#endif -#ifdef PARITY - " PARITY" -#endif -#ifdef SUPPORT_TAGS - " SCSI-2 TAGGED QUEUING" -#endif - ); - printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); -} - -/* - * Function : void NCR5380_print_status (struct Scsi_Host *instance) - * - * Purpose : print commands in the various queues, called from - * NCR5380_abort and NCR5380_debug to aid debugging. - * - * Inputs : instance, pointer to this instance. - */ - -static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd) -{ - int i, s; - unsigned char *command; - printk("scsi%d: destination target %d, lun %llu\n", - H_NO(cmd), cmd->device->id, cmd->device->lun); - printk(KERN_CONT " command = "); - command = cmd->cmnd; - printk(KERN_CONT "%2d (0x%02x)", command[0], command[0]); - for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) - printk(KERN_CONT " %02x", command[i]); - printk("\n"); -} - -static void NCR5380_print_status(struct Scsi_Host *instance) -{ - struct NCR5380_hostdata *hostdata; - Scsi_Cmnd *ptr; - unsigned long flags; - - NCR5380_dprint(NDEBUG_ANY, instance); - NCR5380_dprint_phase(NDEBUG_ANY, instance); - - hostdata = (struct NCR5380_hostdata *)instance->hostdata; - - printk("\nNCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE); - local_irq_save(flags); - printk("NCR5380: coroutine is%s running.\n", - main_running ? "" : "n't"); - if (!hostdata->connected) - printk("scsi%d: no currently connected command\n", HOSTNO); - else - lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected); - printk("scsi%d: issue_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) - lprint_Scsi_Cmnd(ptr); - - printk("scsi%d: disconnected_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; - ptr = NEXT(ptr)) - lprint_Scsi_Cmnd(ptr); - - local_irq_restore(flags); - printk("\n"); -} - -static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m) -{ - int i, s; - unsigned char *command; - seq_printf(m, "scsi%d: destination target %d, lun %llu\n", - H_NO(cmd), cmd->device->id, cmd->device->lun); - seq_printf(m, " command = "); - command = cmd->cmnd; - seq_printf(m, "%2d (0x%02x)", command[0], command[0]); - for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) - seq_printf(m, " %02x", command[i]); - seq_printf(m, "\n"); -} - -static int NCR5380_show_info(struct seq_file *m, struct Scsi_Host *instance) -{ - struct NCR5380_hostdata *hostdata; - Scsi_Cmnd *ptr; - unsigned long flags; - - hostdata = (struct NCR5380_hostdata *)instance->hostdata; - - seq_printf(m, "NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE); - local_irq_save(flags); - seq_printf(m, "NCR5380: coroutine is%s running.\n", - main_running ? "" : "n't"); - if (!hostdata->connected) - seq_printf(m, "scsi%d: no currently connected command\n", HOSTNO); - else - show_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, m); - seq_printf(m, "scsi%d: issue_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) - show_Scsi_Cmnd(ptr, m); - - seq_printf(m, "scsi%d: disconnected_queue\n", HOSTNO); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; - ptr = NEXT(ptr)) - show_Scsi_Cmnd(ptr, m); - - local_irq_restore(flags); - return 0; -} - -/* - * Function : void NCR5380_init (struct Scsi_Host *instance) - * - * Purpose : initializes *instance and corresponding 5380 chip. - * - * Inputs : instance - instantiation of the 5380 driver. - * - * Notes : I assume that the host, hostno, and id bits have been - * set correctly. I don't care about the irq and other fields. - * - */ - -static int __init NCR5380_init(struct Scsi_Host *instance, int flags) -{ - int i; - SETUP_HOSTDATA(instance); - - NCR5380_all_init(); - - hostdata->aborted = 0; - hostdata->id_mask = 1 << instance->this_id; - hostdata->id_higher_mask = 0; - for (i = hostdata->id_mask; i <= 0x80; i <<= 1) - if (i > hostdata->id_mask) - hostdata->id_higher_mask |= i; - for (i = 0; i < 8; ++i) - hostdata->busy[i] = 0; -#ifdef SUPPORT_TAGS - init_tags(); -#endif -#if defined (REAL_DMA) - hostdata->dma_len = 0; -#endif - hostdata->targets_present = 0; - hostdata->connected = NULL; - hostdata->issue_queue = NULL; - hostdata->disconnected_queue = NULL; - hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT; - - if (!the_template) { - the_template = instance->hostt; - first_instance = instance; - } - - -#ifndef AUTOSENSE - if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1)) - printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n" - " without AUTOSENSE option, contingent allegiance conditions may\n" - " be incorrectly cleared.\n", HOSTNO); -#endif /* def AUTOSENSE */ - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(TARGET_COMMAND_REG, 0); - NCR5380_write(SELECT_ENABLE_REG, 0); - - return 0; -} - -static void NCR5380_exit(struct Scsi_Host *instance) -{ - /* Empty, as we didn't schedule any delayed work */ -} - -/* - * Function : int NCR5380_queue_command (struct scsi_cmnd *cmd, - * void (*done)(struct scsi_cmnd *)) - * - * Purpose : enqueues a SCSI command - * - * Inputs : cmd - SCSI command, done - function called on completion, with - * a pointer to the command descriptor. - * - * Returns : 0 - * - * Side effects : - * cmd is added to the per instance issue_queue, with minor - * twiddling done to the host specific fields of cmd. If the - * main coroutine is not running, it is restarted. - * - */ - -/* Only make static if a wrapper function is used */ -static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) -{ - SETUP_HOSTDATA(cmd->device->host); - struct scsi_cmnd *tmp; - unsigned long flags; - -#if (NDEBUG & NDEBUG_NO_WRITE) - switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", - H_NO(cmd)); - cmd->result = (DID_ERROR << 16); - done(cmd); - return 0; - } -#endif /* (NDEBUG & NDEBUG_NO_WRITE) */ - - -#ifdef NCR5380_STATS -# if 0 - if (!hostdata->connected && !hostdata->issue_queue && - !hostdata->disconnected_queue) { - hostdata->timebase = jiffies; - } -# endif -# ifdef NCR5380_STAT_LIMIT - if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT) -# endif - switch (cmd->cmnd[0]) - { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase); - hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd); - hostdata->pendingw++; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase); - hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd); - hostdata->pendingr++; - break; - } -#endif - - /* - * We use the host_scribble field as a pointer to the next command - * in a queue - */ - - SET_NEXT(cmd, NULL); - cmd->scsi_done = done; - - cmd->result = 0; - - - /* - * Insert the cmd into the issue queue. Note that REQUEST SENSE - * commands are added to the head of the queue since any command will - * clear the contingent allegiance condition that exists and the - * sense data is only guaranteed to be valid while the condition exists. - */ - - local_irq_save(flags); - /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA. - * Otherwise a running NCR5380_main may steal the lock. - * Lock before actually inserting due to fairness reasons explained in - * atari_scsi.c. If we insert first, then it's impossible for this driver - * to release the lock. - * Stop timer for this command while waiting for the lock, or timeouts - * may happen (and they really do), and it's no good if the command doesn't - * appear in any of the queues. - * ++roman: Just disabling the NCR interrupt isn't sufficient here, - * because also a timer int can trigger an abort or reset, which would - * alter queues and touch the lock. - */ - if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { - LIST(cmd, hostdata->issue_queue); - SET_NEXT(cmd, hostdata->issue_queue); - hostdata->issue_queue = cmd; - } else { - for (tmp = (struct scsi_cmnd *)hostdata->issue_queue; - NEXT(tmp); tmp = NEXT(tmp)) - ; - LIST(cmd, tmp); - SET_NEXT(tmp, cmd); - } - - local_irq_restore(flags); - - dprintk(NDEBUG_QUEUES, "scsi%d: command added to %s of queue\n", H_NO(cmd), - (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"); - - /* If queue_command() is called from an interrupt (real one or bottom - * half), we let queue_main() do the job of taking care about main. If it - * is already running, this is a no-op, else main will be queued. - * - * If we're not in an interrupt, we can call NCR5380_main() - * unconditionally, because it cannot be already running. - */ - if (in_interrupt() || ((flags >> 8) & 7) >= 6) - queue_main(); - else - NCR5380_main(NULL); - return 0; -} - -static DEF_SCSI_QCMD(NCR5380_queue_command) - -/* - * Function : NCR5380_main (void) - * - * Purpose : NCR5380_main is a coroutine that runs as long as more work can - * be done on the NCR5380 host adapters in a system. Both - * NCR5380_queue_command() and NCR5380_intr() will try to start it - * in case it is not running. - * - * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should - * reenable them. This prevents reentrancy and kernel stack overflow. - */ - -static void NCR5380_main (struct work_struct *bl) -{ - struct scsi_cmnd *tmp, *prev; - struct Scsi_Host *instance = first_instance; - struct NCR5380_hostdata *hostdata = HOSTDATA(instance); - int done; - unsigned long flags; - - /* - * We run (with interrupts disabled) until we're sure that none of - * the host adapters have anything that can be done, at which point - * we set main_running to 0 and exit. - * - * Interrupts are enabled before doing various other internal - * instructions, after we've decided that we need to run through - * the loop again. - * - * this should prevent any race conditions. - * - * ++roman: Just disabling the NCR interrupt isn't sufficient here, - * because also a timer int can trigger an abort or reset, which can - * alter queues and touch the Falcon lock. - */ - - /* Tell int handlers main() is now already executing. Note that - no races are possible here. If an int comes in before - 'main_running' is set here, and queues/executes main via the - task queue, it doesn't do any harm, just this instance of main - won't find any work left to do. */ - if (main_running) - return; - main_running = 1; - - local_save_flags(flags); - do { - local_irq_disable(); /* Freeze request queues */ - done = 1; - - if (!hostdata->connected) { - dprintk(NDEBUG_MAIN, "scsi%d: not connected\n", HOSTNO ); - /* - * Search through the issue_queue for a command destined - * for a target that's not busy. - */ -#if (NDEBUG & NDEBUG_LISTS) - for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL; - tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp)) - ; - if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/ -#endif - for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, - prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) { - - if (prev != tmp) - dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun); - /* When we find one, remove it from the issue queue. */ - /* ++guenther: possible race with Falcon locking */ - if ( -#ifdef SUPPORT_TAGS - !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE) -#else - !(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun)) -#endif - ) { - /* ++guenther: just to be sure, this must be atomic */ - local_irq_disable(); - if (prev) { - REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); - SET_NEXT(prev, NEXT(tmp)); - } else { - REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp)); - hostdata->issue_queue = NEXT(tmp); - } - SET_NEXT(tmp, NULL); - - /* reenable interrupts after finding one */ - local_irq_restore(flags); - - /* - * Attempt to establish an I_T_L nexus here. - * On success, instance->hostdata->connected is set. - * On failure, we must add the command back to the - * issue queue so we can keep trying. - */ - dprintk(NDEBUG_MAIN, "scsi%d: main(): command for target %d " - "lun %llu removed from issue_queue\n", - HOSTNO, tmp->device->id, tmp->device->lun); - /* - * REQUEST SENSE commands are issued without tagged - * queueing, even on SCSI-II devices because the - * contingent allegiance condition exists for the - * entire unit. - */ - /* ++roman: ...and the standard also requires that - * REQUEST SENSE command are untagged. - */ - -#ifdef SUPPORT_TAGS - cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE ); -#endif - if (!NCR5380_select(instance, tmp, - (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : - TAG_NEXT)) { - break; - } else { - local_irq_disable(); - LIST(tmp, hostdata->issue_queue); - SET_NEXT(tmp, hostdata->issue_queue); - hostdata->issue_queue = tmp; -#ifdef SUPPORT_TAGS - cmd_free_tag( tmp ); -#endif - local_irq_restore(flags); - dprintk(NDEBUG_MAIN, "scsi%d: main(): select() failed, " - "returned to issue_queue\n", HOSTNO); - if (hostdata->connected) - break; - } - } /* if target/lun/target queue is not busy */ - } /* for issue_queue */ - } /* if (!hostdata->connected) */ - if (hostdata->connected -#ifdef REAL_DMA - && !hostdata->dma_len -#endif - ) { - local_irq_restore(flags); - dprintk(NDEBUG_MAIN, "scsi%d: main: performing information transfer\n", - HOSTNO); - NCR5380_information_transfer(instance); - dprintk(NDEBUG_MAIN, "scsi%d: main: done set false\n", HOSTNO); - done = 0; - } - } while (!done); - - /* Better allow ints _after_ 'main_running' has been cleared, else - an interrupt could believe we'll pick up the work it left for - us, but we won't see it anymore here... */ - main_running = 0; - local_irq_restore(flags); -} - - -#ifdef REAL_DMA -/* - * Function : void NCR5380_dma_complete (struct Scsi_Host *instance) - * - * Purpose : Called by interrupt handler when DMA finishes or a phase - * mismatch occurs (which would finish the DMA transfer). - * - * Inputs : instance - this instance of the NCR5380. - * - */ - -static void NCR5380_dma_complete( struct Scsi_Host *instance ) -{ - SETUP_HOSTDATA(instance); - int transfered; - unsigned char **data; - volatile int *count; - - if (!hostdata->connected) { - printk(KERN_WARNING "scsi%d: received end of DMA interrupt with " - "no connected cmd\n", HOSTNO); - return; - } - - dprintk(NDEBUG_DMA, "scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n", - HOSTNO, NCR5380_read(BUS_AND_STATUS_REG), - NCR5380_read(STATUS_REG)); - - if((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) { - printk("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", HOSTNO); - printk("please e-mail sammy@sammy.net with a description of how this\n"); - printk("error was produced.\n"); - BUG(); - } - - /* make sure we're not stuck in a data phase */ - if((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | - BASR_ACK)) == - (BASR_PHASE_MATCH | BASR_ACK)) { - printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG)); - printk("scsi%d: bus stuck in data phase -- probably a single byte " - "overrun!\n", HOSTNO); - printk("not prepared for this error!\n"); - printk("please e-mail sammy@sammy.net with a description of how this\n"); - printk("error was produced.\n"); - BUG(); - } - - - - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - transfered = hostdata->dma_len - NCR5380_dma_residual(instance); - hostdata->dma_len = 0; - - data = (unsigned char **) &(hostdata->connected->SCp.ptr); - count = &(hostdata->connected->SCp.this_residual); - *data += transfered; - *count -= transfered; - -} -#endif /* REAL_DMA */ - - -/* - * Function : void NCR5380_intr (int irq) - * - * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses - * from the disconnected queue, and restarting NCR5380_main() - * as required. - * - * Inputs : int irq, irq that caused this interrupt. - * - */ - -static irqreturn_t NCR5380_intr (int irq, void *dev_id) -{ - struct Scsi_Host *instance = first_instance; - int done = 1, handled = 0; - unsigned char basr; - - dprintk(NDEBUG_INTR, "scsi%d: NCR5380 irq triggered\n", HOSTNO); - - /* Look for pending interrupts */ - basr = NCR5380_read(BUS_AND_STATUS_REG); - dprintk(NDEBUG_INTR, "scsi%d: BASR=%02x\n", HOSTNO, basr); - /* dispatch to appropriate routine if found and done=0 */ - if (basr & BASR_IRQ) { - NCR5380_dprint(NDEBUG_INTR, instance); - if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) { - done = 0; -// ENABLE_IRQ(); - dprintk(NDEBUG_INTR, "scsi%d: SEL interrupt\n", HOSTNO); - NCR5380_reselect(instance); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - else if (basr & BASR_PARITY_ERROR) { - dprintk(NDEBUG_INTR, "scsi%d: PARITY interrupt\n", HOSTNO); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) { - dprintk(NDEBUG_INTR, "scsi%d: RESET interrupt\n", HOSTNO); - (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - else { - /* - * The rest of the interrupt conditions can occur only during a - * DMA transfer - */ - -#if defined(REAL_DMA) - /* - * We should only get PHASE MISMATCH and EOP interrupts if we have - * DMA enabled, so do a sanity check based on the current setting - * of the MODE register. - */ - - if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) && - ((basr & BASR_END_DMA_TRANSFER) || - !(basr & BASR_PHASE_MATCH))) { - - dprintk(NDEBUG_INTR, "scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO); - NCR5380_dma_complete( instance ); - done = 0; -// ENABLE_IRQ(); - } else -#endif /* REAL_DMA */ - { -/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */ - if (basr & BASR_PHASE_MATCH) - dprintk(NDEBUG_INTR, "scsi%d: unknown interrupt, " - "BASR 0x%x, MR 0x%x, SR 0x%x\n", - HOSTNO, basr, NCR5380_read(MODE_REG), - NCR5380_read(STATUS_REG)); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_DMA_ENABLE; -#endif - } - } /* if !(SELECTION || PARITY) */ - handled = 1; - } /* BASR & IRQ */ - else { - - printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, " - "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr, - NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_DMA_ENABLE; -#endif - } - - if (!done) { - dprintk(NDEBUG_INTR, "scsi%d: in int routine, calling main\n", HOSTNO); - /* Put a call to NCR5380_main() on the queue... */ - queue_main(); - } - return IRQ_RETVAL(handled); -} - -#ifdef NCR5380_STATS -static void collect_stats(struct NCR5380_hostdata *hostdata, - struct scsi_cmnd *cmd) -{ -# ifdef NCR5380_STAT_LIMIT - if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT) -# endif - switch (cmd->cmnd[0]) - { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase); - /*hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);*/ - hostdata->pendingw--; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase); - /*hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);*/ - hostdata->pendingr--; - break; - } -} -#endif - -/* - * Function : int NCR5380_select(struct Scsi_Host *instance, - * struct scsi_cmnd *cmd, int tag); - * - * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command, - * including ARBITRATION, SELECTION, and initial message out for - * IDENTIFY and queue messages. - * - * Inputs : instance - instantiation of the 5380 driver on which this - * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for - * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for - * the command that is presently connected. - * - * Returns : -1 if selection could not execute for some reason, - * 0 if selection succeeded or failed because the target - * did not respond. - * - * Side effects : - * If bus busy, arbitration failed, etc, NCR5380_select() will exit - * with registers as they should have been on entry - ie - * SELECT_ENABLE will be set appropriately, the NCR5380 - * will cease to drive any SCSI bus signals. - * - * If successful : I_T_L or I_T_L_Q nexus will be established, - * instance->connected will be set to cmd. - * SELECT interrupt will be disabled. - * - * If failed (no target) : cmd->scsi_done() will be called, and the - * cmd->result host byte set to DID_BAD_TARGET. - */ - -static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd, - int tag) -{ - SETUP_HOSTDATA(instance); - unsigned char tmp[3], phase; - unsigned char *data; - int len; - unsigned long timeout; - unsigned long flags; - - hostdata->restart_select = 0; - NCR5380_dprint(NDEBUG_ARBITRATION, instance); - dprintk(NDEBUG_ARBITRATION, "scsi%d: starting arbitration, id = %d\n", HOSTNO, - instance->this_id); - - /* - * Set the phase bits to 0, otherwise the NCR5380 won't drive the - * data bus during SELECTION. - */ - - local_irq_save(flags); - if (hostdata->connected) { - local_irq_restore(flags); - return -1; - } - NCR5380_write(TARGET_COMMAND_REG, 0); - - - /* - * Start arbitration. - */ - - NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); - NCR5380_write(MODE_REG, MR_ARBITRATE); - - local_irq_restore(flags); - - /* Wait for arbitration logic to complete */ -#ifdef NCR_TIMEOUT - { - unsigned long timeout = jiffies + 2*NCR_TIMEOUT; - - while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) - && time_before(jiffies, timeout) && !hostdata->connected) - ; - if (time_after_eq(jiffies, timeout)) - { - printk("scsi : arbitration timeout at %d\n", __LINE__); - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return -1; - } - } -#else /* NCR_TIMEOUT */ - while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) - && !hostdata->connected); -#endif - - dprintk(NDEBUG_ARBITRATION, "scsi%d: arbitration complete\n", HOSTNO); - - if (hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - return -1; - } - /* - * The arbitration delay is 2.2us, but this is a minimum and there is - * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate - * the integral nature of udelay(). - * - */ - - udelay(3); - - /* Check for lost arbitration */ - if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || - (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting MR_ARBITRATE\n", - HOSTNO); - return -1; - } - - /* after/during arbitration, BSY should be asserted. - IBM DPES-31080 Version S31Q works now */ - /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL | - ICR_ASSERT_BSY ) ; - - if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n", - HOSTNO); - return -1; - } - - /* - * Again, bus clear + bus settle time is 1.2us, however, this is - * a minimum so we'll udelay ceil(1.2) - */ - -#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY - /* ++roman: But some targets (see above :-) seem to need a bit more... */ - udelay(15); -#else - udelay(2); -#endif - - if (hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - return -1; - } - - dprintk(NDEBUG_ARBITRATION, "scsi%d: won arbitration\n", HOSTNO); - - /* - * Now that we have won arbitration, start Selection process, asserting - * the host and target ID's on the SCSI bus. - */ - - NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id))); - - /* - * Raise ATN while SEL is true before BSY goes false from arbitration, - * since this is the only way to guarantee that we'll get a MESSAGE OUT - * phase immediately after selection. - */ - - NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | - ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL )); - NCR5380_write(MODE_REG, MR_BASE); - - /* - * Reselect interrupts must be turned off prior to the dropping of BSY, - * otherwise we will trigger an interrupt. - */ - - if (hostdata->connected) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - return -1; - } - - NCR5380_write(SELECT_ENABLE_REG, 0); - - /* - * The initiator shall then wait at least two deskew delays and release - * the BSY signal. - */ - udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */ - - /* Reset BSY */ - NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | - ICR_ASSERT_ATN | ICR_ASSERT_SEL)); - - /* - * Something weird happens when we cease to drive BSY - looks - * like the board/chip is letting us do another read before the - * appropriate propagation delay has expired, and we're confusing - * a BSY signal from ourselves as the target's response to SELECTION. - * - * A small delay (the 'C++' frontend breaks the pipeline with an - * unnecessary jump, making it work on my 386-33/Trantor T128, the - * tighter 'C' code breaks and requires this) solves the problem - - * the 1 us delay is arbitrary, and only used because this delay will - * be the same on other platforms and since it works here, it should - * work there. - * - * wingel suggests that this could be due to failing to wait - * one deskew delay. - */ - - udelay(1); - - dprintk(NDEBUG_SELECTION, "scsi%d: selecting target %d\n", HOSTNO, cmd->device->id); - - /* - * The SCSI specification calls for a 250 ms timeout for the actual - * selection. - */ - - timeout = jiffies + 25; - - /* - * XXX very interesting - we're seeing a bounce where the BSY we - * asserted is being reflected / still asserted (propagation delay?) - * and it's detecting as true. Sigh. - */ - -#if 0 - /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert - * IO while SEL is true. But again, there are some disks out the in the - * world that do that nevertheless. (Somebody claimed that this announces - * reselection capability of the target.) So we better skip that test and - * only wait for BSY... (Famous german words: Der Klügere gibt nach :-) - */ - - while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & - (SR_BSY | SR_IO))); - - if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == - (SR_SEL | SR_IO)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - NCR5380_reselect(instance); - printk (KERN_ERR "scsi%d: reselection after won arbitration?\n", - HOSTNO); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return -1; - } -#else - while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY)); -#endif - - /* - * No less than two deskew delays after the initiator detects the - * BSY signal is true, it shall release the SEL signal and may - * change the DATA BUS. -wingel - */ - - udelay(1); - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - - if (!(NCR5380_read(STATUS_REG) & SR_BSY)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - if (hostdata->targets_present & (1 << cmd->device->id)) { - printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO); - if (hostdata->restart_select) - printk(KERN_NOTICE "\trestart select\n"); - NCR5380_dprint(NDEBUG_ANY, instance); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return -1; - } - cmd->result = DID_BAD_TARGET << 16; -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); -#endif - cmd->scsi_done(cmd); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - dprintk(NDEBUG_SELECTION, "scsi%d: target did not respond within 250ms\n", HOSTNO); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return 0; - } - - hostdata->targets_present |= (1 << cmd->device->id); - - /* - * Since we followed the SCSI spec, and raised ATN while SEL - * was true but before BSY was false during selection, the information - * transfer phase should be a MESSAGE OUT phase so that we can send the - * IDENTIFY message. - * - * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG - * message (2 bytes) with a tag ID that we increment with every command - * until it wraps back to 0. - * - * XXX - it turns out that there are some broken SCSI-II devices, - * which claim to support tagged queuing but fail when more than - * some number of commands are issued at once. - */ - - /* Wait for start of REQ/ACK handshake */ - while (!(NCR5380_read(STATUS_REG) & SR_REQ)); - - dprintk(NDEBUG_SELECTION, "scsi%d: target %d selected, going into MESSAGE OUT phase.\n", - HOSTNO, cmd->device->id); - tmp[0] = IDENTIFY(1, cmd->device->lun); - -#ifdef SUPPORT_TAGS - if (cmd->tag != TAG_NONE) { - tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG; - tmp[2] = cmd->tag; - len = 3; - } else - len = 1; -#else - len = 1; - cmd->tag=0; -#endif /* SUPPORT_TAGS */ - - /* Send message(s) */ - data = tmp; - phase = PHASE_MSGOUT; - NCR5380_transfer_pio(instance, &phase, &len, &data); - dprintk(NDEBUG_SELECTION, "scsi%d: nexus established.\n", HOSTNO); - /* XXX need to handle errors here */ - hostdata->connected = cmd; -#ifndef SUPPORT_TAGS - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); -#endif -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_INTR; -#endif - initialize_SCp(cmd); - - - return 0; -} - -/* - * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance, - * unsigned char *phase, int *count, unsigned char **data) - * - * Purpose : transfers data in given phase using polled I/O - * - * Inputs : instance - instance of driver, *phase - pointer to - * what phase is expected, *count - pointer to number of - * bytes to transfer, **data - pointer to data pointer. - * - * Returns : -1 when different phase is entered without transferring - * maximum number of bytes, 0 if all bytes are transferred or exit - * is in same phase. - * - * Also, *phase, *count, *data are modified in place. - * - * XXX Note : handling for bus free may be useful. - */ - -/* - * Note : this code is not as quick as it could be, however it - * IS 100% reliable, and for the actual data transfer where speed - * counts, we will always do a pseudo DMA or DMA transfer. - */ - -static int NCR5380_transfer_pio( struct Scsi_Host *instance, - unsigned char *phase, int *count, - unsigned char **data) -{ - register unsigned char p = *phase, tmp; - register int c = *count; - register unsigned char *d = *data; - - /* - * The NCR5380 chip will only drive the SCSI bus when the - * phase specified in the appropriate bits of the TARGET COMMAND - * REGISTER match the STATUS REGISTER - */ - - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); - - do { - /* - * Wait for assertion of REQ, after which the phase bits will be - * valid - */ - while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ)); - - dprintk(NDEBUG_HANDSHAKE, "scsi%d: REQ detected\n", HOSTNO); - - /* Check for phase mismatch */ - if ((tmp & PHASE_MASK) != p) { - dprintk(NDEBUG_PIO, "scsi%d: phase mismatch\n", HOSTNO); - NCR5380_dprint_phase(NDEBUG_PIO, instance); - break; - } - - /* Do actual transfer from SCSI bus to / from memory */ - if (!(p & SR_IO)) - NCR5380_write(OUTPUT_DATA_REG, *d); - else - *d = NCR5380_read(CURRENT_SCSI_DATA_REG); - - ++d; - - /* - * The SCSI standard suggests that in MSGOUT phase, the initiator - * should drop ATN on the last byte of the message phase - * after REQ has been asserted for the handshake but before - * the initiator raises ACK. - */ - - if (!(p & SR_IO)) { - if (!((p & SR_MSG) && c > 1)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA); - NCR5380_dprint(NDEBUG_PIO, instance); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ACK); - } else { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ATN); - NCR5380_dprint(NDEBUG_PIO, instance); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK); - } - } else { - NCR5380_dprint(NDEBUG_PIO, instance); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); - } - - while (NCR5380_read(STATUS_REG) & SR_REQ); - - dprintk(NDEBUG_HANDSHAKE, "scsi%d: req false, handshake complete\n", HOSTNO); - -/* - * We have several special cases to consider during REQ/ACK handshaking : - * 1. We were in MSGOUT phase, and we are on the last byte of the - * message. ATN must be dropped as ACK is dropped. - * - * 2. We are in a MSGIN phase, and we are on the last byte of the - * message. We must exit with ACK asserted, so that the calling - * code may raise ATN before dropping ACK to reject the message. - * - * 3. ACK and ATN are clear and the target may proceed as normal. - */ - if (!(p == PHASE_MSGIN && c == 1)) { - if (p == PHASE_MSGOUT && c > 1) - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - else - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - } - } while (--c); - - dprintk(NDEBUG_PIO, "scsi%d: residual %d\n", HOSTNO, c); - - *count = c; - *data = d; - tmp = NCR5380_read(STATUS_REG); - /* The phase read from the bus is valid if either REQ is (already) - * asserted or if ACK hasn't been released yet. The latter is the case if - * we're in MSGIN and all wanted bytes have been received. */ - if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0)) - *phase = tmp & PHASE_MASK; - else - *phase = PHASE_UNKNOWN; - - if (!c || (*phase == p)) - return 0; - else - return -1; -} - -/* - * Function : do_abort (Scsi_Host *host) - * - * Purpose : abort the currently established nexus. Should only be - * called from a routine which can drop into a - * - * Returns : 0 on success, -1 on failure. - */ - -static int do_abort (struct Scsi_Host *host) -{ - unsigned char tmp, *msgptr, phase; - int len; - - /* Request message out phase */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - - /* - * Wait for the target to indicate a valid phase by asserting - * REQ. Once this happens, we'll have either a MSGOUT phase - * and can immediately send the ABORT message, or we'll have some - * other phase and will have to source/sink data. - * - * We really don't care what value was on the bus or what value - * the target sees, so we just handshake. - */ - - while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ)); - - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); - - if ((tmp & PHASE_MASK) != PHASE_MSGOUT) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | - ICR_ASSERT_ACK); - while (NCR5380_read(STATUS_REG) & SR_REQ); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - } - - tmp = ABORT; - msgptr = &tmp; - len = 1; - phase = PHASE_MSGOUT; - NCR5380_transfer_pio (host, &phase, &len, &msgptr); - - /* - * If we got here, and the command completed successfully, - * we're about to go into bus free state. - */ - - return len ? -1 : 0; -} - -#if defined(REAL_DMA) -/* - * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, - * unsigned char *phase, int *count, unsigned char **data) - * - * Purpose : transfers data in given phase using either real - * or pseudo DMA. - * - * Inputs : instance - instance of driver, *phase - pointer to - * what phase is expected, *count - pointer to number of - * bytes to transfer, **data - pointer to data pointer. - * - * Returns : -1 when different phase is entered without transferring - * maximum number of bytes, 0 if all bytes or transferred or exit - * is in same phase. - * - * Also, *phase, *count, *data are modified in place. - * - */ - - -static int NCR5380_transfer_dma( struct Scsi_Host *instance, - unsigned char *phase, int *count, - unsigned char **data) -{ - SETUP_HOSTDATA(instance); - register int c = *count; - register unsigned char p = *phase; - unsigned long flags; - - /* sanity check */ - if(!sun3_dma_setup_done) { - printk("scsi%d: transfer_dma without setup!\n", HOSTNO); - BUG(); - } - hostdata->dma_len = c; - - dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n", - HOSTNO, (p & SR_IO) ? "reading" : "writing", - c, (p & SR_IO) ? "to" : "from", *data); - - /* netbsd turns off ints here, why not be safe and do it too */ - local_irq_save(flags); - - /* send start chain */ - sun3scsi_dma_start(c, *data); - - if (p & SR_IO) { - NCR5380_write(TARGET_COMMAND_REG, 1); - NCR5380_read(RESET_PARITY_INTERRUPT_REG); - NCR5380_write(INITIATOR_COMMAND_REG, 0); - NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR)); - NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0); - } else { - NCR5380_write(TARGET_COMMAND_REG, 0); - NCR5380_read(RESET_PARITY_INTERRUPT_REG); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA); - NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR)); - NCR5380_write(START_DMA_SEND_REG, 0); - } - -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_DMA_ENABLE; -#endif - - local_irq_restore(flags); - - sun3_dma_active = 1; - return 0; -} -#endif /* defined(REAL_DMA) */ - -/* - * Function : NCR5380_information_transfer (struct Scsi_Host *instance) - * - * Purpose : run through the various SCSI phases and do as the target - * directs us to. Operates on the currently connected command, - * instance->connected. - * - * Inputs : instance, instance for which we are doing commands - * - * Side effects : SCSI things happen, the disconnected queue will be - * modified if a command disconnects, *instance->connected will - * change. - * - * XXX Note : we need to watch for bus free or a reset condition here - * to recover from an unexpected bus free condition. - */ - -static void NCR5380_information_transfer (struct Scsi_Host *instance) -{ - SETUP_HOSTDATA(instance); - unsigned long flags; - unsigned char msgout = NOP; - int sink = 0; - int len; -#if defined(REAL_DMA) - int transfersize; -#endif - unsigned char *data; - unsigned char phase, tmp, extended_msg[10], old_phase=0xff; - struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected; - -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_INTR; -#endif - - while (1) { - tmp = NCR5380_read(STATUS_REG); - /* We only have a valid SCSI phase when REQ is asserted */ - if (tmp & SR_REQ) { - phase = (tmp & PHASE_MASK); - if (phase != old_phase) { - old_phase = phase; - NCR5380_dprint_phase(NDEBUG_INFORMATION, instance); - } - - if(phase == PHASE_CMDOUT) { - void *d; - unsigned long count; - - if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { - count = cmd->SCp.buffer->length; - d = SGADDR(cmd->SCp.buffer); - } else { - count = cmd->SCp.this_residual; - d = cmd->SCp.ptr; - } -#ifdef REAL_DMA - /* this command setup for dma yet? */ - if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done - != cmd)) - { - if (cmd->request->cmd_type == REQ_TYPE_FS) { - sun3scsi_dma_setup(d, count, - rq_data_dir(cmd->request)); - sun3_dma_setup_done = cmd; - } - } -#endif -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_INTR; -#endif - } - - - if (sink && (phase != PHASE_MSGOUT)) { - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | - ICR_ASSERT_ACK); - while (NCR5380_read(STATUS_REG) & SR_REQ); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); - sink = 0; - continue; - } - - switch (phase) { - case PHASE_DATAOUT: -#if (NDEBUG & NDEBUG_NO_DATAOUT) - printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT " - "aborted\n", HOSTNO); - sink = 1; - do_abort(instance); - cmd->result = DID_ERROR << 16; - cmd->scsi_done(cmd); - return; -#endif - case PHASE_DATAIN: - /* - * If there is no room left in the current buffer in the - * scatter-gather list, move onto the next one. - */ - if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { - ++cmd->SCp.buffer; - --cmd->SCp.buffers_residual; - cmd->SCp.this_residual = cmd->SCp.buffer->length; - cmd->SCp.ptr = SGADDR(cmd->SCp.buffer); - dprintk(NDEBUG_INFORMATION, "scsi%d: %d bytes and %d buffers left\n", - HOSTNO, cmd->SCp.this_residual, - cmd->SCp.buffers_residual); - } - - /* - * The preferred transfer method is going to be - * PSEUDO-DMA for systems that are strictly PIO, - * since we can let the hardware do the handshaking. - * - * For this to work, we need to know the transfersize - * ahead of time, since the pseudo-DMA code will sit - * in an unconditional loop. - */ - -/* ++roman: I suggest, this should be - * #if def(REAL_DMA) - * instead of leaving REAL_DMA out. - */ - -#if defined(REAL_DMA) -// if (!cmd->device->borken && - if((transfersize = - NCR5380_dma_xfer_len(instance,cmd,phase)) > SUN3_DMA_MINSIZE) { - len = transfersize; - cmd->SCp.phase = phase; - - if (NCR5380_transfer_dma(instance, &phase, - &len, (unsigned char **) &cmd->SCp.ptr)) { - /* - * If the watchdog timer fires, all future - * accesses to this device will use the - * polled-IO. */ - printk(KERN_NOTICE "scsi%d: switching target %d " - "lun %llu to slow handshake\n", HOSTNO, - cmd->device->id, cmd->device->lun); - cmd->device->borken = 1; - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); - sink = 1; - do_abort(instance); - cmd->result = DID_ERROR << 16; - cmd->scsi_done(cmd); - /* XXX - need to source or sink data here, as appropriate */ - } else { -#ifdef REAL_DMA - /* ++roman: When using real DMA, - * information_transfer() should return after - * starting DMA since it has nothing more to - * do. - */ - return; -#else - cmd->SCp.this_residual -= transfersize - len; -#endif - } - } else -#endif /* defined(REAL_DMA) */ - NCR5380_transfer_pio(instance, &phase, - (int *) &cmd->SCp.this_residual, (unsigned char **) - &cmd->SCp.ptr); -#ifdef REAL_DMA - /* if we had intended to dma that command clear it */ - if(sun3_dma_setup_done == cmd) - sun3_dma_setup_done = NULL; -#endif - - break; - case PHASE_MSGIN: - len = 1; - data = &tmp; - NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */ - NCR5380_transfer_pio(instance, &phase, &len, &data); - cmd->SCp.Message = tmp; - - switch (tmp) { - /* - * Linking lets us reduce the time required to get the - * next command out to the device, hopefully this will - * mean we don't waste another revolution due to the delays - * required by ARBITRATION and another SELECTION. - * - * In the current implementation proposal, low level drivers - * merely have to start the next command, pointed to by - * next_link, done() is called as with unlinked commands. - */ -#ifdef LINKED - case LINKED_CMD_COMPLETE: - case LINKED_FLG_CMD_COMPLETE: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked command " - "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun); - - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - /* - * Sanity check : A linked command should only terminate - * with one of these messages if there are more linked - * commands available. - */ - - if (!cmd->next_link) { - printk(KERN_NOTICE "scsi%d: target %d lun %llu " - "linked command complete, no next_link\n", - HOSTNO, cmd->device->id, cmd->device->lun); - sink = 1; - do_abort (instance); - return; - } - - initialize_SCp(cmd->next_link); - /* The next command is still part of this process; copy it - * and don't free it! */ - cmd->next_link->tag = cmd->tag; - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked request " - "done, calling scsi_done().\n", - HOSTNO, cmd->device->id, cmd->device->lun); -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); - cmd = hostdata->connected; - break; -#endif /* def LINKED */ - case ABORT: - case COMMAND_COMPLETE: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - hostdata->connected = NULL; - dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu " - "completed\n", HOSTNO, cmd->device->id, cmd->device->lun); -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); - if (status_byte(cmd->SCp.Status) == QUEUE_FULL) { - /* Turn a QUEUE FULL status into BUSY, I think the - * mid level cannot handle QUEUE FULL :-( (The - * command is retried after BUSY). Also update our - * queue size to the number of currently issued - * commands now. - */ - /* ++Andreas: the mid level code knows about - QUEUE_FULL now. */ - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu returned " - "QUEUE_FULL after %d commands\n", - HOSTNO, cmd->device->id, cmd->device->lun, - ta->nr_allocated); - if (ta->queue_size > ta->nr_allocated) - ta->nr_allocated = ta->queue_size; - } -#else - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); -#endif - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - - /* - * I'm not sure what the correct thing to do here is : - * - * If the command that just executed is NOT a request - * sense, the obvious thing to do is to set the result - * code to the values of the stored parameters. - * - * If it was a REQUEST SENSE command, we need some way to - * differentiate between the failure code of the original - * and the failure code of the REQUEST sense - the obvious - * case is success, where we fall through and leave the - * result code unchanged. - * - * The non-obvious place is where the REQUEST SENSE failed - */ - - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (status_byte(cmd->SCp.Status) != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - -#ifdef AUTOSENSE - if ((cmd->cmnd[0] == REQUEST_SENSE) && - hostdata->ses.cmd_len) { - scsi_eh_restore_cmnd(cmd, &hostdata->ses); - hostdata->ses.cmd_len = 0 ; - } - - if ((cmd->cmnd[0] != REQUEST_SENSE) && - (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) { - scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0); - dprintk(NDEBUG_AUTOSENSE, "scsi%d: performing request sense\n", - HOSTNO); - /* this is initialized from initialize_SCp - cmd->SCp.buffer = NULL; - cmd->SCp.buffers_residual = 0; - */ - - local_irq_save(flags); - LIST(cmd,hostdata->issue_queue); - SET_NEXT(cmd, hostdata->issue_queue); - hostdata->issue_queue = (struct scsi_cmnd *) cmd; - local_irq_restore(flags); - dprintk(NDEBUG_QUEUES, "scsi%d: REQUEST SENSE added to head of " - "issue queue\n", H_NO(cmd)); - } else -#endif /* def AUTOSENSE */ - { -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); - } - - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - /* - * Restore phase bits to 0 so an interrupted selection, - * arbitration can resume. - */ - NCR5380_write(TARGET_COMMAND_REG, 0); - - while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) - barrier(); - - return; - case MESSAGE_REJECT: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - switch (hostdata->last_message) { - case HEAD_OF_QUEUE_TAG: - case ORDERED_QUEUE_TAG: - case SIMPLE_QUEUE_TAG: - /* The target obviously doesn't support tagged - * queuing, even though it announced this ability in - * its INQUIRY data ?!? (maybe only this LUN?) Ok, - * clear 'tagged_supported' and lock the LUN, since - * the command is treated as untagged further on. - */ - cmd->device->tagged_supported = 0; - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); - cmd->tag = TAG_NONE; - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu rejected " - "QUEUE_TAG message; tagged queuing " - "disabled\n", - HOSTNO, cmd->device->id, cmd->device->lun); - break; - } - break; - case DISCONNECT: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - local_irq_save(flags); - cmd->device->disconnect = 1; - LIST(cmd,hostdata->disconnected_queue); - SET_NEXT(cmd, hostdata->disconnected_queue); - hostdata->connected = NULL; - hostdata->disconnected_queue = cmd; - local_irq_restore(flags); - dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d lun %llu was " - "moved from connected to the " - "disconnected_queue\n", HOSTNO, - cmd->device->id, cmd->device->lun); - /* - * Restore phase bits to 0 so an interrupted selection, - * arbitration can resume. - */ - NCR5380_write(TARGET_COMMAND_REG, 0); - - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - /* Wait for bus free to avoid nasty timeouts */ - while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) - barrier(); -#ifdef SUN3_SCSI_VME - dregs->csr |= CSR_DMA_ENABLE; -#endif - return; - /* - * The SCSI data pointer is *IMPLICITLY* saved on a disconnect - * operation, in violation of the SCSI spec so we can safely - * ignore SAVE/RESTORE pointers calls. - * - * Unfortunately, some disks violate the SCSI spec and - * don't issue the required SAVE_POINTERS message before - * disconnecting, and we have to break spec to remain - * compatible. - */ - case SAVE_POINTERS: - case RESTORE_POINTERS: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - break; - case EXTENDED_MESSAGE: -/* - * Extended messages are sent in the following format : - * Byte - * 0 EXTENDED_MESSAGE == 1 - * 1 length (includes one byte for code, doesn't - * include first two bytes) - * 2 code - * 3..length+1 arguments - * - * Start the extended message buffer with the EXTENDED_MESSAGE - * byte, since spi_print_msg() wants the whole thing. - */ - extended_msg[0] = EXTENDED_MESSAGE; - /* Accept first byte by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - dprintk(NDEBUG_EXTENDED, "scsi%d: receiving extended message\n", HOSTNO); - - len = 2; - data = extended_msg + 1; - phase = PHASE_MSGIN; - NCR5380_transfer_pio(instance, &phase, &len, &data); - dprintk(NDEBUG_EXTENDED, "scsi%d: length=%d, code=0x%02x\n", HOSTNO, - (int)extended_msg[1], (int)extended_msg[2]); - - if (!len && extended_msg[1] <= - (sizeof (extended_msg) - 1)) { - /* Accept third byte by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - len = extended_msg[1] - 1; - data = extended_msg + 3; - phase = PHASE_MSGIN; - - NCR5380_transfer_pio(instance, &phase, &len, &data); - dprintk(NDEBUG_EXTENDED, "scsi%d: message received, residual %d\n", - HOSTNO, len); - - switch (extended_msg[2]) { - case EXTENDED_SDTR: - case EXTENDED_WDTR: - case EXTENDED_MODIFY_DATA_POINTER: - case EXTENDED_EXTENDED_IDENTIFY: - tmp = 0; - } - } else if (len) { - printk(KERN_NOTICE "scsi%d: error receiving " - "extended message\n", HOSTNO); - tmp = 0; - } else { - printk(KERN_NOTICE "scsi%d: extended message " - "code %02x length %d is too long\n", - HOSTNO, extended_msg[2], extended_msg[1]); - tmp = 0; - } - /* Fall through to reject message */ - - /* - * If we get something weird that we aren't expecting, - * reject it. - */ - default: - if (!tmp) { - printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO); - spi_print_msg(extended_msg); - printk("\n"); - } else if (tmp != EXTENDED_MESSAGE) - printk(KERN_DEBUG "scsi%d: rejecting unknown " - "message %02x from target %d, lun %llu\n", - HOSTNO, tmp, cmd->device->id, cmd->device->lun); - else - printk(KERN_DEBUG "scsi%d: rejecting unknown " - "extended message " - "code %02x, length %d from target %d, lun %llu\n", - HOSTNO, extended_msg[1], extended_msg[0], - cmd->device->id, cmd->device->lun); - - - msgout = MESSAGE_REJECT; - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); - break; - } /* switch (tmp) */ - break; - case PHASE_MSGOUT: - len = 1; - data = &msgout; - hostdata->last_message = msgout; - NCR5380_transfer_pio(instance, &phase, &len, &data); - if (msgout == ABORT) { -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); -#else - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); -#endif - hostdata->connected = NULL; - cmd->result = DID_ERROR << 16; -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return; - } - msgout = NOP; - break; - case PHASE_CMDOUT: - len = cmd->cmd_len; - data = cmd->cmnd; - /* - * XXX for performance reasons, on machines with a - * PSEUDO-DMA architecture we should probably - * use the dma transfer function. - */ - NCR5380_transfer_pio(instance, &phase, &len, - &data); - break; - case PHASE_STATIN: - len = 1; - data = &tmp; - NCR5380_transfer_pio(instance, &phase, &len, &data); - cmd->SCp.Status = tmp; - break; - default: - printk("scsi%d: unknown phase\n", HOSTNO); - NCR5380_dprint(NDEBUG_ANY, instance); - } /* switch(phase) */ - } /* if (tmp * SR_REQ) */ - } /* while (1) */ -} - -/* - * Function : void NCR5380_reselect (struct Scsi_Host *instance) - * - * Purpose : does reselection, initializing the instance->connected - * field to point to the struct scsi_cmnd for which the I_T_L or I_T_L_Q - * nexus has been reestablished, - * - * Inputs : instance - this instance of the NCR5380. - * - */ - -/* it might eventually prove necessary to do a dma setup on - reselection, but it doesn't seem to be needed now -- sam */ - -static void NCR5380_reselect (struct Scsi_Host *instance) -{ - SETUP_HOSTDATA(instance); - unsigned char target_mask; - unsigned char lun; -#ifdef SUPPORT_TAGS - unsigned char tag; -#endif - unsigned char msg[3]; - struct scsi_cmnd *tmp = NULL, *prev; -/* unsigned long flags; */ - - /* - * Disable arbitration, etc. since the host adapter obviously - * lost, and tell an interrupted NCR5380_select() to restart. - */ - - NCR5380_write(MODE_REG, MR_BASE); - hostdata->restart_select = 1; - - target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask); - - dprintk(NDEBUG_RESELECTION, "scsi%d: reselect\n", HOSTNO); - - /* - * At this point, we have detected that our SCSI ID is on the bus, - * SEL is true and BSY was false for at least one bus settle delay - * (400 ns). - * - * We must assert BSY ourselves, until the target drops the SEL - * signal. - */ - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY); - - while (NCR5380_read(STATUS_REG) & SR_SEL); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - /* - * Wait for target to go into MSGIN. - */ - - while (!(NCR5380_read(STATUS_REG) & SR_REQ)); - -#if 1 - // acknowledge toggle to MSGIN - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN)); - - // peek at the byte without really hitting the bus - msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG); -#endif - - if (!(msg[0] & 0x80)) { - printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO); - spi_print_msg(msg); - do_abort(instance); - return; - } - lun = (msg[0] & 0x07); - - /* - * Find the command corresponding to the I_T_L or I_T_L_Q nexus we - * just reestablished, and remove it from the disconnected queue. - */ - - for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL; - tmp; prev = tmp, tmp = NEXT(tmp) ) { - if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun) -#ifdef SUPPORT_TAGS - && (tag == tmp->tag) -#endif - ) { - if (prev) { - REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); - SET_NEXT(prev, NEXT(tmp)); - } else { - REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp)); - hostdata->disconnected_queue = NEXT(tmp); - } - SET_NEXT(tmp, NULL); - break; - } - } - - if (!tmp) { - printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d " -#ifdef SUPPORT_TAGS - "tag %d " -#endif - "not in disconnected_queue.\n", - HOSTNO, target_mask, lun -#ifdef SUPPORT_TAGS - , tag -#endif - ); - /* - * Since we have an established nexus that we can't do anything - * with, we must abort it. - */ - do_abort(instance); - return; - } -#if 1 - /* engage dma setup for the command we just saw */ - { - void *d; - unsigned long count; - - if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) { - count = tmp->SCp.buffer->length; - d = SGADDR(tmp->SCp.buffer); - } else { - count = tmp->SCp.this_residual; - d = tmp->SCp.ptr; - } -#ifdef REAL_DMA - /* setup this command for dma if not already */ - if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done != tmp)) - { - sun3scsi_dma_setup(d, count, rq_data_dir(tmp->request)); - sun3_dma_setup_done = tmp; - } -#endif - } -#endif - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - -#ifdef SUPPORT_TAGS - /* If the phase is still MSGIN, the target wants to send some more - * messages. In case it supports tagged queuing, this is probably a - * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus. - */ - tag = TAG_NONE; - if (phase == PHASE_MSGIN && setup_use_tagged_queuing) { - /* Accept previous IDENTIFY message by clearing ACK */ - NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); - len = 2; - data = msg+1; - if (!NCR5380_transfer_pio(instance, &phase, &len, &data) && - msg[1] == SIMPLE_QUEUE_TAG) - tag = msg[2]; - dprintk(NDEBUG_TAGS, "scsi%d: target mask %02x, lun %d sent tag %d at " - "reselection\n", HOSTNO, target_mask, lun, tag); - } -#endif - - hostdata->connected = tmp; - dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n", - HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag); -} - - -/* - * Function : int NCR5380_abort(struct scsi_cmnd *cmd) - * - * Purpose : abort a command - * - * Inputs : cmd - the struct scsi_cmnd to abort, code - code to set the - * host byte of the result field to, if zero DID_ABORTED is - * used. - * - * Returns : 0 - success, -1 on failure. - * - * XXX - there is no way to abort the command that is currently - * connected, you have to wait for it to complete. If this is - * a problem, we could implement longjmp() / setjmp(), setjmp() - * called where the loop started in NCR5380_main(). - */ - -static int NCR5380_abort(struct scsi_cmnd *cmd) -{ - struct Scsi_Host *instance = cmd->device->host; - SETUP_HOSTDATA(instance); - struct scsi_cmnd *tmp, **prev; - unsigned long flags; - - printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO); - scsi_print_command(cmd); - - NCR5380_print_status (instance); - - local_irq_save(flags); - - dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, - NCR5380_read(BUS_AND_STATUS_REG), - NCR5380_read(STATUS_REG)); - -#if 1 -/* - * Case 1 : If the command is the currently executing command, - * we'll set the aborted flag and return control so that - * information transfer routine can exit cleanly. - */ - - if (hostdata->connected == cmd) { - - dprintk(NDEBUG_ABORT, "scsi%d: aborting connected command\n", HOSTNO); -/* - * We should perform BSY checking, and make sure we haven't slipped - * into BUS FREE. - */ - -/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */ -/* - * Since we can't change phases until we've completed the current - * handshake, we have to source or sink a byte of data if the current - * phase is not MSGOUT. - */ - -/* - * Return control to the executing NCR drive so we can clear the - * aborted flag and get back into our main loop. - */ - - if (do_abort(instance) == 0) { - hostdata->aborted = 1; - hostdata->connected = NULL; - cmd->result = DID_ABORT << 16; -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); -#else - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); -#endif - local_irq_restore(flags); - cmd->scsi_done(cmd); - return SUCCESS; - } else { -/* local_irq_restore(flags); */ - printk("scsi%d: abort of connected command failed!\n", HOSTNO); - return FAILED; - } - } -#endif - -/* - * Case 2 : If the command hasn't been issued yet, we simply remove it - * from the issue queue. - */ - for (prev = (struct scsi_cmnd **) &(hostdata->issue_queue), - tmp = (struct scsi_cmnd *) hostdata->issue_queue; - tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) - if (cmd == tmp) { - REMOVE(5, *prev, tmp, NEXT(tmp)); - (*prev) = NEXT(tmp); - SET_NEXT(tmp, NULL); - tmp->result = DID_ABORT << 16; - local_irq_restore(flags); - dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n", - HOSTNO); - /* Tagged queuing note: no tag to free here, hasn't been assigned - * yet... */ - tmp->scsi_done(tmp); - return SUCCESS; - } - -/* - * Case 3 : If any commands are connected, we're going to fail the abort - * and let the high level SCSI driver retry at a later time or - * issue a reset. - * - * Timeouts, and therefore aborted commands, will be highly unlikely - * and handling them cleanly in this situation would make the common - * case of noresets less efficient, and would pollute our code. So, - * we fail. - */ - - if (hostdata->connected) { - local_irq_restore(flags); - dprintk(NDEBUG_ABORT, "scsi%d: abort failed, command connected.\n", HOSTNO); - return FAILED; - } - -/* - * Case 4: If the command is currently disconnected from the bus, and - * there are no connected commands, we reconnect the I_T_L or - * I_T_L_Q nexus associated with it, go into message out, and send - * an abort message. - * - * This case is especially ugly. In order to reestablish the nexus, we - * need to call NCR5380_select(). The easiest way to implement this - * function was to abort if the bus was busy, and let the interrupt - * handler triggered on the SEL for reselect take care of lost arbitrations - * where necessary, meaning interrupts need to be enabled. - * - * When interrupts are enabled, the queues may change - so we - * can't remove it from the disconnected queue before selecting it - * because that could cause a failure in hashing the nexus if that - * device reselected. - * - * Since the queues may change, we can't use the pointers from when we - * first locate it. - * - * So, we must first locate the command, and if NCR5380_select() - * succeeds, then issue the abort, relocate the command and remove - * it from the disconnected queue. - */ - - for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; - tmp = NEXT(tmp)) - if (cmd == tmp) { - local_irq_restore(flags); - dprintk(NDEBUG_ABORT, "scsi%d: aborting disconnected command.\n", HOSTNO); - - if (NCR5380_select (instance, cmd, (int) cmd->tag)) - return FAILED; - - dprintk(NDEBUG_ABORT, "scsi%d: nexus reestablished.\n", HOSTNO); - - do_abort (instance); - - local_irq_save(flags); - for (prev = (struct scsi_cmnd **) &(hostdata->disconnected_queue), - tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; - tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) ) - if (cmd == tmp) { - REMOVE(5, *prev, tmp, NEXT(tmp)); - *prev = NEXT(tmp); - SET_NEXT(tmp, NULL); - tmp->result = DID_ABORT << 16; - /* We must unlock the tag/LUN immediately here, since the - * target goes to BUS FREE and doesn't send us another - * message (COMMAND_COMPLETE or the like) - */ -#ifdef SUPPORT_TAGS - cmd_free_tag( tmp ); -#else - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); -#endif - local_irq_restore(flags); - tmp->scsi_done(tmp); - return SUCCESS; - } - } - -/* - * Case 5 : If we reached this point, the command was not found in any of - * the queues. - * - * We probably reached this point because of an unlikely race condition - * between the command completing successfully and the abortion code, - * so we won't panic, but we will notify the user in case something really - * broke. - */ - - local_irq_restore(flags); - printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO); - - return FAILED; -} - - -/* - * Function : int NCR5380_bus_reset(struct scsi_cmnd *cmd) - * - * Purpose : reset the SCSI bus. - * - * Returns : SUCCESS or FAILURE - * - */ - -static int NCR5380_bus_reset(struct scsi_cmnd *cmd) -{ - SETUP_HOSTDATA(cmd->device->host); - int i; - unsigned long flags; -#if defined(RESET_RUN_DONE) - struct scsi_cmnd *connected, *disconnected_queue; -#endif - - - NCR5380_print_status (cmd->device->host); - - /* get in phase */ - NCR5380_write( TARGET_COMMAND_REG, - PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) )); - /* assert RST */ - NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST ); - udelay (40); - /* reset NCR registers */ - NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); - NCR5380_write( MODE_REG, MR_BASE ); - NCR5380_write( TARGET_COMMAND_REG, 0 ); - NCR5380_write( SELECT_ENABLE_REG, 0 ); - /* ++roman: reset interrupt condition! otherwise no interrupts don't get - * through anymore ... */ - (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG ); - - /* MSch 20140115 - looking at the generic NCR5380 driver, all of this - * should go. - * Catch-22: if we don't clear all queues, the SCSI driver lock will - * not be released by atari_scsi_reset()! - */ - -#if defined(RESET_RUN_DONE) - /* XXX Should now be done by midlevel code, but it's broken XXX */ - /* XXX see below XXX */ - - /* MSch: old-style reset: actually abort all command processing here */ - - /* After the reset, there are no more connected or disconnected commands - * and no busy units; to avoid problems with re-inserting the commands - * into the issue_queue (via scsi_done()), the aborted commands are - * remembered in local variables first. - */ - local_irq_save(flags); - connected = (struct scsi_cmnd *)hostdata->connected; - hostdata->connected = NULL; - disconnected_queue = (struct scsi_cmnd *)hostdata->disconnected_queue; - hostdata->disconnected_queue = NULL; -#ifdef SUPPORT_TAGS - free_all_tags(); -#endif - for( i = 0; i < 8; ++i ) - hostdata->busy[i] = 0; -#ifdef REAL_DMA - hostdata->dma_len = 0; -#endif - local_irq_restore(flags); - - /* In order to tell the mid-level code which commands were aborted, - * set the command status to DID_RESET and call scsi_done() !!! - * This ultimately aborts processing of these commands in the mid-level. - */ - - if ((cmd = connected)) { - dprintk(NDEBUG_ABORT, "scsi%d: reset aborted a connected command\n", H_NO(cmd)); - cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); - cmd->scsi_done( cmd ); - } - - for (i = 0; (cmd = disconnected_queue); ++i) { - disconnected_queue = NEXT(cmd); - SET_NEXT(cmd, NULL); - cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); - cmd->scsi_done( cmd ); - } - if (i > 0) - dprintk(NDEBUG_ABORT, "scsi: reset aborted %d disconnected command(s)\n", i); - - - /* since all commands have been explicitly terminated, we need to tell - * the midlevel code that the reset was SUCCESSFUL, and there is no - * need to 'wake up' the commands by a request_sense - */ - return SUCCESS; -#else /* 1 */ - - /* MSch: new-style reset handling: let the mid-level do what it can */ - - /* ++guenther: MID-LEVEL IS STILL BROKEN. - * Mid-level is supposed to requeue all commands that were active on the - * various low-level queues. In fact it does this, but that's not enough - * because all these commands are subject to timeout. And if a timeout - * happens for any removed command, *_abort() is called but all queues - * are now empty. Abort then gives up the falcon lock, which is fatal, - * since the mid-level will queue more commands and must have the lock - * (it's all happening inside timer interrupt handler!!). - * Even worse, abort will return NOT_RUNNING for all those commands not - * on any queue, so they won't be retried ... - * - * Conclusion: either scsi.c disables timeout for all resetted commands - * immediately, or we lose! As of linux-2.0.20 it doesn't. - */ - - /* After the reset, there are no more connected or disconnected commands - * and no busy units; so clear the low-level status here to avoid - * conflicts when the mid-level code tries to wake up the affected - * commands! - */ - - if (hostdata->issue_queue) - dprintk(NDEBUG_ABORT, "scsi%d: reset aborted issued command(s)\n", H_NO(cmd)); - if (hostdata->connected) - dprintk(NDEBUG_ABORT, "scsi%d: reset aborted a connected command\n", H_NO(cmd)); - if (hostdata->disconnected_queue) - dprintk(NDEBUG_ABORT, "scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd)); - - local_irq_save(flags); - hostdata->issue_queue = NULL; - hostdata->connected = NULL; - hostdata->disconnected_queue = NULL; -#ifdef SUPPORT_TAGS - free_all_tags(); -#endif - for( i = 0; i < 8; ++i ) - hostdata->busy[i] = 0; -#ifdef REAL_DMA - hostdata->dma_len = 0; -#endif - local_irq_restore(flags); - - /* we did no complete reset of all commands, so a wakeup is required */ - return SUCCESS; -#endif /* 1 */ -} - -/* Local Variables: */ -/* tab-width: 8 */ -/* End: */ diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c index 9707b7494a89..2a906d1d34ba 100644 --- a/drivers/scsi/sun3_scsi.c +++ b/drivers/scsi/sun3_scsi.c @@ -20,89 +20,58 @@ * Generic Generic NCR5380 driver * * Copyright 1995, Russell King - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ - -/* - * This is from mac_scsi.h, but hey, maybe this is useful for Sun3 too! :) - * - * Options : - * - * PARITY - enable parity checking. Not supported. - * - * SCSI2 - enable support for SCSI-II tagged queueing. Untested. - * - * USLEEP - enable support for devices that don't disconnect. Untested. - */ - -#define AUTOSENSE - #include <linux/types.h> -#include <linux/stddef.h> -#include <linux/ctype.h> #include <linux/delay.h> - #include <linux/module.h> -#include <linux/signal.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/blkdev.h> +#include <linux/platform_device.h> #include <asm/io.h> - -#include <asm/sun3ints.h> #include <asm/dvma.h> -#include <asm/idprom.h> -#include <asm/machines.h> -/* dma on! */ -#define REAL_DMA - -#include "scsi.h" #include <scsi/scsi_host.h> #include "sun3_scsi.h" -#include "NCR5380.h" -extern int sun3_map_test(unsigned long, char *); +/* Definitions for the core NCR5380 driver. */ -#define USE_WRAPPER -/*#define RESET_BOOT */ -#define DRIVER_SETUP +#define REAL_DMA +/* #define SUPPORT_TAGS */ +/* minimum number of bytes to do dma on */ +#define DMA_MIN_SIZE 129 -/* - * BUG can be used to trigger a strange code-size related hang on 2.1 kernels - */ -#ifdef BUG -#undef RESET_BOOT -#undef DRIVER_SETUP -#endif +/* #define MAX_TAGS 32 */ -/* #define SUPPORT_TAGS */ +#define NCR5380_implementation_fields /* none */ -#ifdef SUN3_SCSI_VME -#define ENABLE_IRQ() -#else -#define ENABLE_IRQ() enable_irq( IRQ_SUN3_SCSI ); -#endif +#define NCR5380_read(reg) sun3scsi_read(reg) +#define NCR5380_write(reg, value) sun3scsi_write(reg, value) + +#define NCR5380_queue_command sun3scsi_queue_command +#define NCR5380_bus_reset sun3scsi_bus_reset +#define NCR5380_abort sun3scsi_abort +#define NCR5380_show_info sun3scsi_show_info +#define NCR5380_info sun3scsi_info +#define NCR5380_dma_read_setup(instance, data, count) \ + sun3scsi_dma_setup(data, count, 0) +#define NCR5380_dma_write_setup(instance, data, count) \ + sun3scsi_dma_setup(data, count, 1) +#define NCR5380_dma_residual(instance) \ + sun3scsi_dma_residual(instance) +#define NCR5380_dma_xfer_len(instance, cmd, phase) \ + sun3scsi_dma_xfer_len(cmd->SCp.this_residual, cmd, !((phase) & SR_IO)) -static irqreturn_t scsi_sun3_intr(int irq, void *dummy); -static inline unsigned char sun3scsi_read(int reg); -static inline void sun3scsi_write(int reg, int value); +#define NCR5380_acquire_dma_irq(instance) (1) +#define NCR5380_release_dma_irq(instance) + +#include "NCR5380.h" + + +extern int sun3_map_test(unsigned long, char *); static int setup_can_queue = -1; module_param(setup_can_queue, int, 0); @@ -117,9 +86,7 @@ module_param(setup_use_tagged_queuing, int, 0); static int setup_hostid = -1; module_param(setup_hostid, int, 0); -static struct scsi_cmnd *sun3_dma_setup_done = NULL; - -#define RESET_RUN_DONE +/* #define RESET_BOOT */ #define AFTER_RESET_DELAY (HZ/2) @@ -129,18 +96,15 @@ static struct scsi_cmnd *sun3_dma_setup_done = NULL; /* dvma buffer to allocate -- 32k should hopefully be more than sufficient */ #define SUN3_DVMA_BUFSIZE 0xe000 -/* minimum number of bytes to do dma on */ -#define SUN3_DMA_MINSIZE 128 - -static volatile unsigned char *sun3_scsi_regp; +static struct scsi_cmnd *sun3_dma_setup_done; +static unsigned char *sun3_scsi_regp; static volatile struct sun3_dma_regs *dregs; -#ifndef SUN3_SCSI_VME -static struct sun3_udc_regs *udc_regs = NULL; -#endif +static struct sun3_udc_regs *udc_regs; static unsigned char *sun3_dma_orig_addr = NULL; static unsigned long sun3_dma_orig_count = 0; static int sun3_dma_active = 0; static unsigned long last_residual = 0; +static struct Scsi_Host *default_instance; /* * NCR 5380 register access functions @@ -148,12 +112,12 @@ static unsigned long last_residual = 0; static inline unsigned char sun3scsi_read(int reg) { - return( sun3_scsi_regp[reg] ); + return in_8(sun3_scsi_regp + reg); } static inline void sun3scsi_write(int reg, int value) { - sun3_scsi_regp[reg] = value; + out_8(sun3_scsi_regp + reg, value); } #ifndef SUN3_SCSI_VME @@ -180,213 +144,10 @@ static inline void sun3_udc_write(unsigned short val, unsigned char reg) } #endif -/* - * XXX: status debug - */ -static struct Scsi_Host *default_instance; - -/* - * Function : int sun3scsi_detect(struct scsi_host_template * tpnt) - * - * Purpose : initializes mac NCR5380 driver based on the - * command line / compile time port and irq definitions. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -static int __init sun3scsi_detect(struct scsi_host_template *tpnt) -{ - unsigned long ioaddr, irq; - static int called = 0; - struct Scsi_Host *instance; -#ifdef SUN3_SCSI_VME - int i; - unsigned long addrs[3] = { IOBASE_SUN3_VMESCSI, - IOBASE_SUN3_VMESCSI + 0x4000, - 0 }; - unsigned long vecs[3] = { SUN3_VEC_VMESCSI0, - SUN3_VEC_VMESCSI1, - 0 }; -#endif - - /* check that this machine has an onboard 5380 */ - switch(idprom->id_machtype) { -#ifdef SUN3_SCSI_VME - case SM_SUN3|SM_3_160: - case SM_SUN3|SM_3_260: - break; -#else - case SM_SUN3|SM_3_50: - case SM_SUN3|SM_3_60: - break; -#endif - - default: - return 0; - } - - if(called) - return 0; - -#ifdef SUN3_SCSI_VME - tpnt->proc_name = "Sun3 5380 VME SCSI"; -#else - tpnt->proc_name = "Sun3 5380 SCSI"; -#endif - - /* setup variables */ - tpnt->can_queue = - (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE; - tpnt->cmd_per_lun = - (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN; - tpnt->sg_tablesize = - (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE; - - if (setup_hostid >= 0) - tpnt->this_id = setup_hostid; - else { - /* use 7 as default */ - tpnt->this_id = 7; - } - -#ifdef SUN3_SCSI_VME - ioaddr = 0; - for (i = 0; addrs[i] != 0; i++) { - unsigned char x; - - ioaddr = (unsigned long)sun3_ioremap(addrs[i], PAGE_SIZE, - SUN3_PAGE_TYPE_VME16); - irq = vecs[i]; - sun3_scsi_regp = (unsigned char *)ioaddr; - - dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8); - - if (sun3_map_test((unsigned long)dregs, &x)) { - unsigned short oldcsr; - - oldcsr = dregs->csr; - dregs->csr = 0; - udelay(SUN3_DMA_DELAY); - if (dregs->csr == 0x1400) - break; - - dregs->csr = oldcsr; - } - - iounmap((void *)ioaddr); - ioaddr = 0; - } - - if (!ioaddr) - return 0; -#else - irq = IRQ_SUN3_SCSI; - ioaddr = (unsigned long)ioremap(IOBASE_SUN3_SCSI, PAGE_SIZE); - sun3_scsi_regp = (unsigned char *)ioaddr; - - dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8); - - if((udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs))) - == NULL) { - printk("SUN3 Scsi couldn't allocate DVMA memory!\n"); - return 0; - } -#endif -#ifdef SUPPORT_TAGS - if (setup_use_tagged_queuing < 0) - setup_use_tagged_queuing = USE_TAGGED_QUEUING; -#endif - - instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if(instance == NULL) - return 0; - - default_instance = instance; - - instance->io_port = (unsigned long) ioaddr; - instance->irq = irq; - - NCR5380_init(instance, 0); - - instance->n_io_port = 32; - - ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0; - - if (request_irq(instance->irq, scsi_sun3_intr, - 0, "Sun3SCSI-5380", instance)) { -#ifndef REAL_DMA - printk("scsi%d: IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = SCSI_IRQ_NONE; -#else - printk("scsi%d: IRQ%d not free, bailing out\n", - instance->host_no, instance->irq); - return 0; -#endif - } - - pr_info("scsi%d: %s at port %lX irq", instance->host_no, - tpnt->proc_name, instance->io_port); - if (instance->irq == SCSI_IRQ_NONE) - printk ("s disabled"); - else - printk (" %d", instance->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - instance->can_queue, instance->cmd_per_lun, - SUN3SCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", instance->host_no); - NCR5380_print_options(instance); - printk("\n"); - - dregs->csr = 0; - udelay(SUN3_DMA_DELAY); - dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR; - udelay(SUN3_DMA_DELAY); - dregs->fifo_count = 0; -#ifdef SUN3_SCSI_VME - dregs->fifo_count_hi = 0; - dregs->dma_addr_hi = 0; - dregs->dma_addr_lo = 0; - dregs->dma_count_hi = 0; - dregs->dma_count_lo = 0; - - dregs->ivect = VME_DATA24 | (instance->irq & 0xff); -#endif - - called = 1; - -#ifdef RESET_BOOT - sun3_scsi_reset_boot(instance); -#endif - - return 1; -} - -int sun3scsi_release (struct Scsi_Host *shpnt) -{ - if (shpnt->irq != SCSI_IRQ_NONE) - free_irq(shpnt->irq, shpnt); - - iounmap((void *)sun3_scsi_regp); - - NCR5380_exit(shpnt); - return 0; -} - #ifdef RESET_BOOT -/* - * Our 'bus reset on boot' function - */ - static void sun3_scsi_reset_boot(struct Scsi_Host *instance) { unsigned long end; - - NCR5380_local_declare(); - NCR5380_setup(instance); /* * Do a SCSI reset to clean up the bus during initialization. No @@ -422,11 +183,6 @@ static void sun3_scsi_reset_boot(struct Scsi_Host *instance) } #endif -static const char *sun3scsi_info(struct Scsi_Host *spnt) -{ - return ""; -} - // safe bits for the CSR #define CSR_GOOD 0x060f @@ -468,7 +224,6 @@ static irqreturn_t scsi_sun3_intr(int irq, void *dummy) void sun3_sun3_debug (void) { unsigned long flags; - NCR5380_local_declare(); if (default_instance) { local_irq_save(flags); @@ -732,25 +487,200 @@ static int sun3scsi_dma_finish(int write_flag) } -#include "sun3_NCR5380.c" +#include "atari_NCR5380.c" -static struct scsi_host_template driver_template = { +#ifdef SUN3_SCSI_VME +#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI" +#define DRV_MODULE_NAME "sun3_scsi_vme" +#else +#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI" +#define DRV_MODULE_NAME "sun3_scsi" +#endif + +#define PFX DRV_MODULE_NAME ": " + +static struct scsi_host_template sun3_scsi_template = { + .module = THIS_MODULE, + .proc_name = DRV_MODULE_NAME, .show_info = sun3scsi_show_info, .name = SUN3_SCSI_NAME, - .detect = sun3scsi_detect, - .release = sun3scsi_release, .info = sun3scsi_info, .queuecommand = sun3scsi_queue_command, .eh_abort_handler = sun3scsi_abort, .eh_bus_reset_handler = sun3scsi_bus_reset, - .can_queue = CAN_QUEUE, + .can_queue = 16, .this_id = 7, - .sg_tablesize = SG_TABLESIZE, - .cmd_per_lun = CMD_PER_LUN, + .sg_tablesize = SG_NONE, + .cmd_per_lun = 2, .use_clustering = DISABLE_CLUSTERING }; +static int __init sun3_scsi_probe(struct platform_device *pdev) +{ + struct Scsi_Host *instance; + int error; + struct resource *irq, *mem; + unsigned char *ioaddr; + int host_flags = 0; +#ifdef SUN3_SCSI_VME + int i; +#endif + + if (setup_can_queue > 0) + sun3_scsi_template.can_queue = setup_can_queue; + if (setup_cmd_per_lun > 0) + sun3_scsi_template.cmd_per_lun = setup_cmd_per_lun; + if (setup_sg_tablesize >= 0) + sun3_scsi_template.sg_tablesize = setup_sg_tablesize; + if (setup_hostid >= 0) + sun3_scsi_template.this_id = setup_hostid & 7; + +#ifdef SUN3_SCSI_VME + ioaddr = NULL; + for (i = 0; i < 2; i++) { + unsigned char x; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, i); + mem = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!irq || !mem) + break; + + ioaddr = sun3_ioremap(mem->start, resource_size(mem), + SUN3_PAGE_TYPE_VME16); + dregs = (struct sun3_dma_regs *)(ioaddr + 8); + + if (sun3_map_test((unsigned long)dregs, &x)) { + unsigned short oldcsr; + + oldcsr = dregs->csr; + dregs->csr = 0; + udelay(SUN3_DMA_DELAY); + if (dregs->csr == 0x1400) + break; + + dregs->csr = oldcsr; + } + + iounmap(ioaddr); + ioaddr = NULL; + } + if (!ioaddr) + return -ENODEV; +#else + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!irq || !mem) + return -ENODEV; + + ioaddr = ioremap(mem->start, resource_size(mem)); + dregs = (struct sun3_dma_regs *)(ioaddr + 8); + + udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs)); + if (!udc_regs) { + pr_err(PFX "couldn't allocate DVMA memory!\n"); + iounmap(ioaddr); + return -ENOMEM; + } +#endif + + sun3_scsi_regp = ioaddr; + + instance = scsi_host_alloc(&sun3_scsi_template, + sizeof(struct NCR5380_hostdata)); + if (!instance) { + error = -ENOMEM; + goto fail_alloc; + } + default_instance = instance; + + instance->io_port = (unsigned long)ioaddr; + instance->irq = irq->start; + +#ifdef SUPPORT_TAGS + host_flags |= setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0; +#endif + + NCR5380_init(instance, host_flags); + + error = request_irq(instance->irq, scsi_sun3_intr, 0, + "NCR5380", instance); + if (error) { +#ifdef REAL_DMA + pr_err(PFX "scsi%d: IRQ %d not free, bailing out\n", + instance->host_no, instance->irq); + goto fail_irq; +#else + pr_warn(PFX "scsi%d: IRQ %d not free, interrupts disabled\n", + instance->host_no, instance->irq); + instance->irq = NO_IRQ; +#endif + } + + dregs->csr = 0; + udelay(SUN3_DMA_DELAY); + dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR; + udelay(SUN3_DMA_DELAY); + dregs->fifo_count = 0; +#ifdef SUN3_SCSI_VME + dregs->fifo_count_hi = 0; + dregs->dma_addr_hi = 0; + dregs->dma_addr_lo = 0; + dregs->dma_count_hi = 0; + dregs->dma_count_lo = 0; + + dregs->ivect = VME_DATA24 | (instance->irq & 0xff); +#endif + +#ifdef RESET_BOOT + sun3_scsi_reset_boot(instance); +#endif + + error = scsi_add_host(instance, NULL); + if (error) + goto fail_host; + + platform_set_drvdata(pdev, instance); + + scsi_scan_host(instance); + return 0; + +fail_host: + if (instance->irq != NO_IRQ) + free_irq(instance->irq, instance); +fail_irq: + NCR5380_exit(instance); + scsi_host_put(instance); +fail_alloc: + if (udc_regs) + dvma_free(udc_regs); + iounmap(sun3_scsi_regp); + return error; +} + +static int __exit sun3_scsi_remove(struct platform_device *pdev) +{ + struct Scsi_Host *instance = platform_get_drvdata(pdev); + + scsi_remove_host(instance); + if (instance->irq != NO_IRQ) + free_irq(instance->irq, instance); + NCR5380_exit(instance); + scsi_host_put(instance); + if (udc_regs) + dvma_free(udc_regs); + iounmap(sun3_scsi_regp); + return 0; +} + +static struct platform_driver sun3_scsi_driver = { + .remove = __exit_p(sun3_scsi_remove), + .driver = { + .name = DRV_MODULE_NAME, + .owner = THIS_MODULE, + }, +}; -#include "scsi_module.c" +module_platform_driver_probe(sun3_scsi_driver, sun3_scsi_probe); +MODULE_ALIAS("platform:" DRV_MODULE_NAME); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/sun3_scsi.h b/drivers/scsi/sun3_scsi.h index e96a37cf06ac..d22745fae328 100644 --- a/drivers/scsi/sun3_scsi.h +++ b/drivers/scsi/sun3_scsi.h @@ -13,95 +13,11 @@ * (Unix and Linux consulting and custom programming) * drew@colorado.edu * +1 (303) 440-4894 - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ #ifndef SUN3_SCSI_H #define SUN3_SCSI_H -#define SUN3SCSI_PUBLIC_RELEASE 1 - -/* - * Int: level 2 autovector - * IO: type 1, base 0x00140000, 5 bits phys space: A<4..0> - */ -#define IRQ_SUN3_SCSI 2 -#define IOBASE_SUN3_SCSI 0x00140000 - -#define IOBASE_SUN3_VMESCSI 0xff200000 - -static int sun3scsi_abort(struct scsi_cmnd *); -static int sun3scsi_detect (struct scsi_host_template *); -static const char *sun3scsi_info (struct Scsi_Host *); -static int sun3scsi_bus_reset(struct scsi_cmnd *); -static int sun3scsi_queue_command(struct Scsi_Host *, struct scsi_cmnd *); -static int sun3scsi_release (struct Scsi_Host *); - -#ifndef CMD_PER_LUN -#define CMD_PER_LUN 2 -#endif - -#ifndef CAN_QUEUE -#define CAN_QUEUE 16 -#endif - -#ifndef SG_TABLESIZE -#define SG_TABLESIZE SG_NONE -#endif - -#ifndef MAX_TAGS -#define MAX_TAGS 32 -#endif - -#ifndef USE_TAGGED_QUEUING -#define USE_TAGGED_QUEUING 1 -#endif - -#include <scsi/scsicam.h> - -#ifdef SUN3_SCSI_VME -#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI" -#else -#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI" -#endif - -#define NCR5380_implementation_fields \ - int port, ctrl - -#define NCR5380_local_declare() \ - struct Scsi_Host *_instance - -#define NCR5380_setup(instance) \ - _instance = instance - -#define NCR5380_read(reg) sun3scsi_read(reg) -#define NCR5380_write(reg, value) sun3scsi_write(reg, value) - -#define NCR5380_intr sun3scsi_intr -#define NCR5380_queue_command sun3scsi_queue_command -#define NCR5380_bus_reset sun3scsi_bus_reset -#define NCR5380_abort sun3scsi_abort -#define NCR5380_show_info sun3scsi_show_info -#define NCR5380_dma_xfer_len(i, cmd, phase) \ - sun3scsi_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1) - -#define NCR5380_dma_write_setup(instance, data, count) sun3scsi_dma_setup(data, count, 1) -#define NCR5380_dma_read_setup(instance, data, count) sun3scsi_dma_setup(data, count, 0) -#define NCR5380_dma_residual sun3scsi_dma_residual - /* additional registers - mainly DMA control regs */ /* these start at regbase + 8 -- directly after the NCR regs */ struct sun3_dma_regs { diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index e59e6f96b725..5d00e514ff28 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -820,9 +820,7 @@ static int sym53c8xx_slave_configure(struct scsi_device *sdev) if (reqtags > SYM_CONF_MAX_TAG) reqtags = SYM_CONF_MAX_TAG; depth_to_use = reqtags ? reqtags : 1; - scsi_adjust_queue_depth(sdev, - sdev->tagged_supported ? MSG_SIMPLE_TAG : 0, - depth_to_use); + scsi_change_queue_depth(sdev, depth_to_use); lp->s.scdev_depth = depth_to_use; sym_tune_dev_queuing(tp, sdev->lun, reqtags); diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c index 8cc80931df14..87828acbf7c6 100644 --- a/drivers/scsi/t128.c +++ b/drivers/scsi/t128.c @@ -1,4 +1,3 @@ -#define AUTOSENSE #define PSEUDO_DMA /* @@ -12,8 +11,6 @@ * drew@colorado.edu * +1 (303) 440-4894 * - * DISTRIBUTION RELEASE 3. - * * For more information, please consult * * Trantor Systems, Ltd. @@ -24,40 +21,9 @@ * 5415 Randall Place * Fremont, CA 94538 * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - * - * and - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ /* - * Options : - * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically - * for commands that return with a CHECK CONDITION status. - * - * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance - * increase compared to polled I/O. - * - * PARITY - enable parity checking. Not supported. - * - * SCSI2 - enable support for SCSI-II tagged queueing. Untested. - * - * - * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You - * only really want to use this if you're having a problem with - * dropped characters during high speed communications, and even - * then, you're going to be better off twiddling with transfersize. - * - * USLEEP - enable support for devices that don't disconnect. Untested. - * * The card is detected and initialized in one of several ways : * 1. Autoprobe (default) - since the board is memory mapped, * a BIOS signature is scanned for to locate the registers. @@ -111,7 +77,6 @@ #include <linux/module.h> #include <linux/delay.h> -#include "scsi.h" #include <scsi/scsi_host.h> #include "t128.h" #define AUTOPROBE_IRQ @@ -148,6 +113,7 @@ static struct signature { #define NO_SIGNATURES ARRAY_SIZE(signatures) +#ifndef MODULE /* * Function : t128_setup(char *str, int *ints) * @@ -158,9 +124,13 @@ static struct signature { * */ -void __init t128_setup(char *str, int *ints){ +static int __init t128_setup(char *str) +{ static int commandline_current = 0; int i; + int ints[10]; + + get_options(str, ARRAY_SIZE(ints), ints); if (ints[0] != 2) printk("t128_setup : usage t128=address,irq\n"); else @@ -174,8 +144,12 @@ void __init t128_setup(char *str, int *ints){ } ++commandline_current; } + return 1; } +__setup("t128=", t128_setup); +#endif + /* * Function : int t128_detect(struct scsi_host_template * tpnt) * @@ -189,17 +163,14 @@ void __init t128_setup(char *str, int *ints){ * */ -int __init t128_detect(struct scsi_host_template * tpnt){ +static int __init t128_detect(struct scsi_host_template *tpnt) +{ static int current_override = 0, current_base = 0; struct Scsi_Host *instance; unsigned long base; void __iomem *p; int sig, count; - tpnt->proc_name = "t128"; - tpnt->show_info = t128_show_info; - tpnt->write_info = t128_write_info; - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { base = 0; p = NULL; @@ -254,15 +225,19 @@ found: else instance->irq = NCR5380_probe_irq(instance, T128_IRQS); - if (instance->irq != SCSI_IRQ_NONE) + /* Compatibility with documented NCR5380 kernel parameters */ + if (instance->irq == 255) + instance->irq = NO_IRQ; + + if (instance->irq != NO_IRQ) if (request_irq(instance->irq, t128_intr, 0, "t128", instance)) { printk("scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); - instance->irq = SCSI_IRQ_NONE; + instance->irq = NO_IRQ; } - if (instance->irq == SCSI_IRQ_NONE) { + if (instance->irq == NO_IRQ) { printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); } @@ -271,16 +246,6 @@ found: printk("scsi%d : irq = %d\n", instance->host_no, instance->irq); #endif - printk("scsi%d : at 0x%08lx", instance->host_no, instance->base); - if (instance->irq == SCSI_IRQ_NONE) - printk (" interrupts disabled"); - else - printk (" irq %d", instance->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - CAN_QUEUE, CMD_PER_LUN, T128_PUBLIC_RELEASE); - NCR5380_print_options(instance); - printk("\n"); - ++current_override; ++count; } @@ -291,7 +256,7 @@ static int t128_release(struct Scsi_Host *shost) { NCR5380_local_declare(); NCR5380_setup(shost); - if (shost->irq) + if (shost->irq != NO_IRQ) free_irq(shost->irq, shost); NCR5380_exit(shost); if (shost->io_port && shost->n_io_port) @@ -321,8 +286,8 @@ static int t128_release(struct Scsi_Host *shost) * and matching the H_C_S coordinates to what DOS uses. */ -int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int * ip) +static int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int *ip) { ip[0] = 64; ip[1] = 32; @@ -430,6 +395,10 @@ static struct scsi_host_template driver_template = { .name = "Trantor T128/T128F/T228", .detect = t128_detect, .release = t128_release, + .proc_name = "t128", + .show_info = t128_show_info, + .write_info = t128_write_info, + .info = t128_info, .queuecommand = t128_queue_command, .eh_abort_handler = t128_abort, .eh_bus_reset_handler = t128_bus_reset, diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h index fd68cecc62af..2c7371454dfd 100644 --- a/drivers/scsi/t128.h +++ b/drivers/scsi/t128.h @@ -8,8 +8,6 @@ * drew@colorado.edu * +1 (303) 440-4894 * - * DISTRIBUTION RELEASE 3. - * * For more information, please consult * * Trantor Systems, Ltd. @@ -20,25 +18,11 @@ * 5415 Randall Place * Fremont, CA 94538 * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - * - * and - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 */ #ifndef T128_H #define T128_H -#define T128_PUBLIC_RELEASE 3 - #define TDEBUG 0 #define TDEBUG_INIT 0x1 #define TDEBUG_TRANSFER 0x2 @@ -88,12 +72,6 @@ #define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */ #ifndef ASM -static int t128_abort(struct scsi_cmnd *); -static int t128_biosparam(struct scsi_device *, struct block_device *, - sector_t, int*); -static int t128_detect(struct scsi_host_template *); -static int t128_queue_command(struct Scsi_Host *, struct scsi_cmnd *); -static int t128_bus_reset(struct scsi_cmnd *); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 @@ -134,6 +112,7 @@ static int t128_bus_reset(struct scsi_cmnd *); #define NCR5380_queue_command t128_queue_command #define NCR5380_abort t128_abort #define NCR5380_bus_reset t128_bus_reset +#define NCR5380_info t128_info #define NCR5380_show_info t128_show_info #define NCR5380_write_info t128_write_info diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c deleted file mode 100644 index 764575726c85..000000000000 --- a/drivers/scsi/tmscsim.c +++ /dev/null @@ -1,2620 +0,0 @@ -/************************************************************************ - * FILE NAME : TMSCSIM.C * - * BY : C.L. Huang, ching@tekram.com.tw * - * Description: Device Driver for Tekram DC-390(T) PCI SCSI * - * Bus Master Host Adapter * - * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. * - ************************************************************************ - * (C) Copyright: put under GNU GPL in 10/96 * - * (see Documentation/scsi/tmscsim.txt) * - ************************************************************************ - * $Id: tmscsim.c,v 2.60.2.30 2000/12/20 01:07:12 garloff Exp $ * - * Enhancements and bugfixes by * - * Kurt Garloff <kurt@garloff.de> <garloff@suse.de> * - ************************************************************************ - * HISTORY: * - * * - * REV# DATE NAME DESCRIPTION * - * 1.00 96/04/24 CLH First release * - * 1.01 96/06/12 CLH Fixed bug of Media Change for Removable * - * Device, scan all LUN. Support Pre2.0.10 * - * 1.02 96/06/18 CLH Fixed bug of Command timeout ... * - * 1.03 96/09/25 KG Added tmscsim_proc_info() * - * 1.04 96/10/11 CLH Updating for support KV 2.0.x * - * 1.05 96/10/18 KG Fixed bug in DC390_abort(null ptr deref)* - * 1.06 96/10/25 KG Fixed module support * - * 1.07 96/11/09 KG Fixed tmscsim_proc_info() * - * 1.08 96/11/18 KG Fixed null ptr in DC390_Disconnect() * - * 1.09 96/11/30 KG Added register the allocated IO space * - * 1.10 96/12/05 CLH Modified tmscsim_proc_info(), and reset * - * pending interrupt in DC390_detect() * - * 1.11 97/02/05 KG/CLH Fixeds problem with partitions greater * - * than 1GB * - * 1.12 98/02/15 MJ Rewritten PCI probing * - * 1.13 98/04/08 KG Support for non DC390, __initfunc decls,* - * changed max devs from 10 to 16 * - * 1.14a 98/05/05 KG Dynamic DCB allocation, add-single-dev * - * for LUNs if LUN_SCAN (BIOS) not set * - * runtime config using /proc interface * - * 1.14b 98/05/06 KG eliminated cli (); sti (); spinlocks * - * 1.14c 98/05/07 KG 2.0.x compatibility * - * 1.20a 98/05/07 KG changed names of funcs to be consistent * - * DC390_ (entry points), dc390_ (internal)* - * reworked locking * - * 1.20b 98/05/12 KG bugs: version, kfree, _ctmp * - * debug output * - * 1.20c 98/05/12 KG bugs: kfree, parsing, EEpromDefaults * - * 1.20d 98/05/14 KG bugs: list linkage, clear flag after * - * reset on startup, code cleanup * - * 1.20e 98/05/15 KG spinlock comments, name space cleanup * - * pLastDCB now part of ACB structure * - * added stats, timeout for 2.1, TagQ bug * - * RESET and INQUIRY interface commands * - * 1.20f 98/05/18 KG spinlocks fixes, max_lun fix, free DCBs * - * for missing LUNs, pending int * - * 1.20g 98/05/19 KG Clean up: Avoid short * - * 1.20h 98/05/21 KG Remove AdaptSCSIID, max_lun ... * - * 1.20i 98/05/21 KG Aiiie: Bug with TagQMask * - * 1.20j 98/05/24 KG Handle STAT_BUSY, handle pACB->pLinkDCB * - * == 0 in remove_dev and DoingSRB_Done * - * 1.20k 98/05/25 KG DMA_INT (experimental) * - * 1.20l 98/05/27 KG remove DMA_INT; DMA_IDLE cmds added; * - * 1.20m 98/06/10 KG glitch configurable; made some global * - * vars part of ACB; use DC390_readX * - * 1.20n 98/06/11 KG startup params * - * 1.20o 98/06/15 KG added TagMaxNum to boot/module params * - * Device Nr -> Idx, TagMaxNum power of 2 * - * 1.20p 98/06/17 KG Docu updates. Reset depends on settings * - * pci_set_master added; 2.0.xx: pcibios_* * - * used instead of MechNum things ... * - * 1.20q 98/06/23 KG Changed defaults. Added debug code for * - * removable media and fixed it. TagMaxNum * - * fixed for DC390. Locking: ACB, DRV for * - * better IRQ sharing. Spelling: Queueing * - * Parsing and glitch_cfg changes. Display * - * real SyncSpeed value. Made DisConn * - * functional (!) * - * 1.20r 98/06/30 KG Debug macros, allow disabling DsCn, set * - * BIT4 in CtrlR4, EN_PAGE_INT, 2.0 module * - * param -1 fixed. * - * 1.20s 98/08/20 KG Debug info on abort(), try to check PCI,* - * phys_to_bus instead of phys_to_virt, * - * fixed sel. process, fixed locking, * - * added MODULE_XXX infos, changed IRQ * - * request flags, disable DMA_INT * - * 1.20t 98/09/07 KG TagQ report fixed; Write Erase DMA Stat;* - * initfunc -> __init; better abort; * - * Timeout for XFER_DONE & BLAST_COMPLETE; * - * Allow up to 33 commands being processed * - * 2.0a 98/10/14 KG Max Cmnds back to 17. DMA_Stat clearing * - * all flags. Clear within while() loops * - * in DataIn_0/Out_0. Null ptr in dumpinfo * - * for pSRB==0. Better locking during init.* - * bios_param() now respects part. table. * - * 2.0b 98/10/24 KG Docu fixes. Timeout Msg in DMA Blast. * - * Disallow illegal idx in INQUIRY/REMOVE * - * 2.0c 98/11/19 KG Cleaned up detect/init for SMP boxes, * - * Write Erase DMA (1.20t) caused problems * - * 2.0d 98/12/25 KG Christmas release ;-) Message handling * - * completely reworked. Handle target ini- * - * tiated SDTR correctly. * - * 2.0d1 99/01/25 KG Try to handle RESTORE_PTR * - * 2.0d2 99/02/08 KG Check for failure of kmalloc, correct * - * inclusion of scsicam.h, DelayReset * - * 2.0d3 99/05/31 KG DRIVER_OK -> DID_OK, DID_NO_CONNECT, * - * detect Target mode and warn. * - * pcmd->result handling cleaned up. * - * 2.0d4 99/06/01 KG Cleaned selection process. Found bug * - * which prevented more than 16 tags. Now: * - * 24. SDTR cleanup. Cleaner multi-LUN * - * handling. Don't modify ControlRegs/FIFO * - * when connected. * - * 2.0d5 99/06/01 KG Clear DevID, Fix INQUIRY after cfg chg. * - * 2.0d6 99/06/02 KG Added ADD special command to allow cfg. * - * before detection. Reset SYNC_NEGO_DONE * - * after a bus reset. * - * 2.0d7 99/06/03 KG Fixed bugs wrt add,remove commands * - * 2.0d8 99/06/04 KG Removed copying of cmnd into CmdBlock. * - * Fixed Oops in _release(). * - * 2.0d9 99/06/06 KG Also tag queue INQUIRY, T_U_R, ... * - * Allow arb. no. of Tagged Cmnds. Max 32 * - * 2.0d1099/06/20 KG TagMaxNo changes now honoured! Queueing * - * clearified (renamed ..) TagMask handling* - * cleaned. * - * 2.0d1199/06/28 KG cmd->result now identical to 2.0d2 * - * 2.0d1299/07/04 KG Changed order of processing in IRQ * - * 2.0d1399/07/05 KG Don't update DCB fields if removed * - * 2.0d1499/07/05 KG remove_dev: Move kfree() to the end * - * 2.0d1599/07/12 KG use_new_eh_code: 0, ULONG -> UINT where * - * appropriate * - * 2.0d1699/07/13 KG Reenable StartSCSI interrupt, Retry msg * - * 2.0d1799/07/15 KG Remove debug msg. Disable recfg. when * - * there are queued cmnds * - * 2.0d1899/07/18 KG Selection timeout: Don't requeue * - * 2.0d1999/07/18 KG Abort: Only call scsi_done if dequeued * - * 2.0d2099/07/19 KG Rst_Detect: DoingSRB_Done * - * 2.0d2199/08/15 KG dev_id for request/free_irq, cmnd[0] for* - * RETRY, SRBdone does DID_ABORT for the * - * cmd passed by DC390_reset() * - * 2.0d2299/08/25 KG dev_id fixed. can_queue: 42 * - * 2.0d2399/08/25 KG Removed some debugging code. dev_id * - * now is set to pACB. Use u8,u16,u32. * - * 2.0d2499/11/14 KG Unreg. I/O if failed IRQ alloc. Call * - * done () w/ DID_BAD_TARGET in case of * - * missing DCB. We are old EH!! * - * 2.0d2500/01/15 KG 2.3.3x compat from Andreas Schultz * - * set unique_id. Disable RETRY message. * - * 2.0d2600/01/29 KG Go to new EH. * - * 2.0d2700/01/31 KG ... but maintain 2.0 compat. * - * and fix DCB freeing * - * 2.0d2800/02/14 KG Queue statistics fixed, dump special cmd* - * Waiting_Timer for failed StartSCSI * - * New EH: Don't return cmnds to ML on RST * - * Use old EH (don't have new EH fns yet) * - * Reset: Unlock, but refuse to queue * - * 2.3 __setup function * - * 2.0e 00/05/22 KG Return residual for 2.3 * - * 2.0e1 00/05/25 KG Compile fixes for 2.3.99 * - * 2.0e2 00/05/27 KG Jeff Garzik's pci_enable_device() * - * 2.0e3 00/09/29 KG Some 2.4 changes. Don't try Sync Nego * - * before INQUIRY has reported ability. * - * Recognise INQUIRY as scanning command. * - * 2.0e4 00/10/13 KG Allow compilation into 2.4 kernel * - * 2.0e5 00/11/17 KG Store Inq.flags in DCB * - * 2.0e6 00/11/22 KG 2.4 init function (Thx to O.Schumann) * - * 2.4 PCI device table (Thx to A.Richter) * - * 2.0e7 00/11/28 KG Allow overriding of BIOS settings * - * 2.0f 00/12/20 KG Handle failed INQUIRYs during scan * - * 2.1a 03/11/29 GL, KG Initial fixing for 2.6. Convert to * - * use the current PCI-mapping API, update * - * command-queuing. * - * 2.1b 04/04/13 GL Fix for 64-bit platforms * - * 2.1b1 04/01/31 GL (applied 05.04) Remove internal * - * command-queuing. * - * 2.1b2 04/02/01 CH (applied 05.04) Fix error-handling * - * 2.1c 04/05/23 GL Update to use the new pci_driver API, * - * some scsi EH updates, more cleanup. * - * 2.1d 04/05/27 GL Moved setting of scan_devices to * - * slave_alloc/_configure/_destroy, as * - * suggested by CH. * - ***********************************************************************/ - -/* DEBUG options */ -//#define DC390_DEBUG0 -//#define DC390_DEBUG1 -//#define DC390_DCBDEBUG -//#define DC390_PARSEDEBUG -//#define DC390_REMOVABLEDEBUG -//#define DC390_LOCKDEBUG - -//#define NOP do{}while(0) -#define C_NOP - -/* Debug definitions */ -#ifdef DC390_DEBUG0 -# define DEBUG0(x) x -#else -# define DEBUG0(x) C_NOP -#endif -#ifdef DC390_DEBUG1 -# define DEBUG1(x) x -#else -# define DEBUG1(x) C_NOP -#endif -#ifdef DC390_DCBDEBUG -# define DCBDEBUG(x) x -#else -# define DCBDEBUG(x) C_NOP -#endif -#ifdef DC390_PARSEDEBUG -# define PARSEDEBUG(x) x -#else -# define PARSEDEBUG(x) C_NOP -#endif -#ifdef DC390_REMOVABLEDEBUG -# define REMOVABLEDEBUG(x) x -#else -# define REMOVABLEDEBUG(x) C_NOP -#endif -#define DCBDEBUG1(x) C_NOP - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/ioport.h> -#include <linux/pci.h> -#include <linux/proc_fs.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/blkdev.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <asm/io.h> - -#include <scsi/scsi.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_host.h> -#include <scsi/scsicam.h> -#include <scsi/scsi_tcq.h> - - -#define DC390_BANNER "Tekram DC390/AM53C974" -#define DC390_VERSION "2.1d 2004-05-27" - -#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI - -#include "tmscsim.h" - - -static void dc390_DataOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_DataIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_Command_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_Status_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_MsgOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_MsgIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_DataOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_DataInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_CommandPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_StatusPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_MsgOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_MsgInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_Nop_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); -static void dc390_Nop_1( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus); - -static void dc390_SetXferRate( struct dc390_acb* pACB, struct dc390_dcb* pDCB ); -static void dc390_Disconnect( struct dc390_acb* pACB ); -static void dc390_Reselect( struct dc390_acb* pACB ); -static void dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB ); -static void dc390_ScsiRstDetect( struct dc390_acb* pACB ); -static void dc390_EnableMsgOut_Abort(struct dc390_acb*, struct dc390_srb*); -static void dc390_dumpinfo(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB); -static void dc390_ResetDevParam(struct dc390_acb* pACB); - -static u32 dc390_laststatus = 0; -static u8 dc390_adapterCnt = 0; - -static int disable_clustering; -module_param(disable_clustering, int, S_IRUGO); -MODULE_PARM_DESC(disable_clustering, "If you experience problems with your devices, try setting to 1"); - -/* Startup values, to be overriden on the commandline */ -static int tmscsim[] = {-2, -2, -2, -2, -2, -2}; - -module_param_array(tmscsim, int, NULL, 0); -MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)"); -MODULE_AUTHOR("C.L. Huang / Kurt Garloff"); -MODULE_DESCRIPTION("SCSI host adapter driver for Tekram DC390 and other AMD53C974A based PCI SCSI adapters"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); - -static void *dc390_phase0[]={ - dc390_DataOut_0, - dc390_DataIn_0, - dc390_Command_0, - dc390_Status_0, - dc390_Nop_0, - dc390_Nop_0, - dc390_MsgOut_0, - dc390_MsgIn_0, - dc390_Nop_1 - }; - -static void *dc390_phase1[]={ - dc390_DataOutPhase, - dc390_DataInPhase, - dc390_CommandPhase, - dc390_StatusPhase, - dc390_Nop_0, - dc390_Nop_0, - dc390_MsgOutPhase, - dc390_MsgInPhase, - dc390_Nop_1 - }; - -#ifdef DC390_DEBUG1 -static char* dc390_p0_str[] = { - "dc390_DataOut_0", - "dc390_DataIn_0", - "dc390_Command_0", - "dc390_Status_0", - "dc390_Nop_0", - "dc390_Nop_0", - "dc390_MsgOut_0", - "dc390_MsgIn_0", - "dc390_Nop_1" - }; - -static char* dc390_p1_str[] = { - "dc390_DataOutPhase", - "dc390_DataInPhase", - "dc390_CommandPhase", - "dc390_StatusPhase", - "dc390_Nop_0", - "dc390_Nop_0", - "dc390_MsgOutPhase", - "dc390_MsgInPhase", - "dc390_Nop_1" - }; -#endif - -static u8 dc390_eepromBuf[MAX_ADAPTER_NUM][EE_LEN]; -static u8 dc390_clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20}; -static u8 dc390_clock_speed[] = {100,80,67,57,50, 40, 31, 20}; - -/*********************************************************************** - * Functions for the management of the internal structures - * (DCBs, SRBs, Queueing) - * - **********************************************************************/ -static void inline dc390_start_segment(struct dc390_srb* pSRB) -{ - struct scatterlist *psgl = pSRB->pSegmentList; - - /* start new sg segment */ - pSRB->SGBusAddr = sg_dma_address(psgl); - pSRB->SGToBeXferLen = sg_dma_len(psgl); -} - -static unsigned long inline dc390_advance_segment(struct dc390_srb* pSRB, u32 residue) -{ - unsigned long xfer = pSRB->SGToBeXferLen - residue; - - /* xfer more bytes transferred */ - pSRB->SGBusAddr += xfer; - pSRB->TotalXferredLen += xfer; - pSRB->SGToBeXferLen = residue; - - return xfer; -} - -static struct dc390_dcb __inline__ *dc390_findDCB ( struct dc390_acb* pACB, u8 id, u8 lun) -{ - struct dc390_dcb* pDCB = pACB->pLinkDCB; if (!pDCB) return NULL; - while (pDCB->TargetID != id || pDCB->TargetLUN != lun) - { - pDCB = pDCB->pNextDCB; - if (pDCB == pACB->pLinkDCB) - return NULL; - } - DCBDEBUG1( printk (KERN_DEBUG "DCB %p (%02x,%02x) found.\n", \ - pDCB, pDCB->TargetID, pDCB->TargetLUN)); - return pDCB; -} - -/* Insert SRB oin top of free list */ -static __inline__ void dc390_Free_insert (struct dc390_acb* pACB, struct dc390_srb* pSRB) -{ - DEBUG0(printk ("DC390: Free SRB %p\n", pSRB)); - pSRB->pNextSRB = pACB->pFreeSRB; - pACB->pFreeSRB = pSRB; -} - -static __inline__ void dc390_Going_append (struct dc390_dcb* pDCB, struct dc390_srb* pSRB) -{ - pDCB->GoingSRBCnt++; - DEBUG0(printk("DC390: Append SRB %p to Going\n", pSRB)); - /* Append to the list of Going commands */ - if( pDCB->pGoingSRB ) - pDCB->pGoingLast->pNextSRB = pSRB; - else - pDCB->pGoingSRB = pSRB; - - pDCB->pGoingLast = pSRB; - /* No next one in sent list */ - pSRB->pNextSRB = NULL; -} - -static __inline__ void dc390_Going_remove (struct dc390_dcb* pDCB, struct dc390_srb* pSRB) -{ - DEBUG0(printk("DC390: Remove SRB %p from Going\n", pSRB)); - if (pSRB == pDCB->pGoingSRB) - pDCB->pGoingSRB = pSRB->pNextSRB; - else - { - struct dc390_srb* psrb = pDCB->pGoingSRB; - while (psrb && psrb->pNextSRB != pSRB) - psrb = psrb->pNextSRB; - if (!psrb) - { printk (KERN_ERR "DC390: Remove non-ex. SRB %p from Going!\n", pSRB); return; } - psrb->pNextSRB = pSRB->pNextSRB; - if (pSRB == pDCB->pGoingLast) - pDCB->pGoingLast = psrb; - } - pDCB->GoingSRBCnt--; -} - -static struct scatterlist* dc390_sg_build_single(struct scatterlist *sg, void *addr, unsigned int length) -{ - sg_init_one(sg, addr, length); - return sg; -} - -/* Create pci mapping */ -static int dc390_pci_map (struct dc390_srb* pSRB) -{ - int error = 0; - struct scsi_cmnd *pcmd = pSRB->pcmd; - struct pci_dev *pdev = pSRB->pSRBDCB->pDCBACB->pdev; - dc390_cmd_scp_t* cmdp = ((dc390_cmd_scp_t*)(&pcmd->SCp)); - - /* Map sense buffer */ - if (pSRB->SRBFlag & AUTO_REQSENSE) { - pSRB->pSegmentList = dc390_sg_build_single(&pSRB->Segmentx, pcmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); - pSRB->SGcount = pci_map_sg(pdev, pSRB->pSegmentList, 1, - DMA_FROM_DEVICE); - cmdp->saved_dma_handle = sg_dma_address(pSRB->pSegmentList); - - /* TODO: error handling */ - if (pSRB->SGcount != 1) - error = 1; - DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __func__, pcmd->sense_buffer, cmdp->saved_dma_handle)); - /* Map SG list */ - } else if (scsi_sg_count(pcmd)) { - int nseg; - - nseg = scsi_dma_map(pcmd); - - pSRB->pSegmentList = scsi_sglist(pcmd); - pSRB->SGcount = nseg; - - /* TODO: error handling */ - if (nseg < 0) - error = 1; - DEBUG1(printk("%s(): Mapped SG %p with %d (%d) elements\n",\ - __func__, scsi_sglist(pcmd), nseg, scsi_sg_count(pcmd))); - /* Map single segment */ - } else - pSRB->SGcount = 0; - - return error; -} - -/* Remove pci mapping */ -static void dc390_pci_unmap (struct dc390_srb* pSRB) -{ - struct scsi_cmnd *pcmd = pSRB->pcmd; - struct pci_dev *pdev = pSRB->pSRBDCB->pDCBACB->pdev; - DEBUG1(dc390_cmd_scp_t* cmdp = ((dc390_cmd_scp_t*)(&pcmd->SCp))); - - if (pSRB->SRBFlag) { - pci_unmap_sg(pdev, &pSRB->Segmentx, 1, DMA_FROM_DEVICE); - DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __func__, cmdp->saved_dma_handle)); - } else { - scsi_dma_unmap(pcmd); - DEBUG1(printk("%s(): Unmapped SG at %p with %d elements\n", - __func__, scsi_sglist(pcmd), scsi_sg_count(pcmd))); - } -} - -static void __inline__ -dc390_freetag (struct dc390_dcb* pDCB, struct dc390_srb* pSRB) -{ - if (pSRB->TagNumber != SCSI_NO_TAG) { - pDCB->TagMask &= ~(1 << pSRB->TagNumber); /* free tag mask */ - pSRB->TagNumber = SCSI_NO_TAG; - } -} - - -static int -dc390_StartSCSI( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB ) -{ - struct scsi_cmnd *scmd = pSRB->pcmd; - struct scsi_device *sdev = scmd->device; - u8 cmd, disc_allowed, try_sync_nego; - char tag[2]; - - pSRB->ScsiPhase = SCSI_NOP0; - - if (pACB->Connected) - { - // Should not happen normally - printk (KERN_WARNING "DC390: Can't select when connected! (%08x,%02x)\n", - pSRB->SRBState, pSRB->SRBFlag); - pSRB->SRBState = SRB_READY; - pACB->SelConn++; - return 1; - } - if (time_before (jiffies, pACB->last_reset)) - { - DEBUG0(printk ("DC390: We were just reset and don't accept commands yet!\n")); - return 1; - } - /* KG: Moved pci mapping here */ - dc390_pci_map(pSRB); - /* TODO: error handling */ - DC390_write8 (Scsi_Dest_ID, pDCB->TargetID); - DC390_write8 (Sync_Period, pDCB->SyncPeriod); - DC390_write8 (Sync_Offset, pDCB->SyncOffset); - DC390_write8 (CtrlReg1, pDCB->CtrlR1); - DC390_write8 (CtrlReg3, pDCB->CtrlR3); - DC390_write8 (CtrlReg4, pDCB->CtrlR4); - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); /* Flush FIFO */ - DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\ - scmd->cmnd[0], pDCB->SyncMode)); - - /* Don't disconnect on AUTO_REQSENSE, cause it might be an - * Contingent Allegiance Condition (6.6), where no tags should be used. - * All other have to be allowed to disconnect to prevent Incorrect - * Initiator Connection (6.8.2/6.5.2) */ - /* Changed KG, 99/06/06 */ - if (! (pSRB->SRBFlag & AUTO_REQSENSE)) - disc_allowed = pDCB->DevMode & EN_DISCONNECT_; - else - disc_allowed = 0; - - if ((pDCB->SyncMode & SYNC_ENABLE) && pDCB->TargetLUN == 0 && sdev->sdtr && - (((scmd->cmnd[0] == REQUEST_SENSE || (pSRB->SRBFlag & AUTO_REQSENSE)) && - !(pDCB->SyncMode & SYNC_NEGO_DONE)) || scmd->cmnd[0] == INQUIRY)) - try_sync_nego = 1; - else - try_sync_nego = 0; - - pSRB->MsgCnt = 0; - cmd = SEL_W_ATN; - DC390_write8 (ScsiFifo, IDENTIFY(disc_allowed, pDCB->TargetLUN)); - /* Change 99/05/31: Don't use tags when not disconnecting (BUSY) */ - if ((pDCB->SyncMode & EN_TAG_QUEUEING) && disc_allowed && scsi_populate_tag_msg(scmd, tag)) { - DC390_write8(ScsiFifo, tag[0]); - pDCB->TagMask |= 1 << tag[1]; - pSRB->TagNumber = tag[1]; - DC390_write8(ScsiFifo, tag[1]); - DEBUG1(printk(KERN_INFO "DC390: Select w/DisCn for SRB %p, block tag %02x\n", pSRB, tag[1])); - cmd = SEL_W_ATN3; - } else { - /* No TagQ */ -//no_tag: - DEBUG1(printk(KERN_INFO "DC390: Select w%s/DisCn for SRB %p, No TagQ\n", disc_allowed ? "" : "o", pSRB)); - } - - pSRB->SRBState = SRB_START_; - - if (try_sync_nego) - { - u8 Sync_Off = pDCB->SyncOffset; - DEBUG0(printk (KERN_INFO "DC390: NEW Sync Nego code triggered (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN)); - pSRB->MsgOutBuf[0] = EXTENDED_MESSAGE; - pSRB->MsgOutBuf[1] = 3; - pSRB->MsgOutBuf[2] = EXTENDED_SDTR; - pSRB->MsgOutBuf[3] = pDCB->NegoPeriod; - if (!(Sync_Off & 0x0f)) Sync_Off = SYNC_NEGO_OFFSET; - pSRB->MsgOutBuf[4] = Sync_Off; - pSRB->MsgCnt = 5; - //pSRB->SRBState = SRB_MSGOUT_; - pSRB->SRBState |= DO_SYNC_NEGO; - cmd = SEL_W_ATN_STOP; - } - - /* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */ - if (cmd != SEL_W_ATN_STOP) - { - if( pSRB->SRBFlag & AUTO_REQSENSE ) - { - DC390_write8 (ScsiFifo, REQUEST_SENSE); - DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5); - DC390_write8 (ScsiFifo, 0); - DC390_write8 (ScsiFifo, 0); - DC390_write8 (ScsiFifo, SCSI_SENSE_BUFFERSIZE); - DC390_write8 (ScsiFifo, 0); - DEBUG1(printk (KERN_DEBUG "DC390: AutoReqSense !\n")); - } - else /* write cmnd to bus */ - { - u8 *ptr; u8 i; - ptr = (u8 *)scmd->cmnd; - for (i = 0; i < scmd->cmd_len; i++) - DC390_write8 (ScsiFifo, *(ptr++)); - } - } - DEBUG0(if (pACB->pActiveDCB) \ - printk (KERN_WARNING "DC390: ActiveDCB != 0\n")); - DEBUG0(if (pDCB->pActiveSRB) \ - printk (KERN_WARNING "DC390: ActiveSRB != 0\n")); - //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); - if (DC390_read8 (Scsi_Status) & INTERRUPT) - { - dc390_freetag (pDCB, pSRB); - DEBUG0(printk ("DC390: Interrupt during Start SCSI (target %02i-%02i)\n", - scmd->device->id, (u8)scmd->device->lun)); - pSRB->SRBState = SRB_READY; - //DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - pACB->SelLost++; - return 1; - } - DC390_write8 (ScsiCmd, cmd); - pACB->pActiveDCB = pDCB; - pDCB->pActiveSRB = pSRB; - pACB->Connected = 1; - pSRB->ScsiPhase = SCSI_NOP1; - return 0; -} - - -static void __inline__ -dc390_InvalidCmd(struct dc390_acb* pACB) -{ - if (pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_ | SRB_MSGOUT)) - DC390_write8(ScsiCmd, CLEAR_FIFO_CMD); -} - - -static irqreturn_t __inline__ -DC390_Interrupt(void *dev_id) -{ - struct dc390_acb *pACB = dev_id; - struct dc390_dcb *pDCB; - struct dc390_srb *pSRB; - u8 sstatus=0; - u8 phase; - void (*stateV)( struct dc390_acb*, struct dc390_srb*, u8 *); - u8 istate, istatus; - - sstatus = DC390_read8 (Scsi_Status); - if( !(sstatus & INTERRUPT) ) - return IRQ_NONE; - - DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus)); - - //DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT); - //dstatus = DC390_read8 (DMA_Status); - //DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT); - - spin_lock_irq(pACB->pScsiHost->host_lock); - - istate = DC390_read8 (Intern_State); - istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */ - - DEBUG1(printk (KERN_INFO "Istatus(Res,Inv,Dis,Serv,Succ,ReS,SelA,Sel)=%02x,",istatus)); - dc390_laststatus &= ~0x00ffffff; - dc390_laststatus |= /* dstatus<<24 | */ sstatus<<16 | istate<<8 | istatus; - - if (sstatus & ILLEGAL_OP_ERR) - { - printk ("DC390: Illegal Operation detected (%08x)!\n", dc390_laststatus); - dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB); - } - - else if (istatus & INVALID_CMD) - { - printk ("DC390: Invalid Command detected (%08x)!\n", dc390_laststatus); - dc390_InvalidCmd( pACB ); - goto unlock; - } - - if (istatus & SCSI_RESET) - { - dc390_ScsiRstDetect( pACB ); - goto unlock; - } - - if (istatus & DISCONNECTED) - { - dc390_Disconnect( pACB ); - goto unlock; - } - - if (istatus & RESELECTED) - { - dc390_Reselect( pACB ); - goto unlock; - } - - else if (istatus & (SELECTED | SEL_ATTENTION)) - { - printk (KERN_ERR "DC390: Target mode not supported!\n"); - goto unlock; - } - - if (istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) ) - { - pDCB = pACB->pActiveDCB; - if (!pDCB) - { - printk (KERN_ERR "DC390: Suc. op/ Serv. req: pActiveDCB = 0!\n"); - goto unlock; - } - pSRB = pDCB->pActiveSRB; - if( pDCB->DCBFlag & ABORT_DEV_ ) - dc390_EnableMsgOut_Abort (pACB, pSRB); - - phase = pSRB->ScsiPhase; - DEBUG1(printk (KERN_INFO "DC390: [%i]%s(0) (%02x)\n", phase, dc390_p0_str[phase], sstatus)); - stateV = (void *) dc390_phase0[phase]; - ( *stateV )( pACB, pSRB, &sstatus ); - - pSRB->ScsiPhase = sstatus & 7; - phase = (u8) sstatus & 7; - DEBUG1(printk (KERN_INFO "DC390: [%i]%s(1) (%02x)\n", phase, dc390_p1_str[phase], sstatus)); - stateV = (void *) dc390_phase1[phase]; - ( *stateV )( pACB, pSRB, &sstatus ); - } - - unlock: - spin_unlock_irq(pACB->pScsiHost->host_lock); - return IRQ_HANDLED; -} - -static irqreturn_t do_DC390_Interrupt(int irq, void *dev_id) -{ - irqreturn_t ret; - DEBUG1(printk (KERN_INFO "DC390: Irq (%i) caught: ", irq)); - /* Locking is done in DC390_Interrupt */ - ret = DC390_Interrupt(dev_id); - DEBUG1(printk (".. IRQ returned\n")); - return ret; -} - -static void -dc390_DataOut_0(struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - u8 sstatus; - u32 ResidCnt; - u8 dstate = 0; - - sstatus = *psstatus; - - if( !(pSRB->SRBState & SRB_XFERPAD) ) - { - if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR) ) - pSRB->SRBStatus |= PARITY_ERROR; - - if( sstatus & COUNT_2_ZERO ) - { - unsigned long timeout = jiffies + HZ; - - /* Function called from the ISR with the host_lock held and interrupts disabled */ - if (pSRB->SGToBeXferLen) - while (time_before(jiffies, timeout) && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE)) { - spin_unlock_irq(pACB->pScsiHost->host_lock); - udelay(50); - spin_lock_irq(pACB->pScsiHost->host_lock); - } - if (!time_before(jiffies, timeout)) - printk (KERN_CRIT "DC390: Deadlock in DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n", - DC390_read32 (DMA_Wk_ByteCntr)); - dc390_laststatus &= ~0xff000000; - dc390_laststatus |= dstate << 24; - pSRB->TotalXferredLen += pSRB->SGToBeXferLen; - pSRB->SGIndex++; - if( pSRB->SGIndex < pSRB->SGcount ) - { - pSRB->pSegmentList++; - - dc390_start_segment(pSRB); - } - else - pSRB->SGToBeXferLen = 0; - } - else - { - ResidCnt = ((u32) DC390_read8 (Current_Fifo) & 0x1f) + - (((u32) DC390_read8 (CtcReg_High) << 16) | - ((u32) DC390_read8 (CtcReg_Mid) << 8) | - (u32) DC390_read8 (CtcReg_Low)); - - dc390_advance_segment(pSRB, ResidCnt); - } - } - if ((*psstatus & 7) != SCSI_DATA_OUT) - { - DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - } -} - -static void -dc390_DataIn_0(struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - u8 sstatus, residual, bval; - u32 ResidCnt, i; - unsigned long xferCnt; - - sstatus = *psstatus; - - if( !(pSRB->SRBState & SRB_XFERPAD) ) - { - if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR)) - pSRB->SRBStatus |= PARITY_ERROR; - - if( sstatus & COUNT_2_ZERO ) - { - int dstate = 0; - unsigned long timeout = jiffies + HZ; - - /* Function called from the ISR with the host_lock held and interrupts disabled */ - if (pSRB->SGToBeXferLen) - while (time_before(jiffies, timeout) && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE)) { - spin_unlock_irq(pACB->pScsiHost->host_lock); - udelay(50); - spin_lock_irq(pACB->pScsiHost->host_lock); - } - if (!time_before(jiffies, timeout)) { - printk (KERN_CRIT "DC390: Deadlock in DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n", - DC390_read32 (DMA_Wk_ByteCntr)); - printk (KERN_CRIT "DC390: DataIn_0: DMA State: %i\n", dstate); - } - dc390_laststatus &= ~0xff000000; - dc390_laststatus |= dstate << 24; - DEBUG1(ResidCnt = ((unsigned long) DC390_read8 (CtcReg_High) << 16) \ - + ((unsigned long) DC390_read8 (CtcReg_Mid) << 8) \ - + ((unsigned long) DC390_read8 (CtcReg_Low))); - DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%u,ToBeXfer=%lu),", ResidCnt, pSRB->SGToBeXferLen)); - - DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); - - pSRB->TotalXferredLen += pSRB->SGToBeXferLen; - pSRB->SGIndex++; - if( pSRB->SGIndex < pSRB->SGcount ) - { - pSRB->pSegmentList++; - - dc390_start_segment(pSRB); - } - else - pSRB->SGToBeXferLen = 0; - } - else /* phase changed */ - { - residual = 0; - bval = DC390_read8 (Current_Fifo); - while( bval & 0x1f ) - { - DEBUG1(printk (KERN_DEBUG "Check for residuals,")); - if( (bval & 0x1f) == 1 ) - { - for(i=0; i < 0x100; i++) - { - bval = DC390_read8 (Current_Fifo); - if( !(bval & 0x1f) ) - goto din_1; - else if( i == 0x0ff ) - { - residual = 1; /* ;1 residual byte */ - goto din_1; - } - } - } - else - bval = DC390_read8 (Current_Fifo); - } -din_1: - DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_BLAST_CMD); - for (i = 0xa000; i; i--) - { - bval = DC390_read8 (DMA_Status); - if (bval & BLAST_COMPLETE) - break; - } - /* It seems a DMA Blast abort isn't that bad ... */ - if (!i) printk (KERN_ERR "DC390: DMA Blast aborted unfinished!\n"); - //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); - dc390_laststatus &= ~0xff000000; - dc390_laststatus |= bval << 24; - - DEBUG1(printk (KERN_DEBUG "Blast: Read %i times DMA_Status %02x", 0xa000-i, bval)); - ResidCnt = (((u32) DC390_read8 (CtcReg_High) << 16) | - ((u32) DC390_read8 (CtcReg_Mid) << 8)) | - (u32) DC390_read8 (CtcReg_Low); - - xferCnt = dc390_advance_segment(pSRB, ResidCnt); - - if (residual) { - size_t count = 1; - size_t offset = pSRB->SGBusAddr - sg_dma_address(pSRB->pSegmentList); - unsigned long flags; - u8 *ptr; - - bval = DC390_read8 (ScsiFifo); /* get one residual byte */ - - local_irq_save(flags); - ptr = scsi_kmap_atomic_sg(pSRB->pSegmentList, pSRB->SGcount, &offset, &count); - if (likely(ptr)) { - *(ptr + offset) = bval; - scsi_kunmap_atomic_sg(ptr); - } - local_irq_restore(flags); - WARN_ON(!ptr); - - /* 1 more byte read */ - xferCnt += dc390_advance_segment(pSRB, pSRB->SGToBeXferLen - 1); - } - DEBUG1(printk (KERN_DEBUG "Xfered: %lu, Total: %lu, Remaining: %lu\n", xferCnt,\ - pSRB->TotalXferredLen, pSRB->SGToBeXferLen)); - } - } - if ((*psstatus & 7) != SCSI_DATA_IN) - { - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); - } -} - -static void -dc390_Command_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ -} - -static void -dc390_Status_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - - pSRB->TargetStatus = DC390_read8 (ScsiFifo); - //udelay (1); - pSRB->EndMessage = DC390_read8 (ScsiFifo); /* get message */ - - *psstatus = SCSI_NOP0; - pSRB->SRBState = SRB_COMPLETED; - DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); -} - -static void -dc390_MsgOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) ) - *psstatus = SCSI_NOP0; - //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); -} - - -static void __inline__ -dc390_reprog (struct dc390_acb* pACB, struct dc390_dcb* pDCB) -{ - DC390_write8 (Sync_Period, pDCB->SyncPeriod); - DC390_write8 (Sync_Offset, pDCB->SyncOffset); - DC390_write8 (CtrlReg3, pDCB->CtrlR3); - DC390_write8 (CtrlReg4, pDCB->CtrlR4); - dc390_SetXferRate (pACB, pDCB); -} - - -#ifdef DC390_DEBUG0 -static void -dc390_printMsg (u8 *MsgBuf, u8 len) -{ - int i; - printk (" %02x", MsgBuf[0]); - for (i = 1; i < len; i++) - printk (" %02x", MsgBuf[i]); - printk ("\n"); -} -#endif - -#define DC390_ENABLE_MSGOUT DC390_write8 (ScsiCmd, SET_ATN_CMD) - -/* reject_msg */ -static void __inline__ -dc390_MsgIn_reject (struct dc390_acb* pACB, struct dc390_srb* pSRB) -{ - pSRB->MsgOutBuf[0] = MESSAGE_REJECT; - pSRB->MsgCnt = 1; - DC390_ENABLE_MSGOUT; - DEBUG0 (printk (KERN_INFO "DC390: Reject message\n")); -} - -/* abort command */ -static void -dc390_EnableMsgOut_Abort ( struct dc390_acb* pACB, struct dc390_srb* pSRB ) -{ - pSRB->MsgOutBuf[0] = ABORT; - pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT; - pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_; -} - -static struct dc390_srb* -dc390_MsgIn_QTag (struct dc390_acb* pACB, struct dc390_dcb* pDCB, s8 tag) -{ - struct dc390_srb* pSRB = pDCB->pGoingSRB; - - if (pSRB) - { - struct scsi_cmnd *scmd = scsi_find_tag(pSRB->pcmd->device, tag); - pSRB = (struct dc390_srb *)scmd->host_scribble; - - if (pDCB->DCBFlag & ABORT_DEV_) - { - pSRB->SRBState = SRB_ABORT_SENT; - dc390_EnableMsgOut_Abort( pACB, pSRB ); - } - - if (!(pSRB->SRBState & SRB_DISCONNECT)) - goto mingx0; - - pDCB->pActiveSRB = pSRB; - pSRB->SRBState = SRB_DATA_XFER; - } - else - { - mingx0: - pSRB = pACB->pTmpSRB; - pSRB->SRBState = SRB_UNEXPECT_RESEL; - pDCB->pActiveSRB = pSRB; - pSRB->MsgOutBuf[0] = ABORT_TAG; - pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT; - } - return pSRB; -} - - -/* set async transfer mode */ -static void -dc390_MsgIn_set_async (struct dc390_acb* pACB, struct dc390_srb* pSRB) -{ - struct dc390_dcb* pDCB = pSRB->pSRBDCB; - if (!(pSRB->SRBState & DO_SYNC_NEGO)) - printk (KERN_INFO "DC390: Target %i initiates Non-Sync?\n", pDCB->TargetID); - pSRB->SRBState &= ~DO_SYNC_NEGO; - pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE); - pDCB->SyncPeriod = 0; - pDCB->SyncOffset = 0; - //pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */ - pDCB->CtrlR3 = FAST_CLK; /* fast clock / normal scsi */ - pDCB->CtrlR4 &= 0x3f; - pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */ - dc390_reprog (pACB, pDCB); -} - -/* set sync transfer mode */ -static void -dc390_MsgIn_set_sync (struct dc390_acb* pACB, struct dc390_srb* pSRB) -{ - u8 bval; - u16 wval, wval1; - struct dc390_dcb* pDCB = pSRB->pSRBDCB; - u8 oldsyncperiod = pDCB->SyncPeriod; - u8 oldsyncoffset = pDCB->SyncOffset; - - if (!(pSRB->SRBState & DO_SYNC_NEGO)) - { - printk (KERN_INFO "DC390: Target %i initiates Sync: %ins %i ... answer ...\n", - pDCB->TargetID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]); - - /* reject */ - //dc390_MsgIn_reject (pACB, pSRB); - //return dc390_MsgIn_set_async (pACB, pSRB); - - /* Reply with corrected SDTR Message */ - if (pSRB->MsgInBuf[4] > 15) - { - printk (KERN_INFO "DC390: Lower Sync Offset to 15\n"); - pSRB->MsgInBuf[4] = 15; - } - if (pSRB->MsgInBuf[3] < pDCB->NegoPeriod) - { - printk (KERN_INFO "DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2); - pSRB->MsgInBuf[3] = pDCB->NegoPeriod; - } - memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5); - pSRB->MsgCnt = 5; - DC390_ENABLE_MSGOUT; - } - - pSRB->SRBState &= ~DO_SYNC_NEGO; - pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE; - pDCB->SyncOffset &= 0x0f0; - pDCB->SyncOffset |= pSRB->MsgInBuf[4]; - pDCB->NegoPeriod = pSRB->MsgInBuf[3]; - - wval = (u16) pSRB->MsgInBuf[3]; - wval = wval << 2; wval -= 3; wval1 = wval / 25; /* compute speed */ - if( (wval1 * 25) != wval) wval1++; - bval = FAST_CLK+FAST_SCSI; /* fast clock / fast scsi */ - - pDCB->CtrlR4 &= 0x3f; /* Glitch eater: 12ns less than normal */ - if (pACB->glitch_cfg != NS_TO_GLITCH(0)) - pDCB->CtrlR4 |= NS_TO_GLITCH(((GLITCH_TO_NS(pACB->glitch_cfg)) - 1)); - else - pDCB->CtrlR4 |= NS_TO_GLITCH(0); - if (wval1 < 4) pDCB->CtrlR4 |= NS_TO_GLITCH(0); /* Ultra */ - - if (wval1 >= 8) - { - wval1--; /* Timing computation differs by 1 from FAST_SCSI */ - bval = FAST_CLK; /* fast clock / normal scsi */ - pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */ - } - - pDCB->CtrlR3 = bval; - pDCB->SyncPeriod = (u8)wval1; - - if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->TargetLUN == 0) - { - if (! (bval & FAST_SCSI)) wval1++; - printk (KERN_INFO "DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->TargetID, - 40/wval1, ((40%wval1)*10+wval1/2)/wval1, pDCB->SyncOffset & 0x0f); - } - - dc390_reprog (pACB, pDCB); -} - - -/* handle RESTORE_PTR */ -/* This doesn't look very healthy... to-be-fixed */ -static void -dc390_restore_ptr (struct dc390_acb* pACB, struct dc390_srb* pSRB) -{ - struct scsi_cmnd *pcmd = pSRB->pcmd; - struct scatterlist *psgl; - pSRB->TotalXferredLen = 0; - pSRB->SGIndex = 0; - if (scsi_sg_count(pcmd)) { - size_t saved; - pSRB->pSegmentList = scsi_sglist(pcmd); - psgl = pSRB->pSegmentList; - //dc390_pci_sync(pSRB); - - while (pSRB->TotalXferredLen + (unsigned long) sg_dma_len(psgl) < pSRB->Saved_Ptr) - { - pSRB->TotalXferredLen += (unsigned long) sg_dma_len(psgl); - pSRB->SGIndex++; - if( pSRB->SGIndex < pSRB->SGcount ) - { - pSRB->pSegmentList++; - - dc390_start_segment(pSRB); - } - else - pSRB->SGToBeXferLen = 0; - } - - saved = pSRB->Saved_Ptr - pSRB->TotalXferredLen; - pSRB->SGToBeXferLen -= saved; - pSRB->SGBusAddr += saved; - printk (KERN_INFO "DC390: Pointer restored. Segment %i, Total %li, Bus %08lx\n", - pSRB->SGIndex, pSRB->Saved_Ptr, pSRB->SGBusAddr); - - } else { - pSRB->SGcount = 0; - printk (KERN_INFO "DC390: RESTORE_PTR message for Transfer without Scatter-Gather ??\n"); - } - - pSRB->TotalXferredLen = pSRB->Saved_Ptr; -} - - -/* According to the docs, the AM53C974 reads the message and - * generates a Successful Operation IRQ before asserting ACK for - * the last byte (how does it know whether it's the last ?) */ -/* The old code handled it in another way, indicating, that on - * every message byte an IRQ is generated and every byte has to - * be manually ACKed. Hmmm ? (KG, 98/11/28) */ -/* The old implementation was correct. Sigh! */ - -/* Check if the message is complete */ -static u8 __inline__ -dc390_MsgIn_complete (u8 *msgbuf, u32 len) -{ - if (*msgbuf == EXTENDED_MESSAGE) - { - if (len < 2) return 0; - if (len < msgbuf[1] + 2) return 0; - } - else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages - if (len < 2) return 0; - return 1; -} - - - -/* read and eval received messages */ -static void -dc390_MsgIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - struct dc390_dcb* pDCB = pACB->pActiveDCB; - - /* Read the msg */ - - pSRB->MsgInBuf[pACB->MsgLen++] = DC390_read8 (ScsiFifo); - //pSRB->SRBState = 0; - - /* Msg complete ? */ - if (dc390_MsgIn_complete (pSRB->MsgInBuf, pACB->MsgLen)) - { - DEBUG0 (printk (KERN_INFO "DC390: MsgIn:"); dc390_printMsg (pSRB->MsgInBuf, pACB->MsgLen)); - /* Now eval the msg */ - switch (pSRB->MsgInBuf[0]) - { - case DISCONNECT: - pSRB->SRBState = SRB_DISCONNECT; break; - - case SIMPLE_QUEUE_TAG: - case HEAD_OF_QUEUE_TAG: - case ORDERED_QUEUE_TAG: - pSRB = dc390_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]); - break; - - case MESSAGE_REJECT: - DC390_write8 (ScsiCmd, RESET_ATN_CMD); - pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */ - if( pSRB->SRBState & DO_SYNC_NEGO) - dc390_MsgIn_set_async (pACB, pSRB); - break; - - case EXTENDED_MESSAGE: - /* reject every extended msg but SDTR */ - if (pSRB->MsgInBuf[1] != 3 || pSRB->MsgInBuf[2] != EXTENDED_SDTR) - dc390_MsgIn_reject (pACB, pSRB); - else - { - if (pSRB->MsgInBuf[3] == 0 || pSRB->MsgInBuf[4] == 0) - dc390_MsgIn_set_async (pACB, pSRB); - else - dc390_MsgIn_set_sync (pACB, pSRB); - } - - // nothing has to be done - case COMMAND_COMPLETE: break; - - // SAVE POINTER may be ignored as we have the struct dc390_srb* associated with the - // scsi command. Thanks, Gerard, for pointing it out. - case SAVE_POINTERS: - pSRB->Saved_Ptr = pSRB->TotalXferredLen; - break; - // The device might want to restart transfer with a RESTORE - case RESTORE_POINTERS: - DEBUG0(printk ("DC390: RESTORE POINTER message received ... try to handle\n")); - dc390_restore_ptr (pACB, pSRB); - break; - - // reject unknown messages - default: dc390_MsgIn_reject (pACB, pSRB); - } - - /* Clear counter and MsgIn state */ - pSRB->SRBState &= ~SRB_MSGIN; - pACB->MsgLen = 0; - } - - *psstatus = SCSI_NOP0; - DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); - //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); -} - - -static void -dc390_DataIO_Comm( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 ioDir) -{ - unsigned long lval; - struct dc390_dcb* pDCB = pACB->pActiveDCB; - - if (pSRB == pACB->pTmpSRB) - { - if (pDCB) - printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (%02i-%i)\n", pDCB->TargetID, pDCB->TargetLUN); - else - printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (DCB 0!)\n"); - - /* Try to recover - some broken disks react badly to tagged INQUIRY */ - if (pDCB && pACB->scan_devices && pDCB->GoingSRBCnt == 1) { - pSRB = pDCB->pGoingSRB; - pDCB->pActiveSRB = pSRB; - } else { - pSRB->pSRBDCB = pDCB; - dc390_EnableMsgOut_Abort(pACB, pSRB); - if (pDCB) - pDCB->DCBFlag |= ABORT_DEV; - return; - } - } - - if( pSRB->SGIndex < pSRB->SGcount ) - { - DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); - if( !pSRB->SGToBeXferLen ) - { - dc390_start_segment(pSRB); - - DEBUG1(printk (KERN_DEBUG " DC390: Next SG segment.")); - } - lval = pSRB->SGToBeXferLen; - DEBUG1(printk (KERN_DEBUG " DC390: Start transfer: %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr)); - DC390_write8 (CtcReg_Low, (u8) lval); - lval >>= 8; - DC390_write8 (CtcReg_Mid, (u8) lval); - lval >>= 8; - DC390_write8 (CtcReg_High, (u8) lval); - - DC390_write32 (DMA_XferCnt, pSRB->SGToBeXferLen); - DC390_write32 (DMA_XferAddr, pSRB->SGBusAddr); - - //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); - pSRB->SRBState = SRB_DATA_XFER; - - DC390_write8 (ScsiCmd, DMA_COMMAND+INFO_XFER_CMD); - - DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir); - //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT)); - //DEBUG1(printk (KERN_DEBUG "DC390: DMA_Status: %02x\n", DC390_read8 (DMA_Status))); - //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT)); - } - else /* xfer pad */ - { - if( pSRB->SGcount ) - { - pSRB->AdaptStatus = H_OVER_UNDER_RUN; - pSRB->SRBStatus |= OVER_RUN; - DEBUG0(printk (KERN_WARNING " DC390: Overrun -")); - } - DEBUG0(printk (KERN_WARNING " Clear transfer pad \n")); - DC390_write8 (CtcReg_Low, 0); - DC390_write8 (CtcReg_Mid, 0); - DC390_write8 (CtcReg_High, 0); - - pSRB->SRBState |= SRB_XFERPAD; - DC390_write8 (ScsiCmd, DMA_COMMAND+XFER_PAD_BYTE); -/* - DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); - DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir); -*/ - } -} - - -static void -dc390_DataOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - dc390_DataIO_Comm (pACB, pSRB, WRITE_DIRECTION); -} - -static void -dc390_DataInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - dc390_DataIO_Comm (pACB, pSRB, READ_DIRECTION); -} - -static void -dc390_CommandPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - struct dc390_dcb* pDCB; - u8 i, cnt; - u8 *ptr; - - DC390_write8 (ScsiCmd, RESET_ATN_CMD); - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - if( !(pSRB->SRBFlag & AUTO_REQSENSE) ) - { - cnt = (u8) pSRB->pcmd->cmd_len; - ptr = (u8 *) pSRB->pcmd->cmnd; - for(i=0; i < cnt; i++) - DC390_write8 (ScsiFifo, *(ptr++)); - } - else - { - DC390_write8 (ScsiFifo, REQUEST_SENSE); - pDCB = pACB->pActiveDCB; - DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5); - DC390_write8 (ScsiFifo, 0); - DC390_write8 (ScsiFifo, 0); - DC390_write8 (ScsiFifo, SCSI_SENSE_BUFFERSIZE); - DC390_write8 (ScsiFifo, 0); - DEBUG0(printk(KERN_DEBUG "DC390: AutoReqSense (CmndPhase)!\n")); - } - pSRB->SRBState = SRB_COMMAND; - DC390_write8 (ScsiCmd, INFO_XFER_CMD); -} - -static void -dc390_StatusPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - pSRB->SRBState = SRB_STATUS; - DC390_write8 (ScsiCmd, INITIATOR_CMD_CMPLTE); - //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); -} - -static void -dc390_MsgOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - u8 bval, i, cnt; - u8 *ptr; - struct dc390_dcb* pDCB; - - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - pDCB = pACB->pActiveDCB; - if( !(pSRB->SRBState & SRB_MSGOUT) ) - { - cnt = pSRB->MsgCnt; - if( cnt ) - { - ptr = (u8 *) pSRB->MsgOutBuf; - for(i=0; i < cnt; i++) - DC390_write8 (ScsiFifo, *(ptr++)); - pSRB->MsgCnt = 0; - if( (pDCB->DCBFlag & ABORT_DEV_) && - (pSRB->MsgOutBuf[0] == ABORT) ) - pSRB->SRBState = SRB_ABORT_SENT; - } - else - { - bval = ABORT; /* ??? MSG_NOP */ - if( (pSRB->pcmd->cmnd[0] == INQUIRY ) || - (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) || - (pSRB->SRBFlag & AUTO_REQSENSE) ) - { - if( pDCB->SyncMode & SYNC_ENABLE ) - goto mop1; - } - DC390_write8 (ScsiFifo, bval); - } - DC390_write8 (ScsiCmd, INFO_XFER_CMD); - } - else - { -mop1: - printk (KERN_ERR "DC390: OLD Sync Nego code triggered! (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN); - DC390_write8 (ScsiFifo, EXTENDED_MESSAGE); - DC390_write8 (ScsiFifo, 3); /* ;length of extended msg */ - DC390_write8 (ScsiFifo, EXTENDED_SDTR); /* ; sync nego */ - DC390_write8 (ScsiFifo, pDCB->NegoPeriod); - if (pDCB->SyncOffset & 0x0f) - DC390_write8 (ScsiFifo, pDCB->SyncOffset); - else - DC390_write8 (ScsiFifo, SYNC_NEGO_OFFSET); - pSRB->SRBState |= DO_SYNC_NEGO; - DC390_write8 (ScsiCmd, INFO_XFER_CMD); - } -} - -static void -dc390_MsgInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - if( !(pSRB->SRBState & SRB_MSGIN) ) - { - pSRB->SRBState &= ~SRB_DISCONNECT; - pSRB->SRBState |= SRB_MSGIN; - } - DC390_write8 (ScsiCmd, INFO_XFER_CMD); - //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); -} - -static void -dc390_Nop_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ -} - -static void -dc390_Nop_1( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus) -{ -} - - -static void -dc390_SetXferRate( struct dc390_acb* pACB, struct dc390_dcb* pDCB ) -{ - u8 bval, i, cnt; - struct dc390_dcb* ptr; - - if( !(pDCB->TargetLUN) ) - { - if( !pACB->scan_devices ) - { - ptr = pACB->pLinkDCB; - cnt = pACB->DCBCnt; - bval = pDCB->TargetID; - for(i=0; i<cnt; i++) - { - if( ptr->TargetID == bval ) - { - ptr->SyncPeriod = pDCB->SyncPeriod; - ptr->SyncOffset = pDCB->SyncOffset; - ptr->CtrlR3 = pDCB->CtrlR3; - ptr->CtrlR4 = pDCB->CtrlR4; - ptr->SyncMode = pDCB->SyncMode; - } - ptr = ptr->pNextDCB; - } - } - } - return; -} - - -static void -dc390_Disconnect( struct dc390_acb* pACB ) -{ - struct dc390_dcb *pDCB; - struct dc390_srb *pSRB, *psrb; - u8 i, cnt; - - DEBUG0(printk(KERN_INFO "DISC,")); - - if (!pACB->Connected) printk(KERN_ERR "DC390: Disconnect not-connected bus?\n"); - pACB->Connected = 0; - pDCB = pACB->pActiveDCB; - if (!pDCB) - { - DEBUG0(printk(KERN_ERR "ACB:%p->ActiveDCB:%p IOPort:%04x IRQ:%02x !\n",\ - pACB, pDCB, pACB->IOPortBase, pACB->IRQLevel)); - mdelay(400); - DC390_read8 (INT_Status); /* Reset Pending INT */ - DC390_write8 (ScsiCmd, EN_SEL_RESEL); - return; - } - DC390_write8 (ScsiCmd, EN_SEL_RESEL); - pSRB = pDCB->pActiveSRB; - pACB->pActiveDCB = NULL; - pSRB->ScsiPhase = SCSI_NOP0; - if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) - pSRB->SRBState = 0; - else if( pSRB->SRBState & SRB_ABORT_SENT ) - { - pDCB->TagMask = 0; - pDCB->DCBFlag = 0; - cnt = pDCB->GoingSRBCnt; - pDCB->GoingSRBCnt = 0; - pSRB = pDCB->pGoingSRB; - for( i=0; i < cnt; i++) - { - psrb = pSRB->pNextSRB; - dc390_Free_insert (pACB, pSRB); - pSRB = psrb; - } - pDCB->pGoingSRB = NULL; - } - else - { - if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) || - !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) ) - { /* Selection time out */ - pSRB->AdaptStatus = H_SEL_TIMEOUT; - pSRB->TargetStatus = 0; - goto disc1; - } - else if (!(pSRB->SRBState & SRB_DISCONNECT) && (pSRB->SRBState & SRB_COMPLETED)) - { -disc1: - dc390_freetag (pDCB, pSRB); - pDCB->pActiveSRB = NULL; - pSRB->SRBState = SRB_FREE; - dc390_SRBdone( pACB, pDCB, pSRB); - } - } - pACB->MsgLen = 0; -} - - -static void -dc390_Reselect( struct dc390_acb* pACB ) -{ - struct dc390_dcb* pDCB; - struct dc390_srb* pSRB; - u8 id, lun; - - DEBUG0(printk(KERN_INFO "RSEL,")); - pACB->Connected = 1; - pDCB = pACB->pActiveDCB; - if( pDCB ) - { /* Arbitration lost but Reselection won */ - DEBUG0(printk ("DC390: (ActiveDCB != 0: Arb. lost but resel. won)!\n")); - pSRB = pDCB->pActiveSRB; - if( !( pACB->scan_devices ) ) - { - struct scsi_cmnd *pcmd = pSRB->pcmd; - scsi_set_resid(pcmd, scsi_bufflen(pcmd)); - SET_RES_DID(pcmd->result, DID_SOFT_ERROR); - dc390_Going_remove(pDCB, pSRB); - dc390_Free_insert(pACB, pSRB); - pcmd->scsi_done (pcmd); - DEBUG0(printk(KERN_DEBUG"DC390: Return SRB %p to free\n", pSRB)); - } - } - /* Get ID */ - lun = DC390_read8 (ScsiFifo); - DEBUG0(printk ("Dev %02x,", lun)); - if (!(lun & (1 << pACB->pScsiHost->this_id))) - printk (KERN_ERR "DC390: Reselection must select host adapter: %02x!\n", lun); - else - lun ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */ - id = 0; while (lun >>= 1) id++; - /* Get LUN */ - lun = DC390_read8 (ScsiFifo); - if (!(lun & IDENTIFY_BASE)) printk (KERN_ERR "DC390: Resel: Expect identify message!\n"); - lun &= 7; - DEBUG0(printk ("(%02i-%i),", id, lun)); - pDCB = dc390_findDCB (pACB, id, lun); - if (!pDCB) - { - printk (KERN_ERR "DC390: Reselect from non existing device (%02i-%i)\n", - id, lun); - return; - } - pACB->pActiveDCB = pDCB; - /* TagQ: We expect a message soon, so never mind the exact SRB */ - if( pDCB->SyncMode & EN_TAG_QUEUEING ) - { - pSRB = pACB->pTmpSRB; - pDCB->pActiveSRB = pSRB; - } - else - { - pSRB = pDCB->pActiveSRB; - if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) ) - { - pSRB= pACB->pTmpSRB; - pSRB->SRBState = SRB_UNEXPECT_RESEL; - printk (KERN_ERR "DC390: Reselect without outstanding cmnd (%02i-%i)\n", - id, lun); - pDCB->pActiveSRB = pSRB; - dc390_EnableMsgOut_Abort ( pACB, pSRB ); - } - else - { - if( pDCB->DCBFlag & ABORT_DEV_ ) - { - pSRB->SRBState = SRB_ABORT_SENT; - printk (KERN_INFO "DC390: Reselect: Abort (%02i-%i)\n", - id, lun); - dc390_EnableMsgOut_Abort( pACB, pSRB ); - } - else - pSRB->SRBState = SRB_DATA_XFER; - } - } - - DEBUG1(printk (KERN_DEBUG "Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber)); - pSRB->ScsiPhase = SCSI_NOP0; - DC390_write8 (Scsi_Dest_ID, pDCB->TargetID); - DC390_write8 (Sync_Period, pDCB->SyncPeriod); - DC390_write8 (Sync_Offset, pDCB->SyncOffset); - DC390_write8 (CtrlReg1, pDCB->CtrlR1); - DC390_write8 (CtrlReg3, pDCB->CtrlR3); - DC390_write8 (CtrlReg4, pDCB->CtrlR4); /* ; Glitch eater */ - DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); /* ;to release the /ACK signal */ -} - -static int __inline__ -dc390_RequestSense(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB) -{ - struct scsi_cmnd *pcmd; - - pcmd = pSRB->pcmd; - - REMOVABLEDEBUG(printk(KERN_INFO "DC390: RequestSense(Cmd %02x, Id %02x, LUN %02x)\n",\ - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN)); - - pSRB->SRBFlag |= AUTO_REQSENSE; - pSRB->SavedTotXLen = pSRB->TotalXferredLen; - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = 0; /* CHECK_CONDITION<<1; */ - - /* We are called from SRBdone, original PCI mapping has been removed - * already, new one is set up from StartSCSI */ - pSRB->SGIndex = 0; - - pSRB->TotalXferredLen = 0; - pSRB->SGToBeXferLen = 0; - return dc390_StartSCSI(pACB, pDCB, pSRB); -} - - -static void -dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB ) -{ - u8 status; - struct scsi_cmnd *pcmd; - - pcmd = pSRB->pcmd; - /* KG: Moved pci_unmap here */ - dc390_pci_unmap(pSRB); - - status = pSRB->TargetStatus; - - DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p\n", status, pcmd->result, pSRB)); - if(pSRB->SRBFlag & AUTO_REQSENSE) - { /* Last command was a Request Sense */ - pSRB->SRBFlag &= ~AUTO_REQSENSE; - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = SAM_STAT_CHECK_CONDITION; - - //pcmd->result = MK_RES(DRIVER_SENSE,DID_OK,0,status); - if (status == SAM_STAT_CHECK_CONDITION) - pcmd->result = MK_RES_LNX(0, DID_BAD_TARGET, 0, /*CHECK_CONDITION*/0); - else /* Retry */ - { - if( pSRB->pcmd->cmnd[0] == TEST_UNIT_READY /* || pSRB->pcmd->cmnd[0] == START_STOP */) - { - /* Don't retry on TEST_UNIT_READY */ - pcmd->result = MK_RES_LNX(DRIVER_SENSE, DID_OK, 0, SAM_STAT_CHECK_CONDITION); - REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->pcmd->cmnd[0],\ - (u32) pcmd->result, (u32) pSRB->TotalXferredLen)); - } else { - SET_RES_DRV(pcmd->result, DRIVER_SENSE); - //pSRB->ScsiCmdLen = (u8) (pSRB->Segment1[0] >> 8); - DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, (u8)pcmd->device->lun)); - pSRB->TotalXferredLen = 0; - SET_RES_DID(pcmd->result, DID_SOFT_ERROR); - } - } - goto cmd_done; - } - if( status ) - { - if (status == SAM_STAT_CHECK_CONDITION) - { - if (dc390_RequestSense(pACB, pDCB, pSRB)) { - SET_RES_DID(pcmd->result, DID_ERROR); - goto cmd_done; - } - return; - } - else if (status == SAM_STAT_TASK_SET_FULL) - { - scsi_track_queue_full(pcmd->device, pDCB->GoingSRBCnt - 1); - DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, (u8)pcmd->device->lun)); - pSRB->TotalXferredLen = 0; - SET_RES_DID(pcmd->result, DID_SOFT_ERROR); - } - else if (status == SAM_STAT_BUSY && - (pcmd->cmnd[0] == TEST_UNIT_READY || pcmd->cmnd[0] == INQUIRY) && - pACB->scan_devices) - { - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = status; - pcmd->result = MK_RES(0,0,pSRB->EndMessage,/*status*/0); - } - else - { /* Another error */ - pSRB->TotalXferredLen = 0; - SET_RES_DID(pcmd->result, DID_SOFT_ERROR); - goto cmd_done; - } - } - else - { /* Target status == 0 */ - status = pSRB->AdaptStatus; - if (status == H_OVER_UNDER_RUN) - { - pSRB->TargetStatus = 0; - SET_RES_DID(pcmd->result,DID_OK); - SET_RES_MSG(pcmd->result,pSRB->EndMessage); - } - else if (status == H_SEL_TIMEOUT) - { - pcmd->result = MK_RES(0, DID_NO_CONNECT, 0, 0); - /* Devices are removed below ... */ - } - else if( pSRB->SRBStatus & PARITY_ERROR) - { - //pcmd->result = MK_RES(0,DID_PARITY,pSRB->EndMessage,0); - SET_RES_DID(pcmd->result,DID_PARITY); - SET_RES_MSG(pcmd->result,pSRB->EndMessage); - } - else /* No error */ - { - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = 0; - SET_RES_DID(pcmd->result,DID_OK); - } - } - -cmd_done: - scsi_set_resid(pcmd, scsi_bufflen(pcmd) - pSRB->TotalXferredLen); - - dc390_Going_remove (pDCB, pSRB); - /* Add to free list */ - dc390_Free_insert (pACB, pSRB); - - DEBUG0(printk (KERN_DEBUG "DC390: SRBdone: done\n")); - pcmd->scsi_done (pcmd); - - return; -} - - -/* Remove all SRBs from Going list and inform midlevel */ -static void -dc390_DoingSRB_Done(struct dc390_acb* pACB, struct scsi_cmnd *cmd) -{ - struct dc390_dcb *pDCB, *pdcb; - struct dc390_srb *psrb, *psrb2; - int i; - struct scsi_cmnd *pcmd; - - pDCB = pACB->pLinkDCB; - pdcb = pDCB; - if (! pdcb) return; - do - { - psrb = pdcb->pGoingSRB; - for (i = 0; i < pdcb->GoingSRBCnt; i++) - { - psrb2 = psrb->pNextSRB; - pcmd = psrb->pcmd; - dc390_Free_insert (pACB, psrb); - psrb = psrb2; - } - pdcb->GoingSRBCnt = 0; - pdcb->pGoingSRB = NULL; - pdcb->TagMask = 0; - pdcb = pdcb->pNextDCB; - } while( pdcb != pDCB ); -} - - -static void -dc390_ResetSCSIBus( struct dc390_acb* pACB ) -{ - //DC390_write8 (ScsiCmd, RST_DEVICE_CMD); - //udelay (250); - //DC390_write8 (ScsiCmd, NOP_CMD); - - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); - DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD); - pACB->Connected = 0; - - return; -} - -static void -dc390_ScsiRstDetect( struct dc390_acb* pACB ) -{ - printk ("DC390: Rst_Detect: laststat = %08x\n", dc390_laststatus); - //DEBUG0(printk(KERN_INFO "RST_DETECT,")); - - DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); - /* Unlock before ? */ - /* delay half a second */ - udelay (1000); - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - pACB->last_reset = jiffies + 5*HZ/2 - + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]; - pACB->Connected = 0; - - if( pACB->ACBFlag & RESET_DEV ) - pACB->ACBFlag |= RESET_DONE; - else - { /* Reset was issued by sb else */ - pACB->ACBFlag |= RESET_DETECT; - - dc390_ResetDevParam( pACB ); - dc390_DoingSRB_Done( pACB, NULL); - //dc390_RecoverSRB( pACB ); - pACB->pActiveDCB = NULL; - pACB->ACBFlag = 0; - } - return; -} - -static int DC390_queuecommand_lck(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) -{ - struct scsi_device *sdev = cmd->device; - struct dc390_acb *acb = (struct dc390_acb *)sdev->host->hostdata; - struct dc390_dcb *dcb = sdev->hostdata; - struct dc390_srb *srb; - - if (sdev->queue_depth <= dcb->GoingSRBCnt) - goto device_busy; - if (acb->pActiveDCB) - goto host_busy; - if (acb->ACBFlag & (RESET_DETECT|RESET_DONE|RESET_DEV)) - goto host_busy; - - srb = acb->pFreeSRB; - if (unlikely(srb == NULL)) - goto host_busy; - - cmd->scsi_done = done; - cmd->result = 0; - acb->Cmds++; - - acb->pFreeSRB = srb->pNextSRB; - srb->pNextSRB = NULL; - - srb->pSRBDCB = dcb; - srb->pcmd = cmd; - cmd->host_scribble = (char *)srb; - - srb->SGIndex = 0; - srb->AdaptStatus = 0; - srb->TargetStatus = 0; - srb->MsgCnt = 0; - - srb->SRBStatus = 0; - srb->SRBFlag = 0; - srb->SRBState = 0; - srb->TotalXferredLen = 0; - srb->SGBusAddr = 0; - srb->SGToBeXferLen = 0; - srb->ScsiPhase = 0; - srb->EndMessage = 0; - srb->TagNumber = SCSI_NO_TAG; - - if (dc390_StartSCSI(acb, dcb, srb)) { - dc390_Free_insert(acb, srb); - goto host_busy; - } - - dc390_Going_append(dcb, srb); - - return 0; - - host_busy: - return SCSI_MLQUEUE_HOST_BUSY; - - device_busy: - return SCSI_MLQUEUE_DEVICE_BUSY; -} - -static DEF_SCSI_QCMD(DC390_queuecommand) - -static void dc390_dumpinfo (struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB) -{ - struct pci_dev *pdev; - u16 pstat; - - if (!pDCB) pDCB = pACB->pActiveDCB; - if (!pSRB && pDCB) pSRB = pDCB->pActiveSRB; - - if (pSRB) - { - printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08x, Phase %02x\n", - pSRB->TotalXferredLen, pSRB->SGToBeXferLen, pSRB->SRBState, - pSRB->ScsiPhase); - printk ("DC390: AdpaterStatus: %02x, SRB Status %02x\n", pSRB->AdaptStatus, pSRB->SRBStatus); - } - printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08x\n", dc390_laststatus); - printk ("DC390: Register dump: SCSI block:\n"); - printk ("DC390: XferCnt Cmd Stat IntS IRQS FFIS Ctl1 Ctl2 Ctl3 Ctl4\n"); - printk ("DC390: %06x %02x %02x %02x", - DC390_read8(CtcReg_Low) + (DC390_read8(CtcReg_Mid) << 8) + (DC390_read8(CtcReg_High) << 16), - DC390_read8(ScsiCmd), DC390_read8(Scsi_Status), DC390_read8(Intern_State)); - printk (" %02x %02x %02x %02x %02x %02x\n", - DC390_read8(INT_Status), DC390_read8(Current_Fifo), DC390_read8(CtrlReg1), - DC390_read8(CtrlReg2), DC390_read8(CtrlReg3), DC390_read8(CtrlReg4)); - DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT); - if (DC390_read8(Current_Fifo) & 0x1f) - { - printk ("DC390: FIFO:"); - while (DC390_read8(Current_Fifo) & 0x1f) printk (" %02x", DC390_read8(ScsiFifo)); - printk ("\n"); - } - printk ("DC390: Register dump: DMA engine:\n"); - printk ("DC390: Cmd STrCnt SBusA WrkBC WrkAC Stat SBusCtrl\n"); - printk ("DC390: %02x %08x %08x %08x %08x %02x %08x\n", - DC390_read8(DMA_Cmd), DC390_read32(DMA_XferCnt), DC390_read32(DMA_XferAddr), - DC390_read32(DMA_Wk_ByteCntr), DC390_read32(DMA_Wk_AddrCntr), - DC390_read8(DMA_Status), DC390_read32(DMA_ScsiBusCtrl)); - DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT); - - pdev = pACB->pdev; - pci_read_config_word(pdev, PCI_STATUS, &pstat); - printk ("DC390: Register dump: PCI Status: %04x\n", pstat); - printk ("DC390: In case of driver trouble read Documentation/scsi/tmscsim.txt\n"); -} - - -static int DC390_abort(struct scsi_cmnd *cmd) -{ - struct dc390_acb *pACB = (struct dc390_acb*) cmd->device->host->hostdata; - struct dc390_dcb *pDCB = (struct dc390_dcb*) cmd->device->hostdata; - - scmd_printk(KERN_WARNING, cmd, "DC390: Abort command\n"); - - /* abort() is too stupid for already sent commands at the moment. - * If it's called we are in trouble anyway, so let's dump some info - * into the syslog at least. (KG, 98/08/20,99/06/20) */ - dc390_dumpinfo(pACB, pDCB, NULL); - - pDCB->DCBFlag |= ABORT_DEV_; - printk(KERN_INFO "DC390: Aborted.\n"); - - return FAILED; -} - - -static void dc390_ResetDevParam( struct dc390_acb* pACB ) -{ - struct dc390_dcb *pDCB, *pdcb; - - pDCB = pACB->pLinkDCB; - if (! pDCB) return; - pdcb = pDCB; - do - { - pDCB->SyncMode &= ~SYNC_NEGO_DONE; - pDCB->SyncPeriod = 0; - pDCB->SyncOffset = 0; - pDCB->TagMask = 0; - pDCB->CtrlR3 = FAST_CLK; - pDCB->CtrlR4 &= NEGATE_REQACKDATA | CTRL4_RESERVED | NEGATE_REQACK; - pDCB->CtrlR4 |= pACB->glitch_cfg; - pDCB = pDCB->pNextDCB; - } - while( pdcb != pDCB ); - pACB->ACBFlag &= ~(RESET_DEV | RESET_DONE | RESET_DETECT); - -} - -static int DC390_bus_reset (struct scsi_cmnd *cmd) -{ - struct dc390_acb* pACB = (struct dc390_acb*) cmd->device->host->hostdata; - u8 bval; - - spin_lock_irq(cmd->device->host->host_lock); - - bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST; - DC390_write8(CtrlReg1, bval); /* disable IRQ on bus reset */ - - pACB->ACBFlag |= RESET_DEV; - dc390_ResetSCSIBus(pACB); - - dc390_ResetDevParam(pACB); - mdelay(1); - pACB->last_reset = jiffies + 3*HZ/2 - + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]; - - DC390_write8(ScsiCmd, CLEAR_FIFO_CMD); - DC390_read8(INT_Status); /* Reset Pending INT */ - - dc390_DoingSRB_Done(pACB, cmd); - - pACB->pActiveDCB = NULL; - pACB->ACBFlag = 0; - - bval = DC390_read8(CtrlReg1) & ~DIS_INT_ON_SCSI_RST; - DC390_write8(CtrlReg1, bval); /* re-enable interrupt */ - - spin_unlock_irq(cmd->device->host->host_lock); - - return SUCCESS; -} - -/** - * dc390_slave_alloc - Called by the scsi mid layer to tell us about a new - * scsi device that we need to deal with. - * - * @scsi_device: The new scsi device that we need to handle. - */ -static int dc390_slave_alloc(struct scsi_device *scsi_device) -{ - struct dc390_acb *pACB = (struct dc390_acb*) scsi_device->host->hostdata; - struct dc390_dcb *pDCB, *pDCB2 = NULL; - uint id = scsi_device->id; - uint lun = scsi_device->lun; - - pDCB = kzalloc(sizeof(struct dc390_dcb), GFP_KERNEL); - if (!pDCB) - return -ENOMEM; - - if (!pACB->DCBCnt++) { - pACB->pLinkDCB = pDCB; - pACB->pDCBRunRobin = pDCB; - } else { - pACB->pLastDCB->pNextDCB = pDCB; - } - - pDCB->pNextDCB = pACB->pLinkDCB; - pACB->pLastDCB = pDCB; - - pDCB->pDCBACB = pACB; - pDCB->TargetID = id; - pDCB->TargetLUN = lun; - - /* - * Some values are for all LUNs: Copy them - * In a clean way: We would have an own structure for a SCSI-ID - */ - if (lun && (pDCB2 = dc390_findDCB(pACB, id, 0))) { - pDCB->DevMode = pDCB2->DevMode; - pDCB->SyncMode = pDCB2->SyncMode & SYNC_NEGO_DONE; - pDCB->SyncPeriod = pDCB2->SyncPeriod; - pDCB->SyncOffset = pDCB2->SyncOffset; - pDCB->NegoPeriod = pDCB2->NegoPeriod; - - pDCB->CtrlR3 = pDCB2->CtrlR3; - pDCB->CtrlR4 = pDCB2->CtrlR4; - } else { - u8 index = pACB->AdapterIndex; - PEEprom prom = (PEEprom) &dc390_eepromBuf[index][id << 2]; - - pDCB->DevMode = prom->EE_MODE1; - pDCB->NegoPeriod = - (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2; - pDCB->CtrlR3 = FAST_CLK; - pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED; - if (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) - pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK; - } - - if (pDCB->DevMode & SYNC_NEGO_) - pDCB->SyncMode |= SYNC_ENABLE; - else { - pDCB->SyncMode = 0; - pDCB->SyncOffset &= ~0x0f; - } - - pDCB->CtrlR1 = pACB->pScsiHost->this_id; - if (pDCB->DevMode & PARITY_CHK_) - pDCB->CtrlR1 |= PARITY_ERR_REPO; - - pACB->scan_devices = 1; - scsi_device->hostdata = pDCB; - return 0; -} - -/** - * dc390_slave_destroy - Called by the scsi mid layer to tell us about a - * device that is going away. - * - * @scsi_device: The scsi device that we need to remove. - */ -static void dc390_slave_destroy(struct scsi_device *scsi_device) -{ - struct dc390_acb* pACB = (struct dc390_acb*) scsi_device->host->hostdata; - struct dc390_dcb* pDCB = (struct dc390_dcb*) scsi_device->hostdata; - struct dc390_dcb* pPrevDCB = pACB->pLinkDCB; - - pACB->scan_devices = 0; - - BUG_ON(pDCB->GoingSRBCnt > 1); - - if (pDCB == pACB->pLinkDCB) { - if (pACB->pLastDCB == pDCB) { - pDCB->pNextDCB = NULL; - pACB->pLastDCB = NULL; - } - pACB->pLinkDCB = pDCB->pNextDCB; - } else { - while (pPrevDCB->pNextDCB != pDCB) - pPrevDCB = pPrevDCB->pNextDCB; - pPrevDCB->pNextDCB = pDCB->pNextDCB; - if (pDCB == pACB->pLastDCB) - pACB->pLastDCB = pPrevDCB; - } - - if (pDCB == pACB->pActiveDCB) - pACB->pActiveDCB = NULL; - if (pDCB == pACB->pLinkDCB) - pACB->pLinkDCB = pDCB->pNextDCB; - if (pDCB == pACB->pDCBRunRobin) - pACB->pDCBRunRobin = pDCB->pNextDCB; - kfree(pDCB); - - pACB->DCBCnt--; -} - -static int dc390_slave_configure(struct scsi_device *sdev) -{ - struct dc390_acb *acb = (struct dc390_acb *)sdev->host->hostdata; - struct dc390_dcb *dcb = (struct dc390_dcb *)sdev->hostdata; - - acb->scan_devices = 0; - if (sdev->tagged_supported && (dcb->DevMode & TAG_QUEUEING_)) { - dcb->SyncMode |= EN_TAG_QUEUEING; - scsi_activate_tcq(sdev, acb->TagMaxNum); - } - - return 0; -} - -static struct scsi_host_template driver_template = { - .module = THIS_MODULE, - .proc_name = "tmscsim", - .name = DC390_BANNER " V" DC390_VERSION, - .slave_alloc = dc390_slave_alloc, - .slave_configure = dc390_slave_configure, - .slave_destroy = dc390_slave_destroy, - .queuecommand = DC390_queuecommand, - .eh_abort_handler = DC390_abort, - .eh_bus_reset_handler = DC390_bus_reset, - .can_queue = 1, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = ENABLE_CLUSTERING, - .max_sectors = 0x4000, /* 8MiB = 16 * 1024 * 512 */ -}; - -/*********************************************************************** - * Functions for access to DC390 EEPROM - * and some to emulate it - * - **********************************************************************/ - -static void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd) -{ - u8 carryFlag = 1, j = 0x80, bval; - int i; - - for (i = 0; i < 9; i++) { - if (carryFlag) { - pci_write_config_byte(pdev, 0x80, 0x40); - bval = 0xc0; - } else - bval = 0x80; - - udelay(160); - pci_write_config_byte(pdev, 0x80, bval); - udelay(160); - pci_write_config_byte(pdev, 0x80, 0); - udelay(160); - - carryFlag = (cmd & j) ? 1 : 0; - j >>= 1; - } -} - -static u16 dc390_eeprom_get_data(struct pci_dev *pdev) -{ - int i; - u16 wval = 0; - u8 bval; - - for (i = 0; i < 16; i++) { - wval <<= 1; - - pci_write_config_byte(pdev, 0x80, 0x80); - udelay(160); - pci_write_config_byte(pdev, 0x80, 0x40); - udelay(160); - pci_read_config_byte(pdev, 0x00, &bval); - - if (bval == 0x22) - wval |= 1; - } - - return wval; -} - -static void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr) -{ - u8 cmd = EEPROM_READ, i; - - for (i = 0; i < 0x40; i++) { - pci_write_config_byte(pdev, 0xc0, 0); - udelay(160); - - dc390_eeprom_prepare_read(pdev, cmd++); - *ptr++ = dc390_eeprom_get_data(pdev); - - pci_write_config_byte(pdev, 0x80, 0); - pci_write_config_byte(pdev, 0x80, 0); - udelay(160); - } -} - -/* Override EEprom values with explicitly set values */ -static void dc390_eeprom_override(u8 index) -{ - u8 *ptr = (u8 *) dc390_eepromBuf[index], id; - - /* Adapter Settings */ - if (tmscsim[0] != -2) - ptr[EE_ADAPT_SCSI_ID] = (u8)tmscsim[0]; /* Adapter ID */ - if (tmscsim[3] != -2) - ptr[EE_MODE2] = (u8)tmscsim[3]; - if (tmscsim[5] != -2) - ptr[EE_DELAY] = tmscsim[5]; /* Reset delay */ - if (tmscsim[4] != -2) - ptr[EE_TAG_CMD_NUM] = (u8)tmscsim[4]; /* Tagged Cmds */ - - /* Device Settings */ - for (id = 0; id < MAX_SCSI_ID; id++) { - if (tmscsim[2] != -2) - ptr[id << 2] = (u8)tmscsim[2]; /* EE_MODE1 */ - if (tmscsim[1] != -2) - ptr[(id << 2) + 1] = (u8)tmscsim[1]; /* EE_Speed */ - } -} - -static int tmscsim_def[] = { - 7, - 0 /* 10MHz */, - PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_ | SYNC_NEGO_ | TAG_QUEUEING_, - MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION | LUN_CHECK, - 3 /* 16 Tags per LUN */, - 1 /* s delay after Reset */, -}; - -/* Copy defaults over set values where missing */ -static void dc390_fill_with_defaults (void) -{ - int i; - - for (i = 0; i < 6; i++) { - if (tmscsim[i] < 0 || tmscsim[i] > 255) - tmscsim[i] = tmscsim_def[i]; - } - - /* Sanity checks */ - if (tmscsim[0] > 7) - tmscsim[0] = 7; - if (tmscsim[1] > 7) - tmscsim[1] = 4; - if (tmscsim[4] > 5) - tmscsim[4] = 4; - if (tmscsim[5] > 180) - tmscsim[5] = 180; -} - -static void dc390_check_eeprom(struct pci_dev *pdev, u8 index) -{ - u8 interpd[] = {1, 3, 5, 10, 16, 30, 60, 120}; - u8 EEbuf[128]; - u16 *ptr = (u16 *)EEbuf, wval = 0; - int i; - - dc390_read_eeprom(pdev, ptr); - memcpy(dc390_eepromBuf[index], EEbuf, EE_ADAPT_SCSI_ID); - memcpy(&dc390_eepromBuf[index][EE_ADAPT_SCSI_ID], - &EEbuf[REAL_EE_ADAPT_SCSI_ID], EE_LEN - EE_ADAPT_SCSI_ID); - - dc390_eepromBuf[index][EE_DELAY] = interpd[dc390_eepromBuf[index][EE_DELAY]]; - - for (i = 0; i < 0x40; i++, ptr++) - wval += *ptr; - - /* no Tekram EEprom found */ - if (wval != 0x1234) { - int speed; - - printk(KERN_INFO "DC390_init: No EEPROM found! Trying default settings ...\n"); - - /* - * XXX(hch): bogus, because we might have tekram and - * non-tekram hbas in a single machine. - */ - dc390_fill_with_defaults(); - - speed = dc390_clock_speed[tmscsim[1]]; - printk(KERN_INFO "DC390: Used defaults: AdaptID=%i, SpeedIdx=%i (%i.%i MHz), " - "DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i), DelayReset=%is\n", - tmscsim[0], tmscsim[1], speed / 10, speed % 10, - (u8)tmscsim[2], (u8)tmscsim[3], tmscsim[4], 2 << (tmscsim[4]), tmscsim[5]); - } -} - -static void dc390_init_hw(struct dc390_acb *pACB, u8 index) -{ - struct Scsi_Host *shost = pACB->pScsiHost; - u8 dstate; - - /* Disable SCSI bus reset interrupt */ - DC390_write8(CtrlReg1, DIS_INT_ON_SCSI_RST | shost->this_id); - - if (pACB->Gmode2 & RST_SCSI_BUS) { - dc390_ResetSCSIBus(pACB); - udelay(1000); - pACB->last_reset = jiffies + HZ/2 + - HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]; - } - - pACB->ACBFlag = 0; - - /* Reset Pending INT */ - DC390_read8(INT_Status); - - /* 250ms selection timeout */ - DC390_write8(Scsi_TimeOut, SEL_TIMEOUT); - - /* Conversion factor = 0 , 40MHz clock */ - DC390_write8(Clk_Factor, CLK_FREQ_40MHZ); - - /* NOP cmd - clear command register */ - DC390_write8(ScsiCmd, NOP_CMD); - - /* Enable Feature and SCSI-2 */ - DC390_write8(CtrlReg2, EN_FEATURE+EN_SCSI2_CMD); - - /* Fast clock */ - DC390_write8(CtrlReg3, FAST_CLK); - - /* Negation */ - DC390_write8(CtrlReg4, pACB->glitch_cfg | /* glitch eater */ - (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) ? - NEGATE_REQACKDATA : 0); - - /* Clear Transfer Count High: ID */ - DC390_write8(CtcReg_High, 0); - DC390_write8(DMA_Cmd, DMA_IDLE_CMD); - DC390_write8(ScsiCmd, CLEAR_FIFO_CMD); - DC390_write32(DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT); - - dstate = DC390_read8(DMA_Status); - DC390_write8(DMA_Status, dstate); -} - -static int dc390_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct dc390_acb *pACB; - struct Scsi_Host *shost; - unsigned long io_port; - int error = -ENODEV, i; - - if (pci_enable_device(pdev)) - goto out; - - pci_set_master(pdev); - - error = -ENOMEM; - if (disable_clustering) - driver_template.use_clustering = DISABLE_CLUSTERING; - shost = scsi_host_alloc(&driver_template, sizeof(struct dc390_acb)); - if (!shost) - goto out_disable_device; - - pACB = (struct dc390_acb *)shost->hostdata; - memset(pACB, 0, sizeof(struct dc390_acb)); - - dc390_check_eeprom(pdev, dc390_adapterCnt); - dc390_eeprom_override(dc390_adapterCnt); - - io_port = pci_resource_start(pdev, 0); - - shost->this_id = dc390_eepromBuf[dc390_adapterCnt][EE_ADAPT_SCSI_ID]; - shost->io_port = io_port; - shost->n_io_port = 0x80; - shost->irq = pdev->irq; - shost->base = io_port; - shost->unique_id = io_port; - - pACB->last_reset = jiffies; - pACB->pScsiHost = shost; - pACB->IOPortBase = (u16) io_port; - pACB->IRQLevel = pdev->irq; - - shost->max_id = 8; - - if (shost->max_id - 1 == - dc390_eepromBuf[dc390_adapterCnt][EE_ADAPT_SCSI_ID]) - shost->max_id--; - - if (dc390_eepromBuf[dc390_adapterCnt][EE_MODE2] & LUN_CHECK) - shost->max_lun = 8; - else - shost->max_lun = 1; - - pACB->pFreeSRB = pACB->SRB_array; - pACB->SRBCount = MAX_SRB_CNT; - pACB->AdapterIndex = dc390_adapterCnt; - pACB->TagMaxNum = - 2 << dc390_eepromBuf[dc390_adapterCnt][EE_TAG_CMD_NUM]; - pACB->Gmode2 = dc390_eepromBuf[dc390_adapterCnt][EE_MODE2]; - - for (i = 0; i < pACB->SRBCount-1; i++) - pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1]; - pACB->SRB_array[pACB->SRBCount-1].pNextSRB = NULL; - pACB->pTmpSRB = &pACB->TmpSRB; - - pACB->sel_timeout = SEL_TIMEOUT; - pACB->glitch_cfg = EATER_25NS; - pACB->pdev = pdev; - - if (!request_region(io_port, shost->n_io_port, "tmscsim")) { - printk(KERN_ERR "DC390: register IO ports error!\n"); - goto out_host_put; - } - - /* Reset Pending INT */ - DC390_read8_(INT_Status, io_port); - - if (request_irq(pdev->irq, do_DC390_Interrupt, IRQF_SHARED, - "tmscsim", pACB)) { - printk(KERN_ERR "DC390: register IRQ error!\n"); - goto out_release_region; - } - - dc390_init_hw(pACB, dc390_adapterCnt); - - dc390_adapterCnt++; - - pci_set_drvdata(pdev, shost); - - error = scsi_add_host(shost, &pdev->dev); - if (error) - goto out_free_irq; - scsi_scan_host(shost); - return 0; - - out_free_irq: - free_irq(pdev->irq, pACB); - out_release_region: - release_region(io_port, shost->n_io_port); - out_host_put: - scsi_host_put(shost); - out_disable_device: - pci_disable_device(pdev); - out: - return error; -} - -/** - * dc390_remove_one - Called to remove a single instance of the adapter. - * - * @dev: The PCI device to remove. - */ -static void dc390_remove_one(struct pci_dev *dev) -{ - struct Scsi_Host *scsi_host = pci_get_drvdata(dev); - unsigned long iflags; - struct dc390_acb* pACB = (struct dc390_acb*) scsi_host->hostdata; - u8 bval; - - scsi_remove_host(scsi_host); - - spin_lock_irqsave(scsi_host->host_lock, iflags); - pACB->ACBFlag = RESET_DEV; - bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST; - DC390_write8 (CtrlReg1, bval); /* disable interrupt */ - if (pACB->Gmode2 & RST_SCSI_BUS) - dc390_ResetSCSIBus(pACB); - spin_unlock_irqrestore(scsi_host->host_lock, iflags); - - free_irq(scsi_host->irq, pACB); - release_region(scsi_host->io_port, scsi_host->n_io_port); - - pci_disable_device(dev); - scsi_host_put(scsi_host); -} - -static struct pci_device_id tmscsim_pci_tbl[] = { - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { } -}; -MODULE_DEVICE_TABLE(pci, tmscsim_pci_tbl); - -static struct pci_driver dc390_driver = { - .name = "tmscsim", - .id_table = tmscsim_pci_tbl, - .probe = dc390_probe_one, - .remove = dc390_remove_one, -}; - -static int __init dc390_module_init(void) -{ - if (!disable_clustering) { - printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n"); - printk(KERN_INFO " with \"disable_clustering=1\" and report to maintainers\n"); - } - - if (tmscsim[0] == -1 || tmscsim[0] > 15) { - tmscsim[0] = 7; - tmscsim[1] = 4; - tmscsim[2] = PARITY_CHK_ | TAG_QUEUEING_; - tmscsim[3] = MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION; - tmscsim[4] = 2; - tmscsim[5] = 10; - printk (KERN_INFO "DC390: Using safe settings.\n"); - } - - return pci_register_driver(&dc390_driver); -} - -static void __exit dc390_module_exit(void) -{ - pci_unregister_driver(&dc390_driver); -} - -module_init(dc390_module_init); -module_exit(dc390_module_exit); - -#ifndef MODULE -static int __init dc390_setup (char *str) -{ - int ints[8],i, im; - - get_options(str, ARRAY_SIZE(ints), ints); - im = ints[0]; - - if (im > 6) { - printk (KERN_NOTICE "DC390: ignore extra params!\n"); - im = 6; - } - - for (i = 0; i < im; i++) - tmscsim[i] = ints[i+1]; - /* dc390_checkparams (); */ - return 1; -} - -__setup("tmscsim=", dc390_setup); -#endif diff --git a/drivers/scsi/tmscsim.h b/drivers/scsi/tmscsim.h deleted file mode 100644 index 3d1bb4ad1826..000000000000 --- a/drivers/scsi/tmscsim.h +++ /dev/null @@ -1,551 +0,0 @@ -/*********************************************************************** -;* File Name : TMSCSIM.H * -;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter * -;* Device Driver * -;***********************************************************************/ -/* $Id: tmscsim.h,v 2.15.2.3 2000/11/17 20:52:27 garloff Exp $ */ - -#ifndef _TMSCSIM_H -#define _TMSCSIM_H - -#include <linux/types.h> - -#define SCSI_IRQ_NONE 255 - -#define MAX_ADAPTER_NUM 4 -#define MAX_SG_LIST_BUF 16 /* Not used */ -#define MAX_SCSI_ID 8 -#define MAX_SRB_CNT 50 /* Max number of started commands */ - -#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ - -/* -;----------------------------------------------------------------------- -; SCSI Request Block -;----------------------------------------------------------------------- -*/ -struct dc390_srb -{ -//u8 CmdBlock[12]; - -struct dc390_srb *pNextSRB; -struct dc390_dcb *pSRBDCB; -struct scsi_cmnd *pcmd; -struct scatterlist *pSegmentList; - -struct scatterlist Segmentx; /* make a one entry of S/G list table */ - -unsigned long SGBusAddr; /*;a segment starting address as seen by AM53C974A - in CPU endianness. We're only getting 32-bit bus - addresses by default */ -unsigned long SGToBeXferLen; /*; to be xfer length */ -unsigned long TotalXferredLen; -unsigned long SavedTotXLen; -unsigned long Saved_Ptr; -u32 SRBState; - -u8 SRBStatus; -u8 SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */ - /*; b4-settimeout,b5-Residual valid */ -u8 AdaptStatus; -u8 TargetStatus; - -u8 ScsiPhase; -s8 TagNumber; -u8 SGIndex; -u8 SGcount; - -u8 MsgCnt; -u8 EndMessage; - -u8 MsgInBuf[6]; -u8 MsgOutBuf[6]; - -//u8 IORBFlag; /*;81h-Reset, 2-retry */ -}; - - -/* -;----------------------------------------------------------------------- -; Device Control Block -;----------------------------------------------------------------------- -*/ -struct dc390_dcb -{ -struct dc390_dcb *pNextDCB; -struct dc390_acb *pDCBACB; - -/* Queued SRBs */ -struct dc390_srb *pGoingSRB; -struct dc390_srb *pGoingLast; -struct dc390_srb *pActiveSRB; -u8 GoingSRBCnt; - -u32 TagMask; - -u8 TargetID; /*; SCSI Target ID (SCSI Only) */ -u8 TargetLUN; /*; SCSI Log. Unit (SCSI Only) */ -u8 DevMode; -u8 DCBFlag; - -u8 CtrlR1; -u8 CtrlR3; -u8 CtrlR4; - -u8 SyncMode; /*; 0:async mode */ -u8 NegoPeriod; /*;for nego. */ -u8 SyncPeriod; /*;for reg. */ -u8 SyncOffset; /*;for reg. and nego.(low nibble) */ -}; - - -/* -;----------------------------------------------------------------------- -; Adapter Control Block -;----------------------------------------------------------------------- -*/ -struct dc390_acb -{ -struct Scsi_Host *pScsiHost; -u16 IOPortBase; -u8 IRQLevel; -u8 status; - -u8 SRBCount; -u8 AdapterIndex; /*; nth Adapter this driver */ -u8 DCBCnt; - -u8 TagMaxNum; -u8 ACBFlag; -u8 Gmode2; -u8 scan_devices; - -struct dc390_dcb *pLinkDCB; -struct dc390_dcb *pLastDCB; -struct dc390_dcb *pDCBRunRobin; - -struct dc390_dcb *pActiveDCB; -struct dc390_srb *pFreeSRB; -struct dc390_srb *pTmpSRB; - -u8 msgin123[4]; -u8 Connected; -u8 pad; - -#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(CONFIG_SMP) || DEBUG_SPINLOCKS > 0) -spinlock_t lock; -#endif -u8 sel_timeout; -u8 glitch_cfg; - -u8 MsgLen; -u8 Ignore_IRQ; /* Not used */ - -struct pci_dev *pdev; - -unsigned long last_reset; -unsigned long Cmds; -u32 SelLost; -u32 SelConn; -u32 CmdInQ; -u32 CmdOutOfSRB; - -struct dc390_srb TmpSRB; -struct dc390_srb SRB_array[MAX_SRB_CNT]; /* 50 SRBs */ -}; - - -/*;-----------------------------------------------------------------------*/ - - -#define BIT31 0x80000000 -#define BIT30 0x40000000 -#define BIT29 0x20000000 -#define BIT28 0x10000000 -#define BIT27 0x08000000 -#define BIT26 0x04000000 -#define BIT25 0x02000000 -#define BIT24 0x01000000 -#define BIT23 0x00800000 -#define BIT22 0x00400000 -#define BIT21 0x00200000 -#define BIT20 0x00100000 -#define BIT19 0x00080000 -#define BIT18 0x00040000 -#define BIT17 0x00020000 -#define BIT16 0x00010000 -#define BIT15 0x00008000 -#define BIT14 0x00004000 -#define BIT13 0x00002000 -#define BIT12 0x00001000 -#define BIT11 0x00000800 -#define BIT10 0x00000400 -#define BIT9 0x00000200 -#define BIT8 0x00000100 -#define BIT7 0x00000080 -#define BIT6 0x00000040 -#define BIT5 0x00000020 -#define BIT4 0x00000010 -#define BIT3 0x00000008 -#define BIT2 0x00000004 -#define BIT1 0x00000002 -#define BIT0 0x00000001 - -/*;---UnitCtrlFlag */ -#define UNIT_ALLOCATED BIT0 -#define UNIT_INFO_CHANGED BIT1 -#define FORMATING_MEDIA BIT2 -#define UNIT_RETRY BIT3 - -/*;---UnitFlags */ -#define DASD_SUPPORT BIT0 -#define SCSI_SUPPORT BIT1 -#define ASPI_SUPPORT BIT2 - -/*;----SRBState machine definition */ -#define SRB_FREE 0 -#define SRB_WAIT BIT0 -#define SRB_READY BIT1 -#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/ -#define SRB_MSGIN BIT3 -#define SRB_MSGIN_MULTI BIT4 -#define SRB_COMMAND BIT5 -#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/ -#define SRB_DISCONNECT BIT7 -#define SRB_DATA_XFER BIT8 -#define SRB_XFERPAD BIT9 -#define SRB_STATUS BIT10 -#define SRB_COMPLETED BIT11 -#define SRB_ABORT_SENT BIT12 -#define DO_SYNC_NEGO BIT13 -#define SRB_UNEXPECT_RESEL BIT14 - -/*;---SRBstatus */ -#define SRB_OK BIT0 -#define ABORTION BIT1 -#define OVER_RUN BIT2 -#define UNDER_RUN BIT3 -#define PARITY_ERROR BIT4 -#define SRB_ERROR BIT5 - -/*;---ACBFlag */ -#define RESET_DEV BIT0 -#define RESET_DETECT BIT1 -#define RESET_DONE BIT2 - -/*;---DCBFlag */ -#define ABORT_DEV_ BIT0 - -/*;---SRBFlag */ -#define DATAOUT BIT7 -#define DATAIN BIT6 -#define RESIDUAL_VALID BIT5 -#define ENABLE_TIMER BIT4 -#define RESET_DEV0 BIT2 -#define ABORT_DEV BIT1 -#define AUTO_REQSENSE BIT0 - -/*;---Adapter status */ -#define H_STATUS_GOOD 0 -#define H_SEL_TIMEOUT 0x11 -#define H_OVER_UNDER_RUN 0x12 -#define H_UNEXP_BUS_FREE 0x13 -#define H_TARGET_PHASE_F 0x14 -#define H_INVALID_CCB_OP 0x16 -#define H_LINK_CCB_BAD 0x17 -#define H_BAD_TARGET_DIR 0x18 -#define H_DUPLICATE_CCB 0x19 -#define H_BAD_CCB_OR_SG 0x1A -#define H_ABORT 0x0FF - -/* cmd->result */ -#define RES_TARGET 0x000000FF /* Target State */ -#define RES_TARGET_LNX STATUS_MASK /* Only official ... */ -#define RES_ENDMSG 0x0000FF00 /* End Message */ -#define RES_DID 0x00FF0000 /* DID_ codes */ -#define RES_DRV 0xFF000000 /* DRIVER_ codes */ - -#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)) -#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)) - -#define SET_RES_TARGET(who, tgt) do { who &= ~RES_TARGET; who |= (int)(tgt); } while (0) -#define SET_RES_TARGET_LNX(who, tgt) do { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; } while (0) -#define SET_RES_MSG(who, msg) do { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; } while (0) -#define SET_RES_DID(who, did) do { who &= ~RES_DID; who |= (int)(did) << 16; } while (0) -#define SET_RES_DRV(who, drv) do { who &= ~RES_DRV; who |= (int)(drv) << 24; } while (0) - -/*;---Sync_Mode */ -#define SYNC_DISABLE 0 -#define SYNC_ENABLE BIT0 -#define SYNC_NEGO_DONE BIT1 -#define WIDE_ENABLE BIT2 /* Not used ;-) */ -#define WIDE_NEGO_DONE BIT3 /* Not used ;-) */ -#define EN_TAG_QUEUEING BIT4 -#define EN_ATN_STOP BIT5 - -#define SYNC_NEGO_OFFSET 15 - -/*;---SCSI bus phase*/ -#define SCSI_DATA_OUT 0 -#define SCSI_DATA_IN 1 -#define SCSI_COMMAND 2 -#define SCSI_STATUS_ 3 -#define SCSI_NOP0 4 -#define SCSI_NOP1 5 -#define SCSI_MSG_OUT 6 -#define SCSI_MSG_IN 7 - -/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */ /* One is missing ! */ -#define ABORT_TAG 0x0d - -/* - * SISC query queue - */ -typedef struct { - dma_addr_t saved_dma_handle; -} dc390_cmd_scp_t; - -/* -;========================================================== -; EEPROM byte offset -;========================================================== -*/ -typedef struct _EEprom -{ -u8 EE_MODE1; -u8 EE_SPEED; -u8 xx1; -u8 xx2; -} EEprom, *PEEprom; - -#define REAL_EE_ADAPT_SCSI_ID 64 -#define REAL_EE_MODE2 65 -#define REAL_EE_DELAY 66 -#define REAL_EE_TAG_CMD_NUM 67 - -#define EE_ADAPT_SCSI_ID 32 -#define EE_MODE2 33 -#define EE_DELAY 34 -#define EE_TAG_CMD_NUM 35 - -#define EE_LEN 40 - -/*; EE_MODE1 bits definition*/ -#define PARITY_CHK_ BIT0 -#define SYNC_NEGO_ BIT1 -#define EN_DISCONNECT_ BIT2 -#define SEND_START_ BIT3 -#define TAG_QUEUEING_ BIT4 - -/*; EE_MODE2 bits definition*/ -#define MORE2_DRV BIT0 -#define GREATER_1G BIT1 -#define RST_SCSI_BUS BIT2 -#define ACTIVE_NEGATION BIT3 -#define NO_SEEK BIT4 -#define LUN_CHECK BIT5 - -#define ENABLE_CE 1 -#define DISABLE_CE 0 -#define EEPROM_READ 0x80 - -/* -;========================================================== -; AMD 53C974 Registers bit Definition -;========================================================== -*/ -/* -;==================== -; SCSI Register -;==================== -*/ - -/*; Command Reg.(+0CH) (rw) */ -#define DMA_COMMAND BIT7 -#define NOP_CMD 0 -#define CLEAR_FIFO_CMD 1 -#define RST_DEVICE_CMD 2 -#define RST_SCSI_BUS_CMD 3 - -#define INFO_XFER_CMD 0x10 -#define INITIATOR_CMD_CMPLTE 0x11 -#define MSG_ACCEPTED_CMD 0x12 -#define XFER_PAD_BYTE 0x18 -#define SET_ATN_CMD 0x1A -#define RESET_ATN_CMD 0x1B - -#define SEL_WO_ATN 0x41 /* currently not used */ -#define SEL_W_ATN 0x42 -#define SEL_W_ATN_STOP 0x43 -#define SEL_W_ATN3 0x46 -#define EN_SEL_RESEL 0x44 -#define DIS_SEL_RESEL 0x45 /* currently not used */ -#define RESEL 0x40 /* " */ -#define RESEL_ATN3 0x47 /* " */ - -#define DATA_XFER_CMD INFO_XFER_CMD - - -/*; SCSI Status Reg.(+10H) (r) */ -#define INTERRUPT BIT7 -#define ILLEGAL_OP_ERR BIT6 -#define PARITY_ERR BIT5 -#define COUNT_2_ZERO BIT4 -#define GROUP_CODE_VALID BIT3 -#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0) -/* BIT2: MSG phase; BIT1: C/D physe; BIT0: I/O phase */ - -/*; Interrupt Status Reg.(+14H) (r) */ -#define SCSI_RESET BIT7 -#define INVALID_CMD BIT6 -#define DISCONNECTED BIT5 -#define SERVICE_REQUEST BIT4 -#define SUCCESSFUL_OP BIT3 -#define RESELECTED BIT2 -#define SEL_ATTENTION BIT1 -#define SELECTED BIT0 - -/*; Internal State Reg.(+18H) (r) */ -#define SYNC_OFFSET_FLAG BIT3 -#define INTRN_STATE_MASK (BIT2+BIT1+BIT0) -/* 0x04: Sel. successful (w/o stop), 0x01: Sel. successful (w/ stop) */ - -/*; Clock Factor Reg.(+24H) (w) */ -#define CLK_FREQ_40MHZ 0 -#define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0) -#define CLK_FREQ_30MHZ (BIT2+BIT1) -#define CLK_FREQ_25MHZ (BIT2+BIT0) -#define CLK_FREQ_20MHZ BIT2 -#define CLK_FREQ_15MHZ (BIT1+BIT0) -#define CLK_FREQ_10MHZ BIT1 - -/*; Control Reg. 1(+20H) (rw) */ -#define EXTENDED_TIMING BIT7 -#define DIS_INT_ON_SCSI_RST BIT6 -#define PARITY_ERR_REPO BIT4 -#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0) /* host adapter ID */ - -/*; Control Reg. 2(+2CH) (rw) */ -#define EN_FEATURE BIT6 -#define EN_SCSI2_CMD BIT3 - -/*; Control Reg. 3(+30H) (rw) */ -#define ID_MSG_CHECK BIT7 -#define EN_QTAG_MSG BIT6 -#define EN_GRP2_CMD BIT5 -#define FAST_SCSI BIT4 /* ;10MB/SEC */ -#define FAST_CLK BIT3 /* ;25 - 40 MHZ */ - -/*; Control Reg. 4(+34H) (rw) */ -#define EATER_12NS 0 -#define EATER_25NS BIT7 -#define EATER_35NS BIT6 -#define EATER_0NS (BIT7+BIT6) -#define REDUCED_POWER BIT5 -#define CTRL4_RESERVED BIT4 /* must be 1 acc. to AM53C974.c */ -#define NEGATE_REQACKDATA BIT2 -#define NEGATE_REQACK BIT3 - -#define GLITCH_TO_NS(x) (((~x>>6 & 2) >> 1) | ((x>>6 & 1) << 1 ^ (x>>6 & 2))) -#define NS_TO_GLITCH(y) (((~y<<7) | ~((y<<6) ^ ((y<<5 & 1<<6) | ~0x40))) & 0xc0) - -/* -;==================== -; DMA Register -;==================== -*/ -/*; DMA Command Reg.(+40H) (rw) */ -#define READ_DIRECTION BIT7 -#define WRITE_DIRECTION 0 -#define EN_DMA_INT BIT6 -#define EN_PAGE_INT BIT5 /* page transfer interrupt enable */ -#define MAP_TO_MDL BIT4 -#define DIAGNOSTIC BIT2 -#define DMA_IDLE_CMD 0 -#define DMA_BLAST_CMD BIT0 -#define DMA_ABORT_CMD BIT1 -#define DMA_START_CMD (BIT1+BIT0) - -/*; DMA Status Reg.(+54H) (r) */ -#define PCI_MS_ABORT BIT6 -#define BLAST_COMPLETE BIT5 -#define SCSI_INTERRUPT BIT4 -#define DMA_XFER_DONE BIT3 -#define DMA_XFER_ABORT BIT2 -#define DMA_XFER_ERROR BIT1 -#define POWER_DOWN BIT0 - -/*; DMA SCSI Bus and Ctrl.(+70H) */ -#define EN_INT_ON_PCI_ABORT BIT25 -#define WRT_ERASE_DMA_STAT BIT24 -#define PW_DOWN_CTRL BIT21 -#define SCSI_BUSY BIT20 -#define SCLK BIT19 -#define SCAM BIT18 -#define SCSI_LINES 0x0003ffff - -/* -;========================================================== -; SCSI Chip register address offset -;========================================================== -;Registers are rw unless declared otherwise -*/ -#define CtcReg_Low 0x00 /* r curr. transfer count */ -#define CtcReg_Mid 0x04 /* r */ -#define CtcReg_High 0x38 /* r */ -#define ScsiFifo 0x08 -#define ScsiCmd 0x0C -#define Scsi_Status 0x10 /* r */ -#define INT_Status 0x14 /* r */ -#define Sync_Period 0x18 /* w */ -#define Sync_Offset 0x1C /* w */ -#define Clk_Factor 0x24 /* w */ -#define CtrlReg1 0x20 -#define CtrlReg2 0x2C -#define CtrlReg3 0x30 -#define CtrlReg4 0x34 -#define DMA_Cmd 0x40 -#define DMA_XferCnt 0x44 /* rw starting transfer count (32 bit) */ -#define DMA_XferAddr 0x48 /* rw starting physical address (32 bit) */ -#define DMA_Wk_ByteCntr 0x4C /* r working byte counter */ -#define DMA_Wk_AddrCntr 0x50 /* r working address counter */ -#define DMA_Status 0x54 /* r */ -#define DMA_MDL_Addr 0x58 /* rw starting MDL address */ -#define DMA_Wk_MDL_Cntr 0x5C /* r working MDL counter */ -#define DMA_ScsiBusCtrl 0x70 /* rw SCSI Bus, PCI/DMA Ctrl */ - -#define StcReg_Low CtcReg_Low /* w start transfer count */ -#define StcReg_Mid CtcReg_Mid /* w */ -#define StcReg_High CtcReg_High /* w */ -#define Scsi_Dest_ID Scsi_Status /* w */ -#define Scsi_TimeOut INT_Status /* w */ -#define Intern_State Sync_Period /* r */ -#define Current_Fifo Sync_Offset /* r Curr. FIFO / int. state */ - - -#define DC390_read8(address) \ - (inb (pACB->IOPortBase + (address))) - -#define DC390_read8_(address, base) \ - (inb ((u16)(base) + (address))) - -#define DC390_read16(address) \ - (inw (pACB->IOPortBase + (address))) - -#define DC390_read32(address) \ - (inl (pACB->IOPortBase + (address))) - -#define DC390_write8(address,value) \ - outb ((value), pACB->IOPortBase + (address)) - -#define DC390_write8_(address,value,base) \ - outb ((value), (u16)(base) + (address)) - -#define DC390_write16(address,value) \ - outw ((value), pACB->IOPortBase + (address)) - -#define DC390_write32(address,value) \ - outl ((value), pACB->IOPortBase + (address)) - - -#endif /* _TMSCSIM_H */ diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c index d8dcf36aed11..14eb50b95a1e 100644 --- a/drivers/scsi/u14-34f.c +++ b/drivers/scsi/u14-34f.c @@ -696,25 +696,25 @@ static int u14_34f_slave_configure(struct scsi_device *dev) { if (TLDEV(dev->type) && dev->tagged_supported) if (tag_mode == TAG_SIMPLE) { - scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd); + scsi_change_queue_depth(dev, tqd); tag_suffix = ", simple tags"; } else if (tag_mode == TAG_ORDERED) { - scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd); + scsi_change_queue_depth(dev, tqd); tag_suffix = ", ordered tags"; } else { - scsi_adjust_queue_depth(dev, 0, tqd); + scsi_change_queue_depth(dev, tqd); tag_suffix = ", no tags"; } else if (TLDEV(dev->type) && linked_comm) { - scsi_adjust_queue_depth(dev, 0, tqd); + scsi_change_queue_depth(dev, tqd); tag_suffix = ", untagged"; } else { - scsi_adjust_queue_depth(dev, 0, utqd); + scsi_change_queue_depth(dev, utqd); tag_suffix = ""; } diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 8adf067ff019..1c3467b82566 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -102,7 +102,6 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq), GFP_KERNEL); if (!clkfreq) { - dev_err(dev, "%s: no memory\n", "freq-table-hz"); ret = -ENOMEM; goto out; } @@ -112,19 +111,19 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) if (ret && (ret != -EINVAL)) { dev_err(dev, "%s: error reading array %d\n", "freq-table-hz", ret); - goto free_clkfreq; + return ret; } for (i = 0; i < sz; i += 2) { ret = of_property_read_string_index(np, "clock-names", i/2, (const char **)&name); if (ret) - goto free_clkfreq; + goto out; clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); if (!clki) { ret = -ENOMEM; - goto free_clkfreq; + goto out; } clki->min_freq = clkfreq[i]; @@ -134,8 +133,6 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) clki->min_freq, clki->max_freq, clki->name); list_add_tail(&clki->list, &hba->clk_list_head); } -free_clkfreq: - kfree(clkfreq); out: return ret; } @@ -162,10 +159,8 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name, } vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); - if (!vreg) { - dev_err(dev, "No memory for %s regulator\n", name); - goto out; - } + if (!vreg) + return -ENOMEM; vreg->name = kstrdup(name, GFP_KERNEL); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 497c38a4a866..2e4614b9dddf 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -744,6 +744,8 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) if (!ufshcd_is_clkgating_allowed(hba)) return; device_remove_file(hba->dev, &hba->clk_gating.delay_attr); + cancel_work_sync(&hba->clk_gating.ungate_work); + cancel_delayed_work_sync(&hba->clk_gating.gate_work); } /* Must be called with host lock acquired */ @@ -2246,6 +2248,22 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) return ret; } + /** + * ufshcd_init_pwr_info - setting the POR (power on reset) + * values in hba power info + * @hba: per-adapter instance + */ +static void ufshcd_init_pwr_info(struct ufs_hba *hba) +{ + hba->pwr_info.gear_rx = UFS_PWM_G1; + hba->pwr_info.gear_tx = UFS_PWM_G1; + hba->pwr_info.lane_rx = 1; + hba->pwr_info.lane_tx = 1; + hba->pwr_info.pwr_rx = SLOWAUTO_MODE; + hba->pwr_info.pwr_tx = SLOWAUTO_MODE; + hba->pwr_info.hs_rate = 0; +} + /** * ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device * @hba: per-adapter instance @@ -2695,7 +2713,7 @@ static void ufshcd_set_queue_depth(struct scsi_device *sdev) dev_dbg(hba->dev, "%s: activate tcq with queue depth %d\n", __func__, lun_qdepth); - scsi_activate_tcq(sdev, lun_qdepth); + scsi_change_queue_depth(sdev, lun_qdepth); } /* @@ -2765,11 +2783,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) struct ufs_hba *hba; hba = shost_priv(sdev->host); - sdev->tagged_supported = 1; /* Mode sense(6) is not supported by UFS, so use Mode sense(10) */ sdev->use_10_for_ms = 1; - scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); /* allow SCSI layer to restart the device in case of errors */ sdev->allow_restart = 1; @@ -2789,34 +2805,16 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) * ufshcd_change_queue_depth - change queue depth * @sdev: pointer to SCSI device * @depth: required depth to set - * @reason: reason for changing the depth * - * Change queue depth according to the reason and make sure - * the max. limits are not crossed. + * Change queue depth and make sure the max. limits are not crossed. */ -static int ufshcd_change_queue_depth(struct scsi_device *sdev, - int depth, int reason) +static int ufshcd_change_queue_depth(struct scsi_device *sdev, int depth) { struct ufs_hba *hba = shost_priv(sdev->host); if (depth > hba->nutrs) depth = hba->nutrs; - - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - case SCSI_QDEPTH_RAMP_UP: - if (!sdev->tagged_supported) - depth = 1; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, depth); - break; - default: - return -EOPNOTSUPP; - } - - return depth; + return scsi_change_queue_depth(sdev, depth); } /** @@ -2842,10 +2840,14 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) struct ufs_hba *hba; hba = shost_priv(sdev->host); - scsi_deactivate_tcq(sdev, hba->nutrs); /* Drop the reference as it won't be needed anymore */ - if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) + if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) { + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); hba->sdev_ufs_device = NULL; + spin_unlock_irqrestore(hba->host->host_lock, flags); + } } /** @@ -4062,6 +4064,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) { int ret = 0; + struct scsi_device *sdev_rpmb; + struct scsi_device *sdev_boot; hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0, ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL); @@ -4070,26 +4074,27 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device = NULL; goto out; } + scsi_device_put(hba->sdev_ufs_device); - hba->sdev_boot = __scsi_add_device(hba->host, 0, 0, + sdev_boot = __scsi_add_device(hba->host, 0, 0, ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL); - if (IS_ERR(hba->sdev_boot)) { - ret = PTR_ERR(hba->sdev_boot); - hba->sdev_boot = NULL; + if (IS_ERR(sdev_boot)) { + ret = PTR_ERR(sdev_boot); goto remove_sdev_ufs_device; } + scsi_device_put(sdev_boot); - hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0, + sdev_rpmb = __scsi_add_device(hba->host, 0, 0, ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL); - if (IS_ERR(hba->sdev_rpmb)) { - ret = PTR_ERR(hba->sdev_rpmb); - hba->sdev_rpmb = NULL; + if (IS_ERR(sdev_rpmb)) { + ret = PTR_ERR(sdev_rpmb); goto remove_sdev_boot; } + scsi_device_put(sdev_rpmb); goto out; remove_sdev_boot: - scsi_remove_device(hba->sdev_boot); + scsi_remove_device(sdev_boot); remove_sdev_ufs_device: scsi_remove_device(hba->sdev_ufs_device); out: @@ -4097,30 +4102,6 @@ out: } /** - * ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by - * ufshcd_scsi_add_wlus() - * @hba: per-adapter instance - * - */ -static void ufshcd_scsi_remove_wlus(struct ufs_hba *hba) -{ - if (hba->sdev_ufs_device) { - scsi_remove_device(hba->sdev_ufs_device); - hba->sdev_ufs_device = NULL; - } - - if (hba->sdev_boot) { - scsi_remove_device(hba->sdev_boot); - hba->sdev_boot = NULL; - } - - if (hba->sdev_rpmb) { - scsi_remove_device(hba->sdev_rpmb); - hba->sdev_rpmb = NULL; - } -} - -/** * ufshcd_probe_hba - probe hba to detect device and initialize * @hba: per-adapter instance * @@ -4134,6 +4115,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ret) goto out; + ufshcd_init_pwr_info(hba); + /* UniPro link is active now */ ufshcd_set_link_active(hba); @@ -4235,6 +4218,8 @@ static struct scsi_host_template ufshcd_driver_template = { .cmd_per_lun = UFSHCD_CMD_PER_LUN, .can_queue = UFSHCD_CAN_QUEUE, .max_host_blocked = 1, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, @@ -4264,12 +4249,18 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { + if (!vreg) + return 0; + return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA); } static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { + if (!vreg) + return 0; + return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA); } @@ -4471,7 +4462,7 @@ out: if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled) clk_disable_unprepare(clki->clk); } - } else if (!ret && on) { + } else if (on) { spin_lock_irqsave(hba->host->host_lock, flags); hba->clk_gating.state = CLKS_ON; spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -4675,11 +4666,25 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, { unsigned char cmd[6] = { START_STOP }; struct scsi_sense_hdr sshdr; - struct scsi_device *sdp = hba->sdev_ufs_device; + struct scsi_device *sdp; + unsigned long flags; int ret; - if (!sdp || !scsi_device_online(sdp)) - return -ENODEV; + spin_lock_irqsave(hba->host->host_lock, flags); + sdp = hba->sdev_ufs_device; + if (sdp) { + ret = scsi_device_get(sdp); + if (!ret && !scsi_device_online(sdp)) { + ret = -ENODEV; + scsi_device_put(sdp); + } + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + + if (ret) + return ret; /* * If scsi commands fail, the scsi mid-layer schedules scsi error- @@ -4707,17 +4712,18 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, START_STOP_TIMEOUT, 0, NULL, REQ_PM); if (ret) { sdev_printk(KERN_WARNING, sdp, - "START_STOP failed for power mode: %d\n", pwr_mode); - scsi_show_result(ret); + "START_STOP failed for power mode: %d, result %x\n", + pwr_mode, ret); if (driver_byte(ret) & DRIVER_SENSE) { - scsi_show_sense_hdr(&sshdr); - scsi_show_extd_sense(sshdr.asc, sshdr.ascq); + scsi_show_sense_hdr(sdp, NULL, &sshdr); + scsi_show_extd_sense(sdp, NULL, sshdr.asc, sshdr.ascq); } } if (!ret) hba->curr_dev_pwr_mode = pwr_mode; out: + scsi_device_put(sdp); hba->host->eh_noresume = 0; return ret; } @@ -5087,7 +5093,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba) int ret = 0; if (!hba || !hba->is_powered) - goto out; + return 0; if (pm_runtime_suspended(hba->dev)) { if (hba->rpm_lvl == hba->spm_lvl) @@ -5231,7 +5237,6 @@ EXPORT_SYMBOL(ufshcd_shutdown); void ufshcd_remove(struct ufs_hba *hba) { scsi_remove_host(hba->host); - ufshcd_scsi_remove_wlus(hba); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); ufshcd_hba_stop(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 58ecdff5065c..4a574aa45855 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -392,8 +392,6 @@ struct ufs_hba { * "UFS device" W-LU. */ struct scsi_device *sdev_ufs_device; - struct scsi_device *sdev_rpmb; - struct scsi_device *sdev_boot; enum ufs_dev_pwr_mode curr_dev_pwr_mode; enum uic_link_state uic_link_state; diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index b83846fc7859..22e70126425b 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -561,6 +561,15 @@ static int virtscsi_queuecommand_single(struct Scsi_Host *sh, return virtscsi_queuecommand(vscsi, &vscsi->req_vqs[0], sc); } +static struct virtio_scsi_vq *virtscsi_pick_vq_mq(struct virtio_scsi *vscsi, + struct scsi_cmnd *sc) +{ + u32 tag = blk_mq_unique_tag(sc->request); + u16 hwq = blk_mq_unique_tag_to_hwq(tag); + + return &vscsi->req_vqs[hwq]; +} + static struct virtio_scsi_vq *virtscsi_pick_vq(struct virtio_scsi *vscsi, struct virtio_scsi_target_state *tgt) { @@ -604,7 +613,12 @@ static int virtscsi_queuecommand_multi(struct Scsi_Host *sh, struct virtio_scsi *vscsi = shost_priv(sh); struct virtio_scsi_target_state *tgt = scsi_target(sc->device)->hostdata; - struct virtio_scsi_vq *req_vq = virtscsi_pick_vq(vscsi, tgt); + struct virtio_scsi_vq *req_vq; + + if (shost_use_blk_mq(sh)) + req_vq = virtscsi_pick_vq_mq(vscsi, sc); + else + req_vq = virtscsi_pick_vq(vscsi, tgt); return virtscsi_queuecommand(vscsi, req_vq, sc); } @@ -668,30 +682,13 @@ static int virtscsi_device_reset(struct scsi_cmnd *sc) * virtscsi_change_queue_depth() - Change a virtscsi target's queue depth * @sdev: Virtscsi target whose queue depth to change * @qdepth: New queue depth - * @reason: Reason for the queue depth change. */ -static int virtscsi_change_queue_depth(struct scsi_device *sdev, - int qdepth, - int reason) +static int virtscsi_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; int max_depth = shost->cmd_per_lun; - switch (reason) { - case SCSI_QDEPTH_QFULL: /* Drop qdepth in response to BUSY state */ - scsi_track_queue_full(sdev, qdepth); - break; - case SCSI_QDEPTH_RAMP_UP: /* Raise qdepth after BUSY state resolved */ - case SCSI_QDEPTH_DEFAULT: /* Manual change via sysfs */ - scsi_adjust_queue_depth(sdev, - scsi_get_tag_type(sdev), - min(max_depth, qdepth)); - break; - default: - return -EOPNOTSUPP; - } - - return sdev->queue_depth; + return scsi_change_queue_depth(sdev, min(max_depth, qdepth)); } static int virtscsi_abort(struct scsi_cmnd *sc) @@ -758,6 +755,7 @@ static struct scsi_host_template virtscsi_host_template_single = { .use_clustering = ENABLE_CLUSTERING, .target_alloc = virtscsi_target_alloc, .target_destroy = virtscsi_target_destroy, + .track_queue_depth = 1, }; static struct scsi_host_template virtscsi_host_template_multi = { @@ -776,6 +774,7 @@ static struct scsi_host_template virtscsi_host_template_multi = { .use_clustering = ENABLE_CLUSTERING, .target_alloc = virtscsi_target_alloc, .target_destroy = virtscsi_target_destroy, + .track_queue_depth = 1, }; #define virtscsi_config_get(vdev, fld) \ @@ -983,6 +982,7 @@ static int virtscsi_probe(struct virtio_device *vdev) shost->max_id = num_targets; shost->max_channel = 0; shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE; + shost->nr_hw_queues = num_queues; if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) { host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c index 598f65efaaec..0f133c1817de 100644 --- a/drivers/scsi/vmw_pvscsi.c +++ b/drivers/scsi/vmw_pvscsi.c @@ -504,33 +504,11 @@ static void pvscsi_setup_all_rings(const struct pvscsi_adapter *adapter) } } -static int pvscsi_change_queue_depth(struct scsi_device *sdev, - int qdepth, - int reason) +static int pvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth) { - int max_depth; - struct Scsi_Host *shost = sdev->host; - - if (reason != SCSI_QDEPTH_DEFAULT) - /* - * We support only changing default. - */ - return -EOPNOTSUPP; - - max_depth = shost->can_queue; if (!sdev->tagged_supported) - max_depth = 1; - if (qdepth > max_depth) - qdepth = max_depth; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - - if (sdev->inquiry_len > 7) - sdev_printk(KERN_INFO, sdev, - "qdepth(%d), tagged(%d), simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n", - sdev->queue_depth, sdev->tagged_supported, - sdev->simple_tags, sdev->ordered_tags, - sdev->scsi_level, (sdev->inquiry[7] & 2) >> 1); - return sdev->queue_depth; + qdepth = 1; + return scsi_change_queue_depth(sdev, qdepth); } /* @@ -723,10 +701,6 @@ static int pvscsi_queue_ring(struct pvscsi_adapter *adapter, memcpy(e->cdb, cmd->cmnd, e->cdbLen); e->tag = SIMPLE_QUEUE_TAG; - if (sdev->tagged_supported && - (cmd->tag == HEAD_OF_QUEUE_TAG || - cmd->tag == ORDERED_QUEUE_TAG)) - e->tag = cmd->tag; if (cmd->sc_data_direction == DMA_FROM_DEVICE) e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST; diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index 32674236fec7..f94d73611ab4 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -1653,7 +1653,6 @@ static struct scsi_host_template driver_template = { .can_queue = WD7000_Q, .this_id = 7, .sg_tablesize = WD7000_SG, - .cmd_per_lun = 1, .unchecked_isa_dma = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/drivers/scsi/wd719x.c b/drivers/scsi/wd719x.c new file mode 100644 index 000000000000..7702664d7ed3 --- /dev/null +++ b/drivers/scsi/wd719x.c @@ -0,0 +1,996 @@ +/* + * Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards + * Copyright 2013 Ondrej Zary + * + * Original driver by + * Aaron Dewell <dewell@woods.net> + * Gaerti <Juergen.Gaertner@mbox.si.uni-hannover.de> + * + * HW documentation available in book: + * + * SPIDER Command Protocol + * by Chandru M. Sippy + * SCSI Storage Products (MCP) + * Western Digital Corporation + * 09-15-95 + * + * http://web.archive.org/web/20070717175254/http://sun1.rrzn.uni-hannover.de/gaertner.juergen/wd719x/Linux/Docu/Spider/ + */ + +/* + * Driver workflow: + * 1. SCSI command is transformed to SCB (Spider Control Block) by the + * queuecommand function. + * 2. The address of the SCB is stored in a list to be able to access it, if + * something goes wrong. + * 3. The address of the SCB is written to the Controller, which loads the SCB + * via BM-DMA and processes it. + * 4. After it has finished, it generates an interrupt, and sets registers. + * + * flaws: + * - abort/reset functions + * + * ToDo: + * - tagged queueing + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <linux/eeprom_93cx6.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include "wd719x.h" + +/* low-level register access */ +static inline u8 wd719x_readb(struct wd719x *wd, u8 reg) +{ + return ioread8(wd->base + reg); +} + +static inline u32 wd719x_readl(struct wd719x *wd, u8 reg) +{ + return ioread32(wd->base + reg); +} + +static inline void wd719x_writeb(struct wd719x *wd, u8 reg, u8 val) +{ + iowrite8(val, wd->base + reg); +} + +static inline void wd719x_writew(struct wd719x *wd, u8 reg, u16 val) +{ + iowrite16(val, wd->base + reg); +} + +static inline void wd719x_writel(struct wd719x *wd, u8 reg, u32 val) +{ + iowrite32(val, wd->base + reg); +} + +/* wait until the command register is ready */ +static inline int wd719x_wait_ready(struct wd719x *wd) +{ + int i = 0; + + do { + if (wd719x_readb(wd, WD719X_AMR_COMMAND) == WD719X_CMD_READY) + return 0; + udelay(1); + } while (i++ < WD719X_WAIT_FOR_CMD_READY); + + dev_err(&wd->pdev->dev, "command register is not ready: 0x%02x\n", + wd719x_readb(wd, WD719X_AMR_COMMAND)); + + return -ETIMEDOUT; +} + +/* poll interrupt status register until command finishes */ +static inline int wd719x_wait_done(struct wd719x *wd, int timeout) +{ + u8 status; + + while (timeout > 0) { + status = wd719x_readb(wd, WD719X_AMR_INT_STATUS); + if (status) + break; + timeout--; + udelay(1); + } + + if (timeout <= 0) { + dev_err(&wd->pdev->dev, "direct command timed out\n"); + return -ETIMEDOUT; + } + + if (status != WD719X_INT_NOERRORS) { + dev_err(&wd->pdev->dev, "direct command failed, status 0x%02x, SUE 0x%02x\n", + status, wd719x_readb(wd, WD719X_AMR_SCB_ERROR)); + return -EIO; + } + + return 0; +} + +static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun, + u8 tag, dma_addr_t data, int timeout) +{ + int ret = 0; + + /* clear interrupt status register (allow command register to clear) */ + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); + + /* Wait for the Command register to become free */ + if (wd719x_wait_ready(wd)) + return -ETIMEDOUT; + + /* make sure we get NO interrupts */ + dev |= WD719X_DISABLE_INT; + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, dev); + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_2, lun); + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_3, tag); + if (data) + wd719x_writel(wd, WD719X_AMR_SCB_IN, data); + + /* clear interrupt status register again */ + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); + + /* Now, write the command */ + wd719x_writeb(wd, WD719X_AMR_COMMAND, opcode); + + if (timeout) /* wait for the command to complete */ + ret = wd719x_wait_done(wd, timeout); + + /* clear interrupt status register (clean up) */ + if (opcode != WD719X_CMD_READ_FIRMVER) + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); + + return ret; +} + +static void wd719x_destroy(struct wd719x *wd) +{ + struct wd719x_scb *scb; + + /* stop the RISC */ + if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0, + WD719X_WAIT_FOR_RISC)) + dev_warn(&wd->pdev->dev, "RISC sleep command failed\n"); + /* disable RISC */ + wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0); + + /* free all SCBs */ + list_for_each_entry(scb, &wd->active_scbs, list) + pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb, + scb->phys); + list_for_each_entry(scb, &wd->free_scbs, list) + pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb, + scb->phys); + /* free internal buffers */ + pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys); + wd->fw_virt = NULL; + pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt, + wd->hash_phys); + wd->hash_virt = NULL; + pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param), + wd->params, wd->params_phys); + wd->params = NULL; + free_irq(wd->pdev->irq, wd); +} + +/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */ +static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result) +{ + struct wd719x *wd = shost_priv(cmd->device->host); + struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble; + + if (scb) { + list_move(&scb->list, &wd->free_scbs); + dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + scsi_dma_unmap(cmd); + } + cmd->result = result << 16; + cmd->scsi_done(cmd); +} + +/* Build a SCB and send it to the card */ +static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd) +{ + int i, count_sg; + unsigned long flags; + struct wd719x_scb *scb; + struct wd719x *wd = shost_priv(sh); + dma_addr_t phys; + + cmd->host_scribble = NULL; + + /* get a free SCB - either from existing ones or allocate a new one */ + spin_lock_irqsave(wd->sh->host_lock, flags); + scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list); + if (scb) { + list_del(&scb->list); + phys = scb->phys; + } else { + spin_unlock_irqrestore(wd->sh->host_lock, flags); + scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb), + &phys); + spin_lock_irqsave(wd->sh->host_lock, flags); + if (!scb) { + dev_err(&wd->pdev->dev, "unable to allocate SCB\n"); + wd719x_finish_cmd(cmd, DID_ERROR); + spin_unlock_irqrestore(wd->sh->host_lock, flags); + return 0; + } + } + memset(scb, 0, sizeof(struct wd719x_scb)); + list_add(&scb->list, &wd->active_scbs); + + scb->phys = phys; + scb->cmd = cmd; + cmd->host_scribble = (char *) scb; + + scb->CDB_tag = 0; /* Tagged queueing not supported yet */ + scb->devid = cmd->device->id; + scb->lun = cmd->device->lun; + + /* copy the command */ + memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len); + + /* map sense buffer */ + scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE; + cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle); + + /* request autosense */ + scb->SCB_options |= WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE; + + /* check direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) + scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION + | WD719X_SCB_FLAGS_PCI_TO_SCSI; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION; + + /* Scather/gather */ + count_sg = scsi_dma_map(cmd); + if (count_sg < 0) { + wd719x_finish_cmd(cmd, DID_ERROR); + spin_unlock_irqrestore(wd->sh->host_lock, flags); + return 0; + } + BUG_ON(count_sg > WD719X_SG); + + if (count_sg) { + struct scatterlist *sg; + + scb->data_length = cpu_to_le32(count_sg * + sizeof(struct wd719x_sglist)); + scb->data_p = cpu_to_le32(scb->phys + + offsetof(struct wd719x_scb, sg_list)); + + scsi_for_each_sg(cmd, sg, count_sg, i) { + scb->sg_list[i].ptr = cpu_to_le32(sg_dma_address(sg)); + scb->sg_list[i].length = cpu_to_le32(sg_dma_len(sg)); + } + scb->SCB_options |= WD719X_SCB_FLAGS_DO_SCATTER_GATHER; + } else { /* zero length */ + scb->data_length = 0; + scb->data_p = 0; + } + + /* check if the Command register is free */ + if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) { + spin_unlock_irqrestore(wd->sh->host_lock, flags); + return SCSI_MLQUEUE_HOST_BUSY; + } + + /* write pointer to the AMR */ + wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys); + /* send SCB opcode */ + wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB); + + spin_unlock_irqrestore(wd->sh->host_lock, flags); + + return 0; +} + +static int wd719x_chip_init(struct wd719x *wd) +{ + int i, ret; + u32 risc_init[3]; + const struct firmware *fw_wcs, *fw_risc; + const char fwname_wcs[] = "wd719x-wcs.bin"; + const char fwname_risc[] = "wd719x-risc.bin"; + + memset(wd->hash_virt, 0, WD719X_HASH_TABLE_SIZE); + + /* WCS (sequencer) firmware */ + ret = request_firmware(&fw_wcs, fwname_wcs, &wd->pdev->dev); + if (ret) { + dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n", + fwname_wcs, ret); + return ret; + } + /* RISC firmware */ + ret = request_firmware(&fw_risc, fwname_risc, &wd->pdev->dev); + if (ret) { + dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n", + fwname_risc, ret); + release_firmware(fw_wcs); + return ret; + } + wd->fw_size = ALIGN(fw_wcs->size, 4) + fw_risc->size; + + if (!wd->fw_virt) + wd->fw_virt = pci_alloc_consistent(wd->pdev, wd->fw_size, + &wd->fw_phys); + if (!wd->fw_virt) { + ret = -ENOMEM; + goto wd719x_init_end; + } + + /* make a fresh copy of WCS and RISC code */ + memcpy(wd->fw_virt, fw_wcs->data, fw_wcs->size); + memcpy(wd->fw_virt + ALIGN(fw_wcs->size, 4), fw_risc->data, + fw_risc->size); + + /* Reset the Spider Chip and adapter itself */ + wd719x_writeb(wd, WD719X_PCI_PORT_RESET, WD719X_PCI_RESET); + udelay(WD719X_WAIT_FOR_RISC); + /* Clear PIO mode bits set by BIOS */ + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, 0); + /* ensure RISC is not running */ + wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0); + /* ensure command port is ready */ + wd719x_writeb(wd, WD719X_AMR_COMMAND, 0); + if (wd719x_wait_ready(wd)) { + ret = -ETIMEDOUT; + goto wd719x_init_end; + } + + /* Transfer the first 2K words of RISC code to kick start the uP */ + risc_init[0] = wd->fw_phys; /* WCS FW */ + risc_init[1] = wd->fw_phys + ALIGN(fw_wcs->size, 4); /* RISC FW */ + risc_init[2] = wd->hash_phys; /* hash table */ + + /* clear DMA status */ + wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3STATUS, 0); + + /* address to read firmware from */ + wd719x_writel(wd, WD719X_PCI_EXTERNAL_ADDR, risc_init[1]); + /* base address to write firmware to (on card) */ + wd719x_writew(wd, WD719X_PCI_INTERNAL_ADDR, WD719X_PRAM_BASE_ADDR); + /* size: first 2K words */ + wd719x_writew(wd, WD719X_PCI_DMA_TRANSFER_SIZE, 2048 * 2); + /* start DMA */ + wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3CMD, WD719X_START_CHANNEL2_3DMA); + + /* wait for DMA to complete */ + i = WD719X_WAIT_FOR_RISC; + while (i-- > 0) { + u8 status = wd719x_readb(wd, WD719X_PCI_CHANNEL2_3STATUS); + if (status == WD719X_START_CHANNEL2_3DONE) + break; + if (status == WD719X_START_CHANNEL2_3ABORT) { + dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA aborted\n"); + ret = -EIO; + goto wd719x_init_end; + } + udelay(1); + } + if (i < 1) { + dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA timeout\n"); + ret = -ETIMEDOUT; + goto wd719x_init_end; + } + + /* firmware is loaded, now initialize and wake up the RISC */ + /* write RISC initialization long words to Spider */ + wd719x_writel(wd, WD719X_AMR_SCB_IN, risc_init[0]); + wd719x_writel(wd, WD719X_AMR_SCB_IN + 4, risc_init[1]); + wd719x_writel(wd, WD719X_AMR_SCB_IN + 8, risc_init[2]); + + /* disable interrupts during initialization of RISC */ + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, WD719X_DISABLE_INT); + + /* issue INITIALIZE RISC comand */ + wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_INIT_RISC); + /* enable advanced mode (wake up RISC) */ + wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, WD719X_ENABLE_ADVANCE_MODE); + udelay(WD719X_WAIT_FOR_RISC); + + ret = wd719x_wait_done(wd, WD719X_WAIT_FOR_RISC); + /* clear interrupt status register */ + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); + if (ret) { + dev_warn(&wd->pdev->dev, "Unable to initialize RISC\n"); + goto wd719x_init_end; + } + /* RISC is up and running */ + + /* Read FW version from RISC */ + ret = wd719x_direct_cmd(wd, WD719X_CMD_READ_FIRMVER, 0, 0, 0, 0, + WD719X_WAIT_FOR_RISC); + if (ret) { + dev_warn(&wd->pdev->dev, "Unable to read firmware version\n"); + goto wd719x_init_end; + } + dev_info(&wd->pdev->dev, "RISC initialized with firmware version %.2x.%.2x\n", + wd719x_readb(wd, WD719X_AMR_SCB_OUT + 1), + wd719x_readb(wd, WD719X_AMR_SCB_OUT)); + + /* RESET SCSI bus */ + ret = wd719x_direct_cmd(wd, WD719X_CMD_BUSRESET, 0, 0, 0, 0, + WD719X_WAIT_FOR_SCSI_RESET); + if (ret) { + dev_warn(&wd->pdev->dev, "SCSI bus reset failed\n"); + goto wd719x_init_end; + } + + /* use HostParameter structure to set Spider's Host Parameter Block */ + ret = wd719x_direct_cmd(wd, WD719X_CMD_SET_PARAM, 0, + sizeof(struct wd719x_host_param), 0, + wd->params_phys, WD719X_WAIT_FOR_RISC); + if (ret) { + dev_warn(&wd->pdev->dev, "Failed to set HOST PARAMETERS\n"); + goto wd719x_init_end; + } + + /* initiate SCAM (does nothing if disabled in BIOS) */ + /* bug?: we should pass a mask of static IDs which we don't have */ + ret = wd719x_direct_cmd(wd, WD719X_CMD_INIT_SCAM, 0, 0, 0, 0, + WD719X_WAIT_FOR_SCSI_RESET); + if (ret) { + dev_warn(&wd->pdev->dev, "SCAM initialization failed\n"); + goto wd719x_init_end; + } + + /* clear AMR_BIOS_SHARE_INT register */ + wd719x_writeb(wd, WD719X_AMR_BIOS_SHARE_INT, 0); + +wd719x_init_end: + release_firmware(fw_wcs); + release_firmware(fw_risc); + + return ret; +} + +static int wd719x_abort(struct scsi_cmnd *cmd) +{ + int action, result; + unsigned long flags; + struct wd719x_scb *scb = (struct wd719x_scb *)cmd->host_scribble; + struct wd719x *wd = shost_priv(cmd->device->host); + + dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag); + + action = /*cmd->tag ? WD719X_CMD_ABORT_TAG : */WD719X_CMD_ABORT; + + spin_lock_irqsave(wd->sh->host_lock, flags); + result = wd719x_direct_cmd(wd, action, cmd->device->id, + cmd->device->lun, cmd->tag, scb->phys, 0); + spin_unlock_irqrestore(wd->sh->host_lock, flags); + if (result) + return FAILED; + + return SUCCESS; +} + +static int wd719x_reset(struct scsi_cmnd *cmd, u8 opcode, u8 device) +{ + int result; + unsigned long flags; + struct wd719x *wd = shost_priv(cmd->device->host); + + dev_info(&wd->pdev->dev, "%s reset requested\n", + (opcode == WD719X_CMD_BUSRESET) ? "bus" : "device"); + + spin_lock_irqsave(wd->sh->host_lock, flags); + result = wd719x_direct_cmd(wd, opcode, device, 0, 0, 0, + WD719X_WAIT_FOR_SCSI_RESET); + spin_unlock_irqrestore(wd->sh->host_lock, flags); + if (result) + return FAILED; + + return SUCCESS; +} + +static int wd719x_dev_reset(struct scsi_cmnd *cmd) +{ + return wd719x_reset(cmd, WD719X_CMD_RESET, cmd->device->id); +} + +static int wd719x_bus_reset(struct scsi_cmnd *cmd) +{ + return wd719x_reset(cmd, WD719X_CMD_BUSRESET, 0); +} + +static int wd719x_host_reset(struct scsi_cmnd *cmd) +{ + struct wd719x *wd = shost_priv(cmd->device->host); + struct wd719x_scb *scb, *tmp; + unsigned long flags; + int result; + + dev_info(&wd->pdev->dev, "host reset requested\n"); + spin_lock_irqsave(wd->sh->host_lock, flags); + /* Try to reinit the RISC */ + if (wd719x_chip_init(wd) == 0) + result = SUCCESS; + else + result = FAILED; + + /* flush all SCBs */ + list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) { + struct scsi_cmnd *tmp_cmd = scb->cmd; + wd719x_finish_cmd(tmp_cmd, result); + } + spin_unlock_irqrestore(wd->sh->host_lock, flags); + + return result; +} + +static int wd719x_biosparam(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int geom[]) +{ + if (capacity >= 0x200000) { + geom[0] = 255; /* heads */ + geom[1] = 63; /* sectors */ + } else { + geom[0] = 64; /* heads */ + geom[1] = 32; /* sectors */ + } + geom[2] = sector_div(capacity, geom[0] * geom[1]); /* cylinders */ + + return 0; +} + +/* process a SCB-completion interrupt */ +static inline void wd719x_interrupt_SCB(struct wd719x *wd, + union wd719x_regs regs, + struct wd719x_scb *scb) +{ + struct scsi_cmnd *cmd; + int result; + + /* now have to find result from card */ + switch (regs.bytes.SUE) { + case WD719X_SUE_NOERRORS: + result = DID_OK; + break; + case WD719X_SUE_REJECTED: + dev_err(&wd->pdev->dev, "command rejected\n"); + result = DID_ERROR; + break; + case WD719X_SUE_SCBQFULL: + dev_err(&wd->pdev->dev, "SCB queue is full\n"); + result = DID_ERROR; + break; + case WD719X_SUE_TERM: + dev_dbg(&wd->pdev->dev, "SCB terminated by direct command\n"); + result = DID_ABORT; /* or DID_RESET? */ + break; + case WD719X_SUE_CHAN1ABORT: + case WD719X_SUE_CHAN23ABORT: + result = DID_ABORT; + dev_err(&wd->pdev->dev, "DMA abort\n"); + break; + case WD719X_SUE_CHAN1PAR: + case WD719X_SUE_CHAN23PAR: + result = DID_PARITY; + dev_err(&wd->pdev->dev, "DMA parity error\n"); + break; + case WD719X_SUE_TIMEOUT: + result = DID_TIME_OUT; + dev_dbg(&wd->pdev->dev, "selection timeout\n"); + break; + case WD719X_SUE_RESET: + dev_dbg(&wd->pdev->dev, "bus reset occured\n"); + result = DID_RESET; + break; + case WD719X_SUE_BUSERROR: + dev_dbg(&wd->pdev->dev, "SCSI bus error\n"); + result = DID_ERROR; + break; + case WD719X_SUE_WRONGWAY: + dev_err(&wd->pdev->dev, "wrong data transfer direction\n"); + result = DID_ERROR; + break; + case WD719X_SUE_BADPHASE: + dev_err(&wd->pdev->dev, "invalid SCSI phase\n"); + result = DID_ERROR; + break; + case WD719X_SUE_TOOLONG: + dev_err(&wd->pdev->dev, "record too long\n"); + result = DID_ERROR; + break; + case WD719X_SUE_BUSFREE: + dev_err(&wd->pdev->dev, "unexpected bus free\n"); + result = DID_NO_CONNECT; /* or DID_ERROR ???*/ + break; + case WD719X_SUE_ARSDONE: + dev_dbg(&wd->pdev->dev, "auto request sense\n"); + if (regs.bytes.SCSI == 0) + result = DID_OK; + else + result = DID_PARITY; + break; + case WD719X_SUE_IGNORED: + dev_err(&wd->pdev->dev, "target id %d ignored command\n", + scb->cmd->device->id); + result = DID_NO_CONNECT; + break; + case WD719X_SUE_WRONGTAGS: + dev_err(&wd->pdev->dev, "reversed tags\n"); + result = DID_ERROR; + break; + case WD719X_SUE_BADTAGS: + dev_err(&wd->pdev->dev, "tag type not supported by target\n"); + result = DID_ERROR; + break; + case WD719X_SUE_NOSCAMID: + dev_err(&wd->pdev->dev, "no SCAM soft ID available\n"); + result = DID_ERROR; + break; + default: + dev_warn(&wd->pdev->dev, "unknown SUE error code: 0x%x\n", + regs.bytes.SUE); + result = DID_ERROR; + break; + } + cmd = scb->cmd; + + wd719x_finish_cmd(cmd, result); +} + +static irqreturn_t wd719x_interrupt(int irq, void *dev_id) +{ + struct wd719x *wd = dev_id; + union wd719x_regs regs; + unsigned long flags; + u32 SCB_out; + + spin_lock_irqsave(wd->sh->host_lock, flags); + /* read SCB pointer back from card */ + SCB_out = wd719x_readl(wd, WD719X_AMR_SCB_OUT); + /* read all status info at once */ + regs.all = cpu_to_le32(wd719x_readl(wd, WD719X_AMR_OP_CODE)); + + switch (regs.bytes.INT) { + case WD719X_INT_NONE: + spin_unlock_irqrestore(wd->sh->host_lock, flags); + return IRQ_NONE; + case WD719X_INT_LINKNOSTATUS: + dev_err(&wd->pdev->dev, "linked command completed with no status\n"); + break; + case WD719X_INT_BADINT: + dev_err(&wd->pdev->dev, "unsolicited interrupt\n"); + break; + case WD719X_INT_NOERRORS: + case WD719X_INT_LINKNOERRORS: + case WD719X_INT_ERRORSLOGGED: + case WD719X_INT_SPIDERFAILED: + /* was the cmd completed a direct or SCB command? */ + if (regs.bytes.OPC == WD719X_CMD_PROCESS_SCB) { + struct wd719x_scb *scb; + list_for_each_entry(scb, &wd->active_scbs, list) + if (SCB_out == scb->phys) + break; + if (SCB_out == scb->phys) + wd719x_interrupt_SCB(wd, regs, scb); + else + dev_err(&wd->pdev->dev, "card returned invalid SCB pointer\n"); + } else + dev_warn(&wd->pdev->dev, "direct command 0x%x completed\n", + regs.bytes.OPC); + break; + case WD719X_INT_PIOREADY: + dev_err(&wd->pdev->dev, "card indicates PIO data ready but we never use PIO\n"); + /* interrupt will not be cleared until all data is read */ + break; + default: + dev_err(&wd->pdev->dev, "unknown interrupt reason: %d\n", + regs.bytes.INT); + + } + /* clear interrupt so another can happen */ + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); + spin_unlock_irqrestore(wd->sh->host_lock, flags); + + return IRQ_HANDLED; +} + +static void wd719x_eeprom_reg_read(struct eeprom_93cx6 *eeprom) +{ + struct wd719x *wd = eeprom->data; + u8 reg = wd719x_readb(wd, WD719X_PCI_GPIO_DATA); + + eeprom->reg_data_out = reg & WD719X_EE_DO; +} + +static void wd719x_eeprom_reg_write(struct eeprom_93cx6 *eeprom) +{ + struct wd719x *wd = eeprom->data; + u8 reg = 0; + + if (eeprom->reg_data_in) + reg |= WD719X_EE_DI; + if (eeprom->reg_data_clock) + reg |= WD719X_EE_CLK; + if (eeprom->reg_chip_select) + reg |= WD719X_EE_CS; + + wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, reg); +} + +/* read config from EEPROM so it can be downloaded by the RISC on (re-)init */ +static void wd719x_read_eeprom(struct wd719x *wd) +{ + struct eeprom_93cx6 eeprom; + u8 gpio; + struct wd719x_eeprom_header header; + + eeprom.data = wd; + eeprom.register_read = wd719x_eeprom_reg_read; + eeprom.register_write = wd719x_eeprom_reg_write; + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + /* set all outputs to low */ + wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, 0); + /* configure GPIO pins */ + gpio = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL); + /* GPIO outputs */ + gpio &= (~(WD719X_EE_CLK | WD719X_EE_DI | WD719X_EE_CS)); + /* GPIO input */ + gpio |= WD719X_EE_DO; + wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, gpio); + + /* read EEPROM header */ + eeprom_93cx6_multireadb(&eeprom, 0, (u8 *)&header, sizeof(header)); + + if (header.sig1 == 'W' && header.sig2 == 'D') + eeprom_93cx6_multireadb(&eeprom, header.cfg_offset, + (u8 *)wd->params, + sizeof(struct wd719x_host_param)); + else { /* default EEPROM values */ + dev_warn(&wd->pdev->dev, "EEPROM signature is invalid (0x%02x 0x%02x), using default values\n", + header.sig1, header.sig2); + wd->params->ch_1_th = 0x10; /* 16 DWs = 64 B */ + wd->params->scsi_conf = 0x4c; /* 48ma, spue, parity check */ + wd->params->own_scsi_id = 0x07; /* ID 7, SCAM disabled */ + wd->params->sel_timeout = 0x4d; /* 250 ms */ + wd->params->sleep_timer = 0x01; + wd->params->cdb_size = cpu_to_le16(0x5555); /* all 6 B */ + wd->params->scsi_pad = 0x1b; + if (wd->type == WD719X_TYPE_7193) /* narrow card - disable */ + wd->params->wide = cpu_to_le32(0x00000000); + else /* initiate & respond to WIDE messages */ + wd->params->wide = cpu_to_le32(0xffffffff); + wd->params->sync = cpu_to_le32(0xffffffff); + wd->params->soft_mask = 0x00; /* all disabled */ + wd->params->unsol_mask = 0x00; /* all disabled */ + } + /* disable TAGGED messages */ + wd->params->tag_en = cpu_to_le16(0x0000); +} + +/* Read card type from GPIO bits 1 and 3 */ +static enum wd719x_card_type wd719x_detect_type(struct wd719x *wd) +{ + u8 card = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL); + + card |= WD719X_GPIO_ID_BITS; + wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, card); + card = wd719x_readb(wd, WD719X_PCI_GPIO_DATA) & WD719X_GPIO_ID_BITS; + switch (card) { + case 0x08: + return WD719X_TYPE_7193; + case 0x02: + return WD719X_TYPE_7197; + case 0x00: + return WD719X_TYPE_7296; + default: + dev_warn(&wd->pdev->dev, "unknown card type 0x%x\n", card); + return WD719X_TYPE_UNKNOWN; + } +} + +static int wd719x_board_found(struct Scsi_Host *sh) +{ + struct wd719x *wd = shost_priv(sh); + char *card_types[] = { "Unknown card", "WD7193", "WD7197", "WD7296" }; + int ret; + + INIT_LIST_HEAD(&wd->active_scbs); + INIT_LIST_HEAD(&wd->free_scbs); + + sh->base = pci_resource_start(wd->pdev, 0); + + wd->type = wd719x_detect_type(wd); + + wd->sh = sh; + sh->irq = wd->pdev->irq; + wd->fw_virt = NULL; + + /* memory area for host (EEPROM) parameters */ + wd->params = pci_alloc_consistent(wd->pdev, + sizeof(struct wd719x_host_param), + &wd->params_phys); + if (!wd->params) { + dev_warn(&wd->pdev->dev, "unable to allocate parameter buffer\n"); + return -ENOMEM; + } + + /* memory area for the RISC for hash table of outstanding requests */ + wd->hash_virt = pci_alloc_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, + &wd->hash_phys); + if (!wd->hash_virt) { + dev_warn(&wd->pdev->dev, "unable to allocate hash buffer\n"); + ret = -ENOMEM; + goto fail_free_params; + } + + ret = request_irq(wd->pdev->irq, wd719x_interrupt, IRQF_SHARED, + "wd719x", wd); + if (ret) { + dev_warn(&wd->pdev->dev, "unable to assign IRQ %d\n", + wd->pdev->irq); + goto fail_free_hash; + } + + /* read parameters from EEPROM */ + wd719x_read_eeprom(wd); + + ret = wd719x_chip_init(wd); + if (ret) + goto fail_free_irq; + + sh->this_id = wd->params->own_scsi_id & WD719X_EE_SCSI_ID_MASK; + + dev_info(&wd->pdev->dev, "%s at I/O 0x%lx, IRQ %u, SCSI ID %d\n", + card_types[wd->type], sh->base, sh->irq, sh->this_id); + + return 0; + +fail_free_irq: + free_irq(wd->pdev->irq, wd); +fail_free_hash: + pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt, + wd->hash_phys); +fail_free_params: + pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param), + wd->params, wd->params_phys); + + return ret; +} + +static struct scsi_host_template wd719x_template = { + .name = "Western Digital 719x", + .queuecommand = wd719x_queuecommand, + .eh_abort_handler = wd719x_abort, + .eh_device_reset_handler = wd719x_dev_reset, + .eh_bus_reset_handler = wd719x_bus_reset, + .eh_host_reset_handler = wd719x_host_reset, + .bios_param = wd719x_biosparam, + .proc_name = "wd719x", + .can_queue = 255, + .this_id = 7, + .sg_tablesize = WD719X_SG, + .cmd_per_lun = WD719X_CMD_PER_LUN, + .use_clustering = ENABLE_CLUSTERING, +}; + +static int wd719x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *d) +{ + int err; + struct Scsi_Host *sh; + struct wd719x *wd; + + err = pci_enable_device(pdev); + if (err) + goto fail; + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + dev_warn(&pdev->dev, "Unable to set 32-bit DMA mask\n"); + goto disable_device; + } + + err = pci_request_regions(pdev, "wd719x"); + if (err) + goto disable_device; + pci_set_master(pdev); + + err = -ENODEV; + if (pci_resource_len(pdev, 0) == 0) + goto release_region; + + err = -ENOMEM; + sh = scsi_host_alloc(&wd719x_template, sizeof(struct wd719x)); + if (!sh) + goto release_region; + + wd = shost_priv(sh); + wd->base = pci_iomap(pdev, 0, 0); + if (!wd->base) + goto free_host; + wd->pdev = pdev; + + err = wd719x_board_found(sh); + if (err) + goto unmap; + + err = scsi_add_host(sh, &wd->pdev->dev); + if (err) + goto destroy; + + scsi_scan_host(sh); + + pci_set_drvdata(pdev, sh); + return 0; + +destroy: + wd719x_destroy(wd); +unmap: + pci_iounmap(pdev, wd->base); +free_host: + scsi_host_put(sh); +release_region: + pci_release_regions(pdev); +disable_device: + pci_disable_device(pdev); +fail: + return err; +} + + +static void wd719x_pci_remove(struct pci_dev *pdev) +{ + struct Scsi_Host *sh = pci_get_drvdata(pdev); + struct wd719x *wd = shost_priv(sh); + + scsi_remove_host(sh); + wd719x_destroy(wd); + pci_iounmap(pdev, wd->base); + pci_release_regions(pdev); + pci_disable_device(pdev); + + scsi_host_put(sh); +} + +static DEFINE_PCI_DEVICE_TABLE(wd719x_pci_table) = { + { PCI_DEVICE(PCI_VENDOR_ID_WD, 0x3296) }, + {} +}; + +MODULE_DEVICE_TABLE(pci, wd719x_pci_table); + +static struct pci_driver wd719x_pci_driver = { + .name = "wd719x", + .id_table = wd719x_pci_table, + .probe = wd719x_pci_probe, + .remove = wd719x_pci_remove, +}; + +static int __init wd719x_init(void) +{ + return pci_register_driver(&wd719x_pci_driver); +} + +static void __exit wd719x_exit(void) +{ + pci_unregister_driver(&wd719x_pci_driver); +} + +module_init(wd719x_init); +module_exit(wd719x_exit); + +MODULE_DESCRIPTION("Western Digital WD7193/7197/7296 SCSI driver"); +MODULE_AUTHOR("Ondrej Zary, Aaron Dewell, Juergen Gaertner"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("wd719x-wcs.bin"); +MODULE_FIRMWARE("wd719x-risc.bin"); diff --git a/drivers/scsi/wd719x.h b/drivers/scsi/wd719x.h new file mode 100644 index 000000000000..185e30e4eb93 --- /dev/null +++ b/drivers/scsi/wd719x.h @@ -0,0 +1,249 @@ +#ifndef _WD719X_H_ +#define _WD719X_H_ + +#define WD719X_SG 255 /* Scatter/gather size */ +#define WD719X_CMD_PER_LUN 1 /* We should be able to do linked commands, but + * this is 1 for now to be safe. */ + +struct wd719x_sglist { + __le32 ptr; + __le32 length; +} __packed; + +enum wd719x_card_type { + WD719X_TYPE_UNKNOWN = 0, + WD719X_TYPE_7193, + WD719X_TYPE_7197, + WD719X_TYPE_7296, +}; + +union wd719x_regs { + __le32 all; /* All Status at once */ + struct { + u8 OPC; /* Opcode register */ + u8 SCSI; /* SCSI Errors */ + u8 SUE; /* Spider unique Errors */ + u8 INT; /* Interrupt Status */ + } bytes; +}; + +/* Spider Command Block (SCB) */ +struct wd719x_scb { + __le32 Int_SCB; /* 00-03 Internal SCB link pointer (must be cleared) */ + u8 SCB_opcode; /* 04 SCB Command opcode */ + u8 CDB_tag; /* 05 SCSI Tag byte for CDB queues (0 if untagged) */ + u8 lun; /* 06 SCSI LUN */ + u8 devid; /* 07 SCSI Device ID */ + u8 CDB[16]; /* 08-23 SCSI CDB (16 bytes as defined by ANSI spec. */ + __le32 data_p; /* 24-27 Data transfer address (or SG list address) */ + __le32 data_length; /* 28-31 Data transfer Length (or SG list length) */ + __le32 CDB_link; /* 32-35 SCSI CDB Link Ptr */ + __le32 sense_buf; /* 36-39 Auto request sense buffer address */ + u8 sense_buf_length;/* 40 Auto request sense transfer length */ + u8 reserved; /* 41 reserved */ + u8 SCB_options; /* 42 SCB-options */ + u8 SCB_tag_msg; /* 43 Tagged messages options */ + /* Not filled in by host */ + __le32 req_ptr; /* 44-47 Ptr to Host Request returned on interrupt */ + u8 host_opcode; /* 48 Host Command Opcode (same as AMR_00) */ + u8 scsi_stat; /* 49 SCSI Status returned */ + u8 ret_error; /* 50 SPIDER Unique Error Code returned (SUE) */ + u8 int_stat; /* 51 Message u8 / Interrupt Status byte returned */ + __le32 transferred; /* 52-55 Bytes Transferred */ + u8 last_trans[3]; /* 56-58 Bytes Transferred in last session */ + u8 length; /* 59 SCSI Messages Length (1-8) */ + u8 sync_offset; /* 60 Synchronous offset */ + u8 sync_rate; /* 61 Synchronous rate */ + u8 flags[2]; /* 62-63 SCB specific flags (local to each thread) */ + /* everything below is for driver use (not used by card) */ + dma_addr_t phys; /* bus address of the SCB */ + struct scsi_cmnd *cmd; /* a copy of the pointer we were passed */ + struct list_head list; + struct wd719x_sglist sg_list[WD719X_SG] __aligned(8); /* SG list */ +} __packed; + +struct wd719x { + struct Scsi_Host *sh; /* pointer to host structure */ + struct pci_dev *pdev; + void __iomem *base; + enum wd719x_card_type type; /* type of card */ + void *fw_virt; /* firmware buffer CPU address */ + dma_addr_t fw_phys; /* firmware buffer bus address */ + size_t fw_size; /* firmware buffer size */ + struct wd719x_host_param *params; /* host parameters (EEPROM) */ + dma_addr_t params_phys; /* host parameters bus address */ + void *hash_virt; /* hash table CPU address */ + dma_addr_t hash_phys; /* hash table bus address */ + struct list_head active_scbs; + struct list_head free_scbs; +}; + +/* timeout delays in microsecs */ +#define WD719X_WAIT_FOR_CMD_READY 500 +#define WD719X_WAIT_FOR_RISC 2000 +#define WD719X_WAIT_FOR_SCSI_RESET 3000000 + +/* All commands except 0x00 generate an interrupt */ +#define WD719X_CMD_READY 0x00 /* Command register ready (or noop) */ +#define WD719X_CMD_INIT_RISC 0x01 /* Initialize RISC */ +/* 0x02 is reserved */ +#define WD719X_CMD_BUSRESET 0x03 /* Assert SCSI bus reset */ +#define WD719X_CMD_READ_FIRMVER 0x04 /* Read the Firmware Revision */ +#define WD719X_CMD_ECHO_BYTES 0x05 /* Echo command bytes (DW) */ +/* 0x06 is reserved */ +/* 0x07 is reserved */ +#define WD719X_CMD_GET_PARAM 0x08 /* Get programmable parameters */ +#define WD719X_CMD_SET_PARAM 0x09 /* Set programmable parameters */ +#define WD719X_CMD_SLEEP 0x0a /* Put SPIDER to sleep */ +#define WD719X_CMD_READ_INIT 0x0b /* Read initialization parameters */ +#define WD719X_CMD_RESTORE_INIT 0x0c /* Restore initialization parameters */ +/* 0x0d is reserved */ +/* 0x0e is reserved */ +/* 0x0f is reserved */ +#define WD719X_CMD_ABORT_TAG 0x10 /* Send Abort tag message to target */ +#define WD719X_CMD_ABORT 0x11 /* Send Abort message to target */ +#define WD719X_CMD_RESET 0x12 /* Send Reset message to target */ +#define WD719X_CMD_INIT_SCAM 0x13 /* Initiate SCAM */ +#define WD719X_CMD_GET_SYNC 0x14 /* Get synchronous rates */ +#define WD719X_CMD_SET_SYNC 0x15 /* Set synchronous rates */ +#define WD719X_CMD_GET_WIDTH 0x16 /* Get SCSI bus width */ +#define WD719X_CMD_SET_WIDTH 0x17 /* Set SCSI bus width */ +#define WD719X_CMD_GET_TAGS 0x18 /* Get tag flags */ +#define WD719X_CMD_SET_TAGS 0x19 /* Set tag flags */ +#define WD719X_CMD_GET_PARAM2 0x1a /* Get programmable params (format 2) */ +#define WD719X_CMD_SET_PARAM2 0x1b /* Set programmable params (format 2) */ +/* Commands with request pointers (mailbox) */ +#define WD719X_CMD_PROCESS_SCB 0x80 /* Process SCSI Control Block (SCB) */ +/* No interrupt generated on acceptance of SCB pointer */ + +/* interrupt status defines */ +#define WD719X_INT_NONE 0x00 /* No interrupt pending */ +#define WD719X_INT_NOERRORS 0x01 /* Command completed with no errors */ +#define WD719X_INT_LINKNOERRORS 0x02 /* link cmd completed with no errors */ +#define WD719X_INT_LINKNOSTATUS 0x03 /* link cmd completed with no flag set */ +#define WD719X_INT_ERRORSLOGGED 0x04 /* cmd completed with errors logged */ +#define WD719X_INT_SPIDERFAILED 0x05 /* cmd failed without valid SCSI status */ +#define WD719X_INT_BADINT 0x80 /* unsolicited interrupt */ +#define WD719X_INT_PIOREADY 0xf0 /* data ready for PIO output */ + +/* Spider Unique Error Codes (SUE) */ +#define WD719X_SUE_NOERRORS 0x00 /* No errors detected by SPIDER */ +#define WD719X_SUE_REJECTED 0x01 /* Command Rejected (bad opcode/param) */ +#define WD719X_SUE_SCBQFULL 0x02 /* SCB queue full */ +/* 0x03 is reserved */ +#define WD719X_SUE_TERM 0x04 /* Host terminated SCB via primative cmd */ +#define WD719X_SUE_CHAN1PAR 0x05 /* PCI Channel 1 parity error occurred */ +#define WD719X_SUE_CHAN1ABORT 0x06 /* PCI Channel 1 system abort occurred */ +#define WD719X_SUE_CHAN23PAR 0x07 /* PCI Channel 2/3 parity error occurred */ +#define WD719X_SUE_CHAN23ABORT 0x08 /* PCI Channel 2/3 system abort occurred */ +#define WD719X_SUE_TIMEOUT 0x10 /* Selection/reselection timeout */ +#define WD719X_SUE_RESET 0x11 /* SCSI bus reset occurred */ +#define WD719X_SUE_BUSERROR 0x12 /* SCSI bus error */ +#define WD719X_SUE_WRONGWAY 0x13 /* Wrong data transfer dir set by target */ +#define WD719X_SUE_BADPHASE 0x14 /* SCSI phase illegal or unexpected */ +#define WD719X_SUE_TOOLONG 0x15 /* target requested too much data */ +#define WD719X_SUE_BUSFREE 0x16 /* Unexpected SCSI bus free */ +#define WD719X_SUE_ARSDONE 0x17 /* Auto request sense executed */ +#define WD719X_SUE_IGNORED 0x18 /* SCSI message was ignored by target */ +#define WD719X_SUE_WRONGTAGS 0x19 /* Tagged SCB & tags off (or vice versa) */ +#define WD719X_SUE_BADTAGS 0x1a /* Wrong tag message type for target */ +#define WD719X_SUE_NOSCAMID 0x1b /* No SCAM soft ID available */ + +/* code sizes */ +#define WD719X_HASH_TABLE_SIZE 4096 + +/* Advanced Mode Registers */ +/* Regs 0x00..0x1f are for Advanced Mode of the card (RISC is running). */ +#define WD719X_AMR_COMMAND 0x00 +#define WD719X_AMR_CMD_PARAM 0x01 +#define WD719X_AMR_CMD_PARAM_2 0x02 +#define WD719X_AMR_CMD_PARAM_3 0x03 +#define WD719X_AMR_SCB_IN 0x04 + +#define WD719X_AMR_BIOS_SHARE_INT 0x0f + +#define WD719X_AMR_SCB_OUT 0x18 +#define WD719X_AMR_OP_CODE 0x1c +#define WD719X_AMR_SCSI_STATUS 0x1d +#define WD719X_AMR_SCB_ERROR 0x1e +#define WD719X_AMR_INT_STATUS 0x1f + +#define WD719X_DISABLE_INT 0x80 + +/* SCB flags */ +#define WD719X_SCB_FLAGS_CHECK_DIRECTION 0x01 +#define WD719X_SCB_FLAGS_PCI_TO_SCSI 0x02 +#define WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE 0x10 +#define WD719X_SCB_FLAGS_DO_SCATTER_GATHER 0x20 +#define WD719X_SCB_FLAGS_NO_DISCONNECT 0x40 + +/* PCI Registers used for reset, initial code download */ +/* Regs 0x20..0x3f are for Normal (DOS) mode (RISC is asleep). */ +#define WD719X_PCI_GPIO_CONTROL 0x3C +#define WD719X_PCI_GPIO_DATA 0x3D +#define WD719X_PCI_PORT_RESET 0x3E +#define WD719X_PCI_MODE_SELECT 0x3F + +#define WD719X_PCI_EXTERNAL_ADDR 0x60 +#define WD719X_PCI_INTERNAL_ADDR 0x64 +#define WD719X_PCI_DMA_TRANSFER_SIZE 0x66 +#define WD719X_PCI_CHANNEL2_3CMD 0x68 +#define WD719X_PCI_CHANNEL2_3STATUS 0x69 + +#define WD719X_GPIO_ID_BITS 0x0a +#define WD719X_PRAM_BASE_ADDR 0x00 + +/* codes written to or read from the card */ +#define WD719X_PCI_RESET 0x01 +#define WD719X_ENABLE_ADVANCE_MODE 0x01 + +#define WD719X_START_CHANNEL2_3DMA 0x17 +#define WD719X_START_CHANNEL2_3DONE 0x01 +#define WD719X_START_CHANNEL2_3ABORT 0x20 + +/* 33C296 GPIO bits for EEPROM pins */ +#define WD719X_EE_DI (1 << 1) +#define WD719X_EE_CS (1 << 2) +#define WD719X_EE_CLK (1 << 3) +#define WD719X_EE_DO (1 << 4) + +/* EEPROM contents */ +struct wd719x_eeprom_header { + u8 sig1; + u8 sig2; + u8 version; + u8 checksum; + u8 cfg_offset; + u8 cfg_size; + u8 setup_offset; + u8 setup_size; +} __packed; + +#define WD719X_EE_SIG1 0 +#define WD719X_EE_SIG2 1 +#define WD719X_EE_VERSION 2 +#define WD719X_EE_CHECKSUM 3 +#define WD719X_EE_CFG_OFFSET 4 +#define WD719X_EE_CFG_SIZE 5 +#define WD719X_EE_SETUP_OFFSET 6 +#define WD719X_EE_SETUP_SIZE 7 + +#define WD719X_EE_SCSI_ID_MASK 0xf + +/* SPIDER Host Parameters Block (=EEPROM configuration block) */ +struct wd719x_host_param { + u8 ch_1_th; /* FIFO threshold */ + u8 scsi_conf; /* SCSI configuration */ + u8 own_scsi_id; /* controller SCSI ID */ + u8 sel_timeout; /* selection timeout*/ + u8 sleep_timer; /* seep timer */ + __le16 cdb_size;/* CDB size groups */ + __le16 tag_en; /* Tag msg enables (ID 0-15) */ + u8 scsi_pad; /* SCSI pad control */ + __le32 wide; /* WIDE msg options (ID 0-15) */ + __le32 sync; /* SYNC msg options (ID 0-15) */ + u8 soft_mask; /* soft error mask */ + u8 unsol_mask; /* unsolicited error mask */ +} __packed; + +#endif /* _WD719X_H_ */ diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h index bc9dcc8cc3ce..51da2341280d 100644 --- a/drivers/soc/ti/knav_qmss.h +++ b/drivers/soc/ti/knav_qmss.h @@ -348,15 +348,15 @@ struct knav_range_info { list_for_each_entry(region, &kdev->regions, list) #define first_region(kdev) \ - list_first_entry(&kdev->regions, \ - struct knav_region, list) + list_first_entry_or_null(&kdev->regions, \ + struct knav_region, list) #define for_each_queue_range(kdev, range) \ list_for_each_entry(range, &kdev->queue_ranges, list) #define first_queue_range(kdev) \ - list_first_entry(&kdev->queue_ranges, \ - struct knav_range_info, list) + list_first_entry_or_null(&kdev->queue_ranges, \ + struct knav_range_info, list) #define for_each_pool(kdev, pool) \ list_for_each_entry(pool, &kdev->pools, list) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 0a2c8634c48b..9b8dd6732681 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -785,7 +785,7 @@ void *knav_pool_create(const char *name, dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n", region_id, name); ret = -ENOMEM; - goto err; + goto err_unlock; } /* Region maintains a sorted (by region offset) list of pools @@ -815,15 +815,16 @@ void *knav_pool_create(const char *name, dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n", name, region_id); ret = -ENOMEM; - goto err; + goto err_unlock; } mutex_unlock(&knav_dev_lock); kdesc_fill_pool(pool); return pool; -err: +err_unlock: mutex_unlock(&knav_dev_lock); +err: kfree(pool->name); devm_kfree(kdev->dev, pool); return ERR_PTR(ret); @@ -1305,14 +1306,14 @@ static void knav_free_queue_ranges(struct knav_device *kdev) static void knav_queue_free_regions(struct knav_device *kdev) { struct knav_region *region; - struct knav_pool *pool; + struct knav_pool *pool, *tmp; unsigned size; for (;;) { region = first_region(kdev); if (!region) break; - list_for_each_entry(pool, ®ion->pools, region_inst) + list_for_each_entry_safe(pool, tmp, ®ion->pools, region_inst) knav_pool_destroy(pool); size = region->virt_end - region->virt_start; @@ -1639,7 +1640,7 @@ static int knav_queue_init_queues(struct knav_device *kdev) size = (1 << kdev->inst_shift) * kdev->num_queues_in_use; kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL); if (!kdev->instances) - return -1; + return -ENOMEM; for_each_queue_range(kdev, range) { if (range->ops && range->ops->init_range) diff --git a/drivers/soc/versatile/Kconfig b/drivers/soc/versatile/Kconfig index bf5ee9c85330..a928a7fc6be4 100644 --- a/drivers/soc/versatile/Kconfig +++ b/drivers/soc/versatile/Kconfig @@ -1,6 +1,15 @@ # # ARM Versatile SoC drivers # +config SOC_INTEGRATOR_CM + bool "SoC bus device for the ARM Integrator platform core modules" + depends on ARCH_INTEGRATOR + select SOC_BUS + help + Include support for the SoC bus on the ARM Integrator platform + core modules providing some sysfs information about the ASIC + variant. + config SOC_REALVIEW bool "SoC bus device for the ARM RealView platforms" depends on ARCH_REALVIEW diff --git a/drivers/soc/versatile/Makefile b/drivers/soc/versatile/Makefile index ad547435648e..cf612fe3a659 100644 --- a/drivers/soc/versatile/Makefile +++ b/drivers/soc/versatile/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_SOC_INTEGRATOR_CM) += soc-integrator.o obj-$(CONFIG_SOC_REALVIEW) += soc-realview.o diff --git a/drivers/soc/versatile/soc-integrator.c b/drivers/soc/versatile/soc-integrator.c new file mode 100644 index 000000000000..a5d7d39ae0ad --- /dev/null +++ b/drivers/soc/versatile/soc-integrator.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/of.h> + +#define INTEGRATOR_HDR_ID_OFFSET 0x00 + +static u32 integrator_coreid; + +static const struct of_device_id integrator_cm_match[] = { + { .compatible = "arm,core-module-integrator", }, + { } +}; + +static const char *integrator_arch_str(u32 id) +{ + switch ((id >> 16) & 0xff) { + case 0x00: + return "ASB little-endian"; + case 0x01: + return "AHB little-endian"; + case 0x03: + return "AHB-Lite system bus, bi-endian"; + case 0x04: + return "AHB"; + case 0x08: + return "AHB system bus, ASB processor bus"; + default: + return "Unknown"; + } +} + +static const char *integrator_fpga_str(u32 id) +{ + switch ((id >> 12) & 0xf) { + case 0x01: + return "XC4062"; + case 0x02: + return "XC4085"; + case 0x03: + return "XVC600"; + case 0x04: + return "EPM7256AE (Altera PLD)"; + default: + return "Unknown"; + } +} + +static ssize_t integrator_get_manf(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", integrator_coreid >> 24); +} + +static struct device_attribute integrator_manf_attr = + __ATTR(manufacturer, S_IRUGO, integrator_get_manf, NULL); + +static ssize_t integrator_get_arch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", integrator_arch_str(integrator_coreid)); +} + +static struct device_attribute integrator_arch_attr = + __ATTR(arch, S_IRUGO, integrator_get_arch, NULL); + +static ssize_t integrator_get_fpga(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", integrator_fpga_str(integrator_coreid)); +} + +static struct device_attribute integrator_fpga_attr = + __ATTR(fpga, S_IRUGO, integrator_get_fpga, NULL); + +static ssize_t integrator_get_build(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", (integrator_coreid >> 4) & 0xFF); +} + +static struct device_attribute integrator_build_attr = + __ATTR(build, S_IRUGO, integrator_get_build, NULL); + +static int __init integrator_soc_init(void) +{ + static struct regmap *syscon_regmap; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + struct device_node *np; + struct device *dev; + u32 val; + int ret; + + np = of_find_matching_node(NULL, integrator_cm_match); + if (!np) + return -ENODEV; + + syscon_regmap = syscon_node_to_regmap(np); + if (IS_ERR(syscon_regmap)) + return PTR_ERR(syscon_regmap); + + ret = regmap_read(syscon_regmap, INTEGRATOR_HDR_ID_OFFSET, + &val); + if (ret) + return -ENODEV; + integrator_coreid = val; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->soc_id = "Integrator"; + soc_dev_attr->machine = "Integrator"; + soc_dev_attr->family = "Versatile"; + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr); + return -ENODEV; + } + dev = soc_device_to_device(soc_dev); + + device_create_file(dev, &integrator_manf_attr); + device_create_file(dev, &integrator_arch_attr); + device_create_file(dev, &integrator_fpga_attr); + device_create_file(dev, &integrator_build_attr); + + dev_info(dev, "Detected ARM core module:\n"); + dev_info(dev, " Manufacturer: %02x\n", (val >> 24)); + dev_info(dev, " Architecture: %s\n", integrator_arch_str(val)); + dev_info(dev, " FPGA: %s\n", integrator_fpga_str(val)); + dev_info(dev, " Build: %02x\n", (val >> 4) & 0xFF); + dev_info(dev, " Rev: %c\n", ('A' + (val & 0x03))); + + return 0; +} +device_initcall(integrator_soc_init); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 72e12bad14b9..d0d5542efc06 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -376,9 +376,6 @@ static void pump_transfers(unsigned long data) chip = dws->cur_chip; spi = message->spi; - if (unlikely(!chip->clk_div)) - chip->clk_div = dws->max_freq / chip->speed_hz; - if (message->state == ERROR_STATE) { message->status = -EIO; goto early_exit; @@ -419,7 +416,7 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz) { speed = chip->speed_hz; - if (transfer->speed_hz != speed) { + if ((transfer->speed_hz != speed) || (!chip->clk_div)) { speed = transfer->speed_hz; /* clk_div doesn't support odd number */ @@ -581,7 +578,6 @@ static int dw_spi_setup(struct spi_device *spi) dev_err(&spi->dev, "No max speed HZ parameter\n"); return -EINVAL; } - chip->speed_hz = spi->max_speed_hz; chip->tmode = 0; /* Tx & Rx */ /* Default SPI mode is SCPOL = 0, SCPH = 0 */ diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 39e2c0a55a28..f63de781c729 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -562,9 +562,9 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) sspi->word_width = DIV_ROUND_UP(bits_per_word, 8); txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - sspi->word_width; + (sspi->word_width >> 1); rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - sspi->word_width; + (sspi->word_width >> 1); if (!(spi->mode & SPI_CS_HIGH)) regval |= SIRFSOC_SPI_CS_IDLE_STAT; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ebcb33df2eb2..50f20f243981 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -615,13 +615,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, sg_free_table(sgt); return -ENOMEM; } - sg_buf = page_address(vm_page) + - ((size_t)buf & ~PAGE_MASK); + sg_set_page(&sgt->sgl[i], vm_page, + min, offset_in_page(buf)); } else { sg_buf = buf; + sg_set_buf(&sgt->sgl[i], sg_buf, min); } - sg_set_buf(&sgt->sgl[i], sg_buf, min); buf += min; len -= min; diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 6014625920b0..e969107ddb47 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -537,7 +537,8 @@ static const struct i2c_device_id isl29028_id[] = { MODULE_DEVICE_TABLE(i2c, isl29028_id); static const struct of_device_id isl29028_of_match[] = { - { .compatible = "isil,isl29028", }, + { .compatible = "isl,isl29028", }, + { .compatible = "isil,isl29028", },/* deprecated, don't use */ { }, }; MODULE_DEVICE_TABLE(of, isl29028_of_match); diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c index 9935e66935af..eddef9cd2e16 100644 --- a/drivers/staging/rtl8188eu/core/rtw_cmd.c +++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c @@ -275,11 +275,11 @@ u8 rtw_sitesurvey_cmd(struct adapter *padapter, struct ndis_802_11_ssid *ssid, if (check_fwstate(pmlmepriv, _FW_LINKED) == true) rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SCAN, 1); - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) return _FAIL; - psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL); + psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC); if (psurveyPara == NULL) { kfree(ph2c); return _FAIL; @@ -405,7 +405,7 @@ u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork) else RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+Join cmd: SSid =[%s]\n", pmlmepriv->assoc_ssid.Ssid)); - pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (pcmd == NULL) { res = _FAIL; RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("rtw_joinbss_cmd: memory allocate for cmd_obj fail!!!\n")); @@ -755,13 +755,13 @@ u8 rtw_dynamic_chk_wk_cmd(struct adapter *padapter) u8 res = _SUCCESS; - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ph2c); res = _FAIL; @@ -967,13 +967,13 @@ u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue) u8 res = _SUCCESS; if (enqueue) { - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ph2c); res = _FAIL; @@ -1010,13 +1010,13 @@ u8 rtw_rpt_timer_cfg_cmd(struct adapter *padapter, u16 min_time) u8 res = _SUCCESS; - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ph2c); res = _FAIL; @@ -1088,13 +1088,13 @@ u8 rtw_ps_cmd(struct adapter *padapter) u8 res = _SUCCESS; - ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ppscmd == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ppscmd); res = _FAIL; diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c index 5ba5099ec20d..70b1bc3e0e63 100644 --- a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c @@ -4241,12 +4241,12 @@ void report_survey_event(struct adapter *padapter, pcmdpriv = &padapter->cmdpriv; - pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (pcmd_obj == NULL) return; cmdsz = (sizeof(struct survey_event) + sizeof(struct C2HEvent_Header)); - pevtcmd = kzalloc(cmdsz, GFP_KERNEL); + pevtcmd = kzalloc(cmdsz, GFP_ATOMIC); if (pevtcmd == NULL) { kfree(pcmd_obj); return; @@ -4339,12 +4339,12 @@ void report_join_res(struct adapter *padapter, int res) struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (pcmd_obj == NULL) return; cmdsz = (sizeof(struct joinbss_event) + sizeof(struct C2HEvent_Header)); - pevtcmd = kzalloc(cmdsz, GFP_KERNEL); + pevtcmd = kzalloc(cmdsz, GFP_ATOMIC); if (pevtcmd == NULL) { kfree(pcmd_obj); return; @@ -4854,11 +4854,11 @@ void survey_timer_hdl(void *function_context) pmlmeext->scan_abort = false;/* reset */ } - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) goto exit_survey_timer_hdl; - psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL); + psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC); if (psurveyPara == NULL) { kfree(ph2c); goto exit_survey_timer_hdl; diff --git a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c index 33ccbbbd8ed6..d300369977fa 100644 --- a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c @@ -935,7 +935,7 @@ int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len) return true; } - bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_KERNEL); + bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_ATOMIC); subtype = GetFrameSubType(pframe) >> 4; diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 407a318b09db..2f87150a21b7 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -47,6 +47,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */ {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */ {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */ + {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */ {} /* Terminating entry */ }; diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index ab3ab27d49b7..4d1b7224a7f2 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -110,58 +110,6 @@ static struct device_driver tcm_loop_driverfs = { */ struct device *tcm_loop_primary; -/* - * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and - * drivers/scsi/libiscsi.c:iscsi_change_queue_depth() - */ -static int tcm_loop_change_queue_depth( - struct scsi_device *sdev, - int depth, - int reason) -{ - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, depth); - break; - case SCSI_QDEPTH_RAMP_UP: - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); - break; - default: - return -EOPNOTSUPP; - } - return sdev->queue_depth; -} - -static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag); - - if (tag) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag = 0; - - return tag; -} - -/* - * Locate the SAM Task Attr from struct scsi_cmnd * - */ -static int tcm_loop_sam_attr(struct scsi_cmnd *sc, int tag) -{ - if (sc->device->tagged_supported && - sc->device->ordered_tags && tag >= 0) - return MSG_ORDERED_TAG; - - return MSG_SIMPLE_TAG; -} - static void tcm_loop_submission_work(struct work_struct *work) { struct tcm_loop_cmd *tl_cmd = @@ -220,7 +168,7 @@ static void tcm_loop_submission_work(struct work_struct *work) rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, - transfer_length, tcm_loop_sam_attr(sc, tl_cmd->sc_cmd_tag), + transfer_length, MSG_SIMPLE_TAG, sc->sc_data_direction, 0, scsi_sglist(sc), scsi_sg_count(sc), sgl_bidi, sgl_bidi_count, @@ -431,27 +379,13 @@ static int tcm_loop_slave_alloc(struct scsi_device *sd) return 0; } -static int tcm_loop_slave_configure(struct scsi_device *sd) -{ - if (sd->tagged_supported) { - scsi_activate_tcq(sd, sd->queue_depth); - scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG, - sd->host->cmd_per_lun); - } else { - scsi_adjust_queue_depth(sd, 0, - sd->host->cmd_per_lun); - } - - return 0; -} - static struct scsi_host_template tcm_loop_driver_template = { .show_info = tcm_loop_show_info, .proc_name = "tcm_loopback", .name = "TCM_Loopback", .queuecommand = tcm_loop_queuecommand, - .change_queue_depth = tcm_loop_change_queue_depth, - .change_queue_type = tcm_loop_change_queue_type, + .change_queue_depth = scsi_change_queue_depth, + .change_queue_type = scsi_change_queue_type, .eh_abort_handler = tcm_loop_abort_task, .eh_device_reset_handler = tcm_loop_device_reset, .eh_target_reset_handler = tcm_loop_target_reset, @@ -462,8 +396,9 @@ static struct scsi_host_template tcm_loop_driver_template = { .max_sectors = 0xFFFF, .use_clustering = DISABLE_CLUSTERING, .slave_alloc = tcm_loop_slave_alloc, - .slave_configure = tcm_loop_slave_configure, .module = THIS_MODULE, + .use_blk_tags = 1, + .track_queue_depth = 1, }; static int tcm_loop_driver_probe(struct device *dev) diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index fb87780929d2..75cbde1f7c5b 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -576,7 +576,7 @@ static inline int core_alua_state_standby( case SEND_DIAGNOSTIC: case READ_CAPACITY: return 0; - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: switch (cdb[1] & 0x1f) { case SAI_READ_CAPACITY_16: return 0; diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 9f93b8234095..4c261c33cf55 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -459,7 +459,7 @@ static int core_scsi3_pr_seq_non_holder( case ACCESS_CONTROL_OUT: case INQUIRY: case LOG_SENSE: - case READ_MEDIA_SERIAL_NUMBER: + case SERVICE_ACTION_IN_12: case REPORT_LUNS: case REQUEST_SENSE: case PERSISTENT_RESERVE_IN: diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index ebe62afb957d..8d171ff77e75 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -852,7 +852,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) size = READ_CAP_LEN; cmd->execute_cmd = sbc_emulate_readcapacity; break; - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: switch (cmd->t_task_cdb[1] & 0x1f) { case SAI_READ_CAPACITY_16: cmd->execute_cmd = sbc_emulate_readcapacity_16; diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 1ab0018271c5..ad09e51ffae4 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -50,15 +50,14 @@ struct cpufreq_cooling_device { unsigned int cpufreq_state; unsigned int cpufreq_val; struct cpumask allowed_cpus; + struct list_head node; }; static DEFINE_IDR(cpufreq_idr); static DEFINE_MUTEX(cooling_cpufreq_lock); static unsigned int cpufreq_dev_count; -/* notify_table passes value to the CPUFREQ_ADJUST callback function. */ -#define NOTIFY_INVALID NULL -static struct cpufreq_cooling_device *notify_device; +static LIST_HEAD(cpufreq_dev_list); /** * get_idr - function to get a unique id. @@ -287,15 +286,12 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, cpufreq_device->cpufreq_state = cooling_state; cpufreq_device->cpufreq_val = clip_freq; - notify_device = cpufreq_device; for_each_cpu(cpuid, mask) { if (is_cpufreq_valid(cpuid)) cpufreq_update_policy(cpuid); } - notify_device = NOTIFY_INVALID; - return 0; } @@ -316,21 +312,28 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, { struct cpufreq_policy *policy = data; unsigned long max_freq = 0; + struct cpufreq_cooling_device *cpufreq_dev; - if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID) + if (event != CPUFREQ_ADJUST) return 0; - if (cpumask_test_cpu(policy->cpu, ¬ify_device->allowed_cpus)) - max_freq = notify_device->cpufreq_val; - else - return 0; + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { + if (!cpumask_test_cpu(policy->cpu, + &cpufreq_dev->allowed_cpus)) + continue; + + if (!cpufreq_dev->cpufreq_val) + cpufreq_dev->cpufreq_val = get_cpu_frequency( + cpumask_any(&cpufreq_dev->allowed_cpus), + cpufreq_dev->cpufreq_state); - /* Never exceed user_policy.max */ - if (max_freq > policy->user_policy.max) - max_freq = policy->user_policy.max; + max_freq = cpufreq_dev->cpufreq_val; - if (policy->max != max_freq) - cpufreq_verify_within_limits(policy, 0, max_freq); + if (policy->max != max_freq) + cpufreq_verify_within_limits(policy, 0, max_freq); + } + mutex_unlock(&cooling_cpufreq_lock); return 0; } @@ -486,6 +489,7 @@ __cpufreq_cooling_register(struct device_node *np, cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); cpufreq_dev_count++; + list_add(&cpufreq_dev->node, &cpufreq_dev_list); mutex_unlock(&cooling_cpufreq_lock); @@ -549,6 +553,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) cpufreq_dev = cdev->devdata; mutex_lock(&cooling_cpufreq_lock); + list_del(&cpufreq_dev->node); cpufreq_dev_count--; /* Unregister the notifier for the last cpufreq cooling device */ diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c index 3f5ad25ddca8..b6be572704a4 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -417,13 +417,10 @@ void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) th_zone = sensor_conf->pzone_data; - if (th_zone->therm_dev) - thermal_zone_device_unregister(th_zone->therm_dev); + thermal_zone_device_unregister(th_zone->therm_dev); - for (i = 0; i < th_zone->cool_dev_size; i++) { - if (th_zone->cool_dev[i]) - cpufreq_cooling_unregister(th_zone->cool_dev[i]); - } + for (i = 0; i < th_zone->cool_dev_size; ++i) + cpufreq_cooling_unregister(th_zone->cool_dev[i]); dev_info(sensor_conf->dev, "Exynos: Kernel Thermal management unregistered\n"); diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index 90163b384660..d1ec5804c0bb 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -275,6 +275,7 @@ int st_thermal_unregister(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(st_thermal_unregister); +#ifdef CONFIG_PM_SLEEP static int st_thermal_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -305,6 +306,8 @@ static int st_thermal_resume(struct device *dev) return 0; } +#endif + SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); EXPORT_SYMBOL_GPL(st_thermal_pm_ops); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 649b784081c7..98f8bcaf3e7e 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -249,14 +249,14 @@ config SERIAL_SAMSUNG config SERIAL_SAMSUNG_UARTS_4 bool - depends on PLAT_SAMSUNG + depends on SERIAL_SAMSUNG default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442) help Internal node for the common case of 4 Samsung compatible UARTs config SERIAL_SAMSUNG_UARTS int - depends on PLAT_SAMSUNG + depends on SERIAL_SAMSUNG default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416 default 3 help diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 56982da4a9e9..bf355050eab6 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -240,32 +240,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int of_serial_suspend(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - serial8250_suspend_port(info->line); - if (info->clk) - clk_disable_unprepare(info->clk); - - return 0; -} - -static int of_serial_resume(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - if (info->clk) - clk_prepare_enable(info->clk); - - serial8250_resume_port(info->line); - - return 0; -} -#endif -static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); - /* * A few common types, add more as needed. */ @@ -297,7 +271,6 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, - .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 39b4081b632d..96fafed92b76 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -44,6 +44,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft Wireless Laser Mouse 6000 Receiver */ + { USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft LifeCam-VX700 v2.0 */ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 711b23019d54..df38e7ef4976 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -791,6 +791,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, trb = dwc->ep0_trb; + r = next_request(&ep0->request_list); + if (!r) + return; + status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); @@ -801,10 +805,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, return; } - r = next_request(&ep0->request_list); - if (!r) - return; - ur = &r->request; length = trb->size & DWC3_TRB_SIZE_MASK; diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 6cdb7a534f23..024f58475a94 100644 --- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -912,7 +912,7 @@ static int get_cmd_dir(const unsigned char *cdb) case INQUIRY: case MODE_SENSE: case MODE_SENSE_10: - case SERVICE_ACTION_IN: + case SERVICE_ACTION_IN_16: case MAINTENANCE_IN: case PERSISTENT_RESERVE_IN: case SECURITY_PROTOCOL_IN: diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 696160d48ae8..388cfd83b6b6 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -22,7 +22,6 @@ #include <linux/slab.h> -#include <linux/device.h> #include <asm/unaligned.h> #include "xhci.h" @@ -1149,9 +1148,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) * including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME * is enabled, so also enable remote wake here. */ - if (hcd->self.root_hub->do_remote_wakeup - && device_may_wakeup(hcd->self.controller)) { - + if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; t2 &= ~PORT_WKCONN_E; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 9a69b1f1b300..142b601f9563 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -281,7 +281,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (xhci->quirks & XHCI_COMP_MODE_QUIRK) pdev->no_d3cold = true; - return xhci_suspend(xhci); + return xhci_suspend(xhci, do_wakeup); } static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 3d78b0cd674b..646300cbe5f7 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -204,7 +204,15 @@ static int xhci_plat_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - return xhci_suspend(xhci); + /* + * xhci_suspend() needs `do_wakeup` to know whether host is allowed + * to do wakeup during suspend. Since xhci_plat_suspend is currently + * only designed for system suspend, device_may_wakeup() is enough + * to dertermine whether host is allowed to do wakeup. Need to + * reconsider this when xhci_plat_suspend enlarges its scope, e.g., + * also applies to runtime suspend. + */ + return xhci_suspend(xhci, device_may_wakeup(dev)); } static int xhci_plat_resume(struct device *dev) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index bc6fcbc16f61..06433aec81d7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1067,9 +1067,8 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, false); xhci_ring_cmd_db(xhci); } else { - /* Clear our internal halted state and restart the ring(s) */ + /* Clear our internal halted state */ xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } } @@ -1823,22 +1822,13 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, ep->stopped_td = td; return 0; } else { - if (trb_comp_code == COMP_STALL) { - /* The transfer is completed from the driver's - * perspective, but we need to issue a set dequeue - * command for this stalled endpoint to move the dequeue - * pointer past the TD. We can't do that here because - * the halt condition must be cleared first. Let the - * USB class driver clear the stall later. - */ - ep->stopped_td = td; - ep->stopped_stream = ep_ring->stream_id; - } else if (xhci_requires_manual_halt_cleanup(xhci, - ep_ctx, trb_comp_code)) { - /* Other types of errors halt the endpoint, but the - * class driver doesn't call usb_reset_endpoint() unless - * the error is -EPIPE. Clear the halted status in the - * xHCI hardware manually. + if (trb_comp_code == COMP_STALL || + xhci_requires_manual_halt_cleanup(xhci, ep_ctx, + trb_comp_code)) { + /* Issue a reset endpoint command to clear the host side + * halt, followed by a set dequeue command to move the + * dequeue pointer past the TD. + * The class driver clears the device side halt later. */ xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, ep_ring->stream_id, @@ -1958,9 +1948,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, else td->urb->actual_length = 0; - xhci_cleanup_halted_endpoint(xhci, - slot_id, ep_index, 0, td, event_trb); - return finish_td(xhci, td, event_trb, event, ep, status, true); + return finish_td(xhci, td, event_trb, event, ep, status, false); } /* * Did we transfer any data, despite the errors that might have @@ -2519,17 +2507,8 @@ cleanup: if (ret) { urb = td->urb; urb_priv = urb->hcpriv; - /* Leave the TD around for the reset endpoint function - * to use(but only if it's not a control endpoint, - * since we already queued the Set TR dequeue pointer - * command for stalled control endpoints). - */ - if (usb_endpoint_xfer_control(&urb->ep->desc) || - (trb_comp_code != COMP_STALL && - trb_comp_code != COMP_BABBLE)) - xhci_urb_free_priv(xhci, urb_priv); - else - kfree(urb_priv); + + xhci_urb_free_priv(xhci, urb_priv); usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); if ((urb->actual_length != urb->transfer_buffer_length && diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2a5d45b4cb15..033b46c470bd 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -35,6 +35,8 @@ #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" +#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E) + /* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */ static int link_quirk; module_param(link_quirk, int, S_IRUGO | S_IWUSR); @@ -851,13 +853,47 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) xhci_set_cmd_ring_deq(xhci); } +static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci) +{ + int port_index; + __le32 __iomem **port_array; + unsigned long flags; + u32 t1, t2; + + spin_lock_irqsave(&xhci->lock, flags); + + /* disble usb3 ports Wake bits*/ + port_index = xhci->num_usb3_ports; + port_array = xhci->usb3_ports; + while (port_index--) { + t1 = readl(port_array[port_index]); + t1 = xhci_port_state_to_neutral(t1); + t2 = t1 & ~PORT_WAKE_BITS; + if (t1 != t2) + writel(t2, port_array[port_index]); + } + + /* disble usb2 ports Wake bits*/ + port_index = xhci->num_usb2_ports; + port_array = xhci->usb2_ports; + while (port_index--) { + t1 = readl(port_array[port_index]); + t1 = xhci_port_state_to_neutral(t1); + t2 = t1 & ~PORT_WAKE_BITS; + if (t1 != t2) + writel(t2, port_array[port_index]); + } + + spin_unlock_irqrestore(&xhci->lock, flags); +} + /* * Stop HC (not bus-specific) * * This is called when the machine transition into S3/S4 mode. * */ -int xhci_suspend(struct xhci_hcd *xhci) +int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) { int rc = 0; unsigned int delay = XHCI_MAX_HALT_USEC; @@ -868,6 +904,10 @@ int xhci_suspend(struct xhci_hcd *xhci) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + /* Clear root port wake on bits if wakeup not allowed. */ + if (!do_wakeup) + xhci_disable_port_wake_on_bits(xhci); + /* Don't poll the roothubs on bus suspend. */ xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2912,68 +2952,33 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, } } -/* Deal with stalled endpoints. The core should have sent the control message - * to clear the halt condition. However, we need to make the xHCI hardware - * reset its sequence number, since a device will expect a sequence number of - * zero after the halt condition is cleared. +/* Called when clearing halted device. The core should have sent the control + * message to clear the device halt condition. The host side of the halt should + * already be cleared with a reset endpoint command issued when the STALL tx + * event was received. + * * Context: in_interrupt */ + void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; - struct usb_device *udev; - unsigned int ep_index; - unsigned long flags; - int ret; - struct xhci_virt_ep *virt_ep; - struct xhci_command *command; xhci = hcd_to_xhci(hcd); - udev = (struct usb_device *) ep->hcpriv; - /* Called with a root hub endpoint (or an endpoint that wasn't added - * with xhci_add_endpoint() - */ - if (!ep->hcpriv) - return; - ep_index = xhci_get_endpoint_index(&ep->desc); - virt_ep = &xhci->devs[udev->slot_id]->eps[ep_index]; - if (!virt_ep->stopped_td) { - xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Endpoint 0x%x not halted, refusing to reset.", - ep->desc.bEndpointAddress); - return; - } - if (usb_endpoint_xfer_control(&ep->desc)) { - xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Control endpoint stall already handled."); - return; - } - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); - if (!command) - return; - - xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Queueing reset endpoint command"); - spin_lock_irqsave(&xhci->lock, flags); - ret = xhci_queue_reset_ep(xhci, command, udev->slot_id, ep_index); /* - * Can't change the ring dequeue pointer until it's transitioned to the - * stopped state, which is only upon a successful reset endpoint - * command. Better hope that last command worked! + * We might need to implement the config ep cmd in xhci 4.8.1 note: + * The Reset Endpoint Command may only be issued to endpoints in the + * Halted state. If software wishes reset the Data Toggle or Sequence + * Number of an endpoint that isn't in the Halted state, then software + * may issue a Configure Endpoint Command with the Drop and Add bits set + * for the target endpoint. that is in the Stopped state. */ - if (!ret) { - xhci_cleanup_stalled_ring(xhci, udev, ep_index); - kfree(virt_ep->stopped_td); - xhci_ring_cmd_db(xhci); - } - virt_ep->stopped_td = NULL; - virt_ep->stopped_stream = 0; - spin_unlock_irqrestore(&xhci->lock, flags); - if (ret) - xhci_warn(xhci, "FIXME allocate a new ring segment\n"); + /* For now just print debug to follow the situation */ + xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n", + ep->desc.bEndpointAddress); } static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index df76d642e719..d745715a1e2f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1746,7 +1746,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)); #ifdef CONFIG_PM -int xhci_suspend(struct xhci_hcd *xhci); +int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); int xhci_resume(struct xhci_hcd *xhci, bool hibernated); #else #define xhci_suspend NULL diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index cfd009dc4018..6c4eb3cf5efd 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -120,6 +120,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ + { USB_DEVICE(0x10C4, 0x8875) }, /* CEL MeshConnect USB Stick */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0dad8ce5a609..1ebb351b9e9a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -470,6 +470,39 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_4701_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9300_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9301_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9302_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9303_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9304_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9305_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9306_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9307_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9308_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9309_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9310_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9311_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9312_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9313_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9314_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9315_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9316_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9317_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9318_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9319_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931F_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 6786b705ccf6..e52409c9be99 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -926,8 +926,8 @@ #define BAYER_CONTOUR_CABLE_PID 0x6001 /* - * The following are the values for the Matrix Orbital FTDI Range - * Anything in this range will use an FT232RL. + * Matrix Orbital Intelligent USB displays. + * http://www.matrixorbital.com */ #define MTXORB_VID 0x1B3D #define MTXORB_FTDI_RANGE_0100_PID 0x0100 @@ -1186,8 +1186,39 @@ #define MTXORB_FTDI_RANGE_01FD_PID 0x01FD #define MTXORB_FTDI_RANGE_01FE_PID 0x01FE #define MTXORB_FTDI_RANGE_01FF_PID 0x01FF - - +#define MTXORB_FTDI_RANGE_4701_PID 0x4701 +#define MTXORB_FTDI_RANGE_9300_PID 0x9300 +#define MTXORB_FTDI_RANGE_9301_PID 0x9301 +#define MTXORB_FTDI_RANGE_9302_PID 0x9302 +#define MTXORB_FTDI_RANGE_9303_PID 0x9303 +#define MTXORB_FTDI_RANGE_9304_PID 0x9304 +#define MTXORB_FTDI_RANGE_9305_PID 0x9305 +#define MTXORB_FTDI_RANGE_9306_PID 0x9306 +#define MTXORB_FTDI_RANGE_9307_PID 0x9307 +#define MTXORB_FTDI_RANGE_9308_PID 0x9308 +#define MTXORB_FTDI_RANGE_9309_PID 0x9309 +#define MTXORB_FTDI_RANGE_930A_PID 0x930A +#define MTXORB_FTDI_RANGE_930B_PID 0x930B +#define MTXORB_FTDI_RANGE_930C_PID 0x930C +#define MTXORB_FTDI_RANGE_930D_PID 0x930D +#define MTXORB_FTDI_RANGE_930E_PID 0x930E +#define MTXORB_FTDI_RANGE_930F_PID 0x930F +#define MTXORB_FTDI_RANGE_9310_PID 0x9310 +#define MTXORB_FTDI_RANGE_9311_PID 0x9311 +#define MTXORB_FTDI_RANGE_9312_PID 0x9312 +#define MTXORB_FTDI_RANGE_9313_PID 0x9313 +#define MTXORB_FTDI_RANGE_9314_PID 0x9314 +#define MTXORB_FTDI_RANGE_9315_PID 0x9315 +#define MTXORB_FTDI_RANGE_9316_PID 0x9316 +#define MTXORB_FTDI_RANGE_9317_PID 0x9317 +#define MTXORB_FTDI_RANGE_9318_PID 0x9318 +#define MTXORB_FTDI_RANGE_9319_PID 0x9319 +#define MTXORB_FTDI_RANGE_931A_PID 0x931A +#define MTXORB_FTDI_RANGE_931B_PID 0x931B +#define MTXORB_FTDI_RANGE_931C_PID 0x931C +#define MTXORB_FTDI_RANGE_931D_PID 0x931D +#define MTXORB_FTDI_RANGE_931E_PID 0x931E +#define MTXORB_FTDI_RANGE_931F_PID 0x931F /* * The Mobility Lab (TML) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 93cb7cebda62..077c714f1285 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -311,24 +311,30 @@ static void usa26_indat_callback(struct urb *urb) if ((data[0] & 0x80) == 0) { /* no errors on individual bytes, only possible overrun err */ - if (data[0] & RXERROR_OVERRUN) - err = TTY_OVERRUN; - else - err = 0; + if (data[0] & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } for (i = 1; i < urb->actual_length ; ++i) - tty_insert_flip_char(&port->port, data[i], err); + tty_insert_flip_char(&port->port, data[i], + TTY_NORMAL); } else { /* some bytes had errors, every byte has status */ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__); for (i = 0; i + 1 < urb->actual_length; i += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + int stat = data[i]; + int flag = TTY_NORMAL; + + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); } @@ -649,14 +655,19 @@ static void usa49_indat_callback(struct urb *urb) } else { /* some bytes had errors, every byte has status */ for (i = 0; i + 1 < urb->actual_length; i += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + int stat = data[i]; + int flag = TTY_NORMAL; + + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); } @@ -713,15 +724,19 @@ static void usa49wg_indat_callback(struct urb *urb) */ for (x = 0; x + 1 < len && i + 1 < urb->actual_length; x += 2) { - int stat = data[i], flag = 0; + int stat = data[i]; + int flag = TTY_NORMAL; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); i += 2; @@ -773,25 +788,31 @@ static void usa90_indat_callback(struct urb *urb) if ((data[0] & 0x80) == 0) { /* no errors on individual bytes, only possible overrun err*/ - if (data[0] & RXERROR_OVERRUN) - err = TTY_OVERRUN; - else - err = 0; + if (data[0] & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } for (i = 1; i < urb->actual_length ; ++i) tty_insert_flip_char(&port->port, - data[i], err); + data[i], TTY_NORMAL); } else { /* some bytes had errors, every byte has status */ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__); for (i = 0; i + 1 < urb->actual_length; i += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + int stat = data[i]; + int flag = TTY_NORMAL; + + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char( + &port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); } diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index a7fe664b6b7d..70a098de429f 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -490,10 +490,9 @@ static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr, if (*tty_flag == TTY_NORMAL) *tty_flag = TTY_FRAME; } - if (lsr & UART_LSR_OE){ + if (lsr & UART_LSR_OE) { port->icount.overrun++; - if (*tty_flag == TTY_NORMAL) - *tty_flag = TTY_OVERRUN; + tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); } } @@ -511,12 +510,8 @@ static void ssu100_process_read_urb(struct urb *urb) if ((len >= 4) && (packet[0] == 0x1b) && (packet[1] == 0x1b) && ((packet[2] == 0x00) || (packet[2] == 0x01))) { - if (packet[2] == 0x00) { + if (packet[2] == 0x00) ssu100_update_lsr(port, packet[3], &flag); - if (flag == TTY_OVERRUN) - tty_insert_flip_char(&port->port, 0, - TTY_OVERRUN); - } if (packet[2] == 0x01) ssu100_update_msr(port, packet[3]); diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index e08f64780e30..66a684a29938 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -164,10 +164,10 @@ void usb_stor_show_sense(const struct us_data *us, unsigned char asc, unsigned char ascq) { - const char *what, *keystr; + const char *what, *keystr, *fmt; keystr = scsi_sense_key_string(key); - what = scsi_extd_sense_format(asc, ascq); + what = scsi_extd_sense_format(asc, ascq, &fmt); if (keystr == NULL) keystr = "(Unknown Key)"; @@ -175,8 +175,10 @@ void usb_stor_show_sense(const struct us_data *us, what = "(unknown ASC/ASCQ)"; usb_stor_dbg(us, "%s: ", keystr); - US_DEBUGPX(what, ascq); - US_DEBUGPX("\n"); + if (fmt) + US_DEBUGPX("%s (%s%x)\n", what, fmt, ascq); + else + US_DEBUGPX("%s\n", what); } int usb_stor_dbg(const struct us_data *us, const char *fmt, ...) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 89b24349269e..4047edfb64e1 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -181,7 +181,7 @@ static int uas_get_tag(struct scsi_cmnd *cmnd) { int tag; - if (blk_rq_tagged(cmnd->request)) + if (cmnd->flags & SCMD_TAGGED) tag = cmnd->request->tag + 2; else tag = 1; @@ -799,8 +799,7 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_NO_REPORT_OPCODES) sdev->no_report_opcodes = 1; - scsi_set_tag_type(sdev, MSG_ORDERED_TAG); - scsi_activate_tcq(sdev, devinfo->qdepth - 2); + scsi_change_queue_depth(sdev, devinfo->qdepth - 2); return 0; } @@ -817,7 +816,6 @@ static struct scsi_host_template uas_host_template = { .sg_tablesize = SG_NONE, .cmd_per_lun = 1, /* until we override it */ .skip_settle_delay = 1, - .ordered_tag = 1, /* * The uas drivers expects tags not to be bigger than the maximum @@ -825,6 +823,7 @@ static struct scsi_host_template uas_host_template = { * allocator. */ .disable_blk_mq = true, + .use_blk_tags = 1, }; #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 2fefaf923e4a..18a283d6de1c 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -103,3 +103,10 @@ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, "VL711", USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), + +/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ +UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999, + "Hitachi", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 8d03924749b8..efb09046a8cf 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -168,7 +168,6 @@ if BACKLIGHT_CLASS_DEVICE config BACKLIGHT_ATMEL_LCDC bool "Atmel LCDC Contrast-as-Backlight control" depends on FB_ATMEL - default y if MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK help This provides a backlight control internal to the Atmel LCDC driver. If the LCD "contrast control" on your board is wired diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index c7bf606a8706..c78bfd1d1b34 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -999,23 +999,6 @@ config FB_ATMEL help This enables support for the AT91/AT32 LCD Controller. -config FB_INTSRAM - bool "Frame Buffer in internal SRAM" - depends on FB_ATMEL && ARCH_AT91SAM9261 - help - Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want - to let frame buffer in external SDRAM. - -config FB_ATMEL_STN - bool "Use a STN display with AT91/AT32 LCD Controller" - depends on FB_ATMEL && (MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK) - default n - help - Say Y if you want to connect a STN LCD display to the AT91/AT32 LCD - Controller. Say N if you want to connect a TFT. - - If unsure, say N. - config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 9900e8ec7393..03321d6a2684 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -72,11 +72,18 @@ struct hdq_data { static int omap_hdq_probe(struct platform_device *pdev); static int omap_hdq_remove(struct platform_device *pdev); +static struct of_device_id omap_hdq_dt_ids[] = { + { .compatible = "ti,omap3-1w" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_hdq_dt_ids); + static struct platform_driver omap_hdq_driver = { .probe = omap_hdq_probe, .remove = omap_hdq_remove, .driver = { .name = "omap_hdq", + .of_match_table = omap_hdq_dt_ids, }, }; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d0107d424ee4..08f41add1461 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -154,14 +154,14 @@ config ARM_SP805_WATCHDOG config AT91RM9200_WATCHDOG tristate "AT91RM9200 watchdog" - depends on ARCH_AT91RM9200 + depends on SOC_AT91RM9200 help Watchdog timer embedded into AT91RM9200 chips. This will reboot your system when the timeout is reached. config AT91SAM9X_WATCHDOG tristate "AT91SAM9X / AT91CAP9 watchdog" - depends on ARCH_AT91 && !ARCH_AT91RM9200 + depends on ARCH_AT91 select WATCHDOG_CORE help Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 8532c3e2aea7..1626dc66e763 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -161,7 +161,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { static const struct s3c2410_wdt_variant drv_data_exynos7 = { .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, - .mask_bit = 0, + .mask_bit = 23, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 23, /* A57 WDTRESET */ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, diff --git a/drivers/xen/efi.c b/drivers/xen/efi.c index 1f850c97482f..f745db270171 100644 --- a/drivers/xen/efi.c +++ b/drivers/xen/efi.c @@ -294,6 +294,7 @@ static const struct efi efi_xen __initconst = { .acpi = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, + .smbios3 = EFI_INVALID_TABLE_ADDR, .sal_systab = EFI_INVALID_TABLE_ADDR, .boot_info = EFI_INVALID_TABLE_ADDR, .hcdp = EFI_INVALID_TABLE_ADDR, diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 3e32146472a5..50610a6acf3d 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -274,10 +274,6 @@ static void scsiback_print_status(char *sense_buffer, int errors, tpg->tport->tport_name, pending_req->v2p->lun, pending_req->cmnd[0], status_byte(errors), msg_byte(errors), host_byte(errors), driver_byte(errors)); - - if (CHECK_CONDITION & status_byte(errors)) - __scsi_print_sense("xen-pvscsi", sense_buffer, - SCSI_SENSE_BUFFERSIZE); } static void scsiback_fast_flush_area(struct vscsibk_pend *req) |