Merge branch 'misc' into for-linus
This commit is contained in:
commit
3eff4c7828
|
@ -6,17 +6,15 @@ NCR53c400 extensions (c) 1994,1995,1996 Kevin Lentin
|
|||
This file documents the NCR53c400 extensions by Kevin Lentin and some
|
||||
enhancements to the NCR5380 core.
|
||||
|
||||
This driver supports both NCR5380 and NCR53c400 cards in port or memory
|
||||
mapped modes. Currently this driver can only support one of those mapping
|
||||
modes at a time but it does support both of these chips at the same time.
|
||||
The next release of this driver will support port & memory mapped cards at
|
||||
the same time. It should be able to handle multiple different cards in the
|
||||
same machine.
|
||||
This driver supports NCR5380 and NCR53c400 and compatible cards in port or
|
||||
memory mapped modes.
|
||||
|
||||
The drivers/scsi/Makefile has an override in it for the most common
|
||||
NCR53c400 card, the Trantor T130B in its default configuration:
|
||||
Port: 0x350
|
||||
IRQ : 5
|
||||
Use of an interrupt is recommended, if supported by the board, as this will
|
||||
allow targets to disconnect and thereby improve SCSI bus utilization.
|
||||
|
||||
If the irq parameter is 254 or is omitted entirely, the driver will probe
|
||||
for the correct IRQ line automatically. If the irq parameter is 0 or 255
|
||||
then no IRQ will be used.
|
||||
|
||||
The NCR53c400 does not support DMA but it does have Pseudo-DMA which is
|
||||
supported by the driver.
|
||||
|
@ -47,22 +45,24 @@ These old-style parameters can support only one card:
|
|||
dtc_3181e=1 to set up for a Domex Technology Corp 3181E board
|
||||
hp_c2502=1 to set up for a Hewlett Packard C2502 board
|
||||
|
||||
e.g.
|
||||
OLD: modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
|
||||
NEW: modprobe g_NCR5380 irq=5 base=0x350 card=0
|
||||
for a port mapped NCR5380 board or
|
||||
E.g. Trantor T130B in its default configuration:
|
||||
modprobe g_NCR5380 irq=5 base=0x350 card=1
|
||||
or alternatively, using the old syntax,
|
||||
modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_53c400=1
|
||||
|
||||
OLD: modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
|
||||
NEW: modprobe g_NCR5380 irq=255 base=0xc8000 card=1
|
||||
for a memory mapped NCR53C400 board with interrupts disabled or
|
||||
E.g. a port mapped NCR5380 board, driver to probe for IRQ:
|
||||
modprobe g_NCR5380 base=0x350 card=0
|
||||
or alternatively,
|
||||
modprobe g_NCR5380 ncr_addr=0x350 ncr_5380=1
|
||||
|
||||
NEW: modprobe g_NCR5380 irq=0,7 base=0x240,0x300 card=3,4
|
||||
for two cards: DTC3181 (in non-PnP mode) at 0x240 with no IRQ
|
||||
and HP C2502 at 0x300 with IRQ 7
|
||||
|
||||
(255 should be specified for no or DMA interrupt, 254 to autoprobe for an
|
||||
IRQ line if overridden on the command line.)
|
||||
E.g. a memory mapped NCR53C400 board with no IRQ:
|
||||
modprobe g_NCR5380 irq=255 base=0xc8000 card=1
|
||||
or alternatively,
|
||||
modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
|
||||
|
||||
E.g. two cards, DTC3181 (in non-PnP mode) at 0x240 with no IRQ
|
||||
and HP C2502 at 0x300 with IRQ 7:
|
||||
modprobe g_NCR5380 irq=0,7 base=0x240,0x300 card=3,4
|
||||
|
||||
Kevin Lentin
|
||||
K.Lentin@cs.monash.edu.au
|
||||
|
|
|
@ -97,9 +97,6 @@
|
|||
* and macros and include this file in your driver.
|
||||
*
|
||||
* These macros control options :
|
||||
* AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be
|
||||
* defined.
|
||||
*
|
||||
* AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
|
||||
* for commands that return with a CHECK CONDITION status.
|
||||
*
|
||||
|
@ -127,9 +124,7 @@
|
|||
* NCR5380_dma_residual - residual byte count
|
||||
*
|
||||
* 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.
|
||||
* after setting the appropriate host specific fields and ID.
|
||||
*/
|
||||
|
||||
#ifndef NCR5380_io_delay
|
||||
|
@ -351,76 +346,6 @@ static void NCR5380_print_phase(struct Scsi_Host *instance)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int probe_irq;
|
||||
|
||||
/**
|
||||
* probe_intr - helper for IRQ autoprobe
|
||||
* @irq: interrupt number
|
||||
* @dev_id: unused
|
||||
* @regs: unused
|
||||
*
|
||||
* Set a flag to indicate the IRQ in question was received. This is
|
||||
* used by the IRQ probe code.
|
||||
*/
|
||||
|
||||
static irqreturn_t probe_intr(int irq, void *dev_id)
|
||||
{
|
||||
probe_irq = irq;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* NCR5380_probe_irq - find the IRQ of an NCR5380
|
||||
* @instance: NCR5380 controller
|
||||
* @possible: bitmask of ISA IRQ lines
|
||||
*
|
||||
* Autoprobe for the IRQ line used by the NCR5380 by triggering an IRQ
|
||||
* and then looking to see what interrupt actually turned up.
|
||||
*/
|
||||
|
||||
static int __maybe_unused NCR5380_probe_irq(struct Scsi_Host *instance,
|
||||
int possible)
|
||||
{
|
||||
struct NCR5380_hostdata *hostdata = shost_priv(instance);
|
||||
unsigned long timeout;
|
||||
int trying_irqs, i, mask;
|
||||
|
||||
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 + msecs_to_jiffies(250);
|
||||
probe_irq = NO_IRQ;
|
||||
|
||||
/*
|
||||
* A interrupt is triggered whenever BSY = false, SEL = true
|
||||
* and a bit set in the SELECT_ENABLE_REG is asserted on the
|
||||
* SCSI bus.
|
||||
*
|
||||
* Note that the bus is only driven when the phase control signals
|
||||
* (I/O, C/D, and MSG) match those in the TCR, so we must reset that
|
||||
* to zero.
|
||||
*/
|
||||
|
||||
NCR5380_write(TARGET_COMMAND_REG, 0);
|
||||
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
|
||||
NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
|
||||
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL);
|
||||
|
||||
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 = 1, mask = 2; i < 16; ++i, mask <<= 1)
|
||||
if (trying_irqs & mask)
|
||||
free_irq(i, NULL);
|
||||
|
||||
return probe_irq;
|
||||
}
|
||||
|
||||
/**
|
||||
* NCR58380_info - report driver and host information
|
||||
* @instance: relevant scsi host instance
|
||||
|
|
|
@ -199,16 +199,6 @@
|
|||
|
||||
#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)
|
||||
|
||||
/*
|
||||
* These are "special" values for the irq and dma_channel fields of the
|
||||
* Scsi_Host structure
|
||||
*/
|
||||
|
||||
#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
|
||||
|
@ -290,7 +280,6 @@ static void NCR5380_print(struct Scsi_Host *instance);
|
|||
#define NCR5380_dprint_phase(flg, arg) do {} while (0)
|
||||
#endif
|
||||
|
||||
static int NCR5380_probe_irq(struct Scsi_Host *instance, int possible);
|
||||
static int NCR5380_init(struct Scsi_Host *instance, int flags);
|
||||
static int NCR5380_maybe_reset_bus(struct Scsi_Host *);
|
||||
static void NCR5380_exit(struct Scsi_Host *instance);
|
||||
|
|
|
@ -160,7 +160,6 @@ static const struct pci_device_id aac_pci_tbl[] = {
|
|||
{ 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Series 6 (Tupelo) */
|
||||
{ 0x9005, 0x028c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 63 }, /* Adaptec PMC Series 7 (Denali) */
|
||||
{ 0x9005, 0x028d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 64 }, /* Adaptec PMC Series 8 */
|
||||
{ 0x9005, 0x028f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 65 }, /* Adaptec PMC Series 9 */
|
||||
{ 0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, aac_pci_tbl);
|
||||
|
@ -239,7 +238,6 @@ static struct aac_driver_ident aac_drivers[] = {
|
|||
{ aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 6 (Tupelo) */
|
||||
{ aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 7 (Denali) */
|
||||
{ aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 8 */
|
||||
{ aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC } /* Adaptec PMC Series 9 */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#define MAX_CARDS 8
|
||||
|
||||
/* old-style parameters for compatibility */
|
||||
static int ncr_irq;
|
||||
static int ncr_irq = -1;
|
||||
static int ncr_addr;
|
||||
static int ncr_5380;
|
||||
static int ncr_53c400;
|
||||
|
@ -52,9 +52,9 @@ module_param(ncr_53c400a, int, 0);
|
|||
module_param(dtc_3181e, int, 0);
|
||||
module_param(hp_c2502, int, 0);
|
||||
|
||||
static int irq[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static int irq[] = { -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (0=none, 254=auto [default])");
|
||||
|
||||
static int base[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
module_param_array(base, int, NULL, 0);
|
||||
|
@ -67,6 +67,56 @@ MODULE_PARM_DESC(card, "card type (0=NCR5380, 1=NCR53C400, 2=NCR53C400A, 3=DTC31
|
|||
MODULE_ALIAS("g_NCR5380_mmio");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static void g_NCR5380_trigger_irq(struct Scsi_Host *instance)
|
||||
{
|
||||
struct NCR5380_hostdata *hostdata = shost_priv(instance);
|
||||
|
||||
/*
|
||||
* An interrupt is triggered whenever BSY = false, SEL = true
|
||||
* and a bit set in the SELECT_ENABLE_REG is asserted on the
|
||||
* SCSI bus.
|
||||
*
|
||||
* Note that the bus is only driven when the phase control signals
|
||||
* (I/O, C/D, and MSG) match those in the TCR.
|
||||
*/
|
||||
NCR5380_write(TARGET_COMMAND_REG,
|
||||
PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
|
||||
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
|
||||
NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
|
||||
NCR5380_write(INITIATOR_COMMAND_REG,
|
||||
ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL);
|
||||
|
||||
msleep(1);
|
||||
|
||||
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
|
||||
NCR5380_write(SELECT_ENABLE_REG, 0);
|
||||
NCR5380_write(TARGET_COMMAND_REG, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_NCR5380_probe_irq - find the IRQ of a NCR5380 or equivalent
|
||||
* @instance: SCSI host instance
|
||||
*
|
||||
* Autoprobe for the IRQ line used by the card by triggering an IRQ
|
||||
* and then looking to see what interrupt actually turned up.
|
||||
*/
|
||||
|
||||
static int g_NCR5380_probe_irq(struct Scsi_Host *instance)
|
||||
{
|
||||
struct NCR5380_hostdata *hostdata = shost_priv(instance);
|
||||
int irq_mask, irq;
|
||||
|
||||
NCR5380_read(RESET_PARITY_INTERRUPT_REG);
|
||||
irq_mask = probe_irq_on();
|
||||
g_NCR5380_trigger_irq(instance);
|
||||
irq = probe_irq_off(irq_mask);
|
||||
NCR5380_read(RESET_PARITY_INTERRUPT_REG);
|
||||
|
||||
if (irq <= 0)
|
||||
return NO_IRQ;
|
||||
return irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure I/O address of 53C400A or DTC436 by writing magic numbers
|
||||
* to ports 0x779 and 0x379.
|
||||
|
@ -81,14 +131,33 @@ static void magic_configure(int idx, u8 irq, u8 magic[])
|
|||
outb(magic[3], 0x379);
|
||||
outb(magic[4], 0x379);
|
||||
|
||||
/* allowed IRQs for HP C2502 */
|
||||
if (irq != 2 && irq != 3 && irq != 4 && irq != 5 && irq != 7)
|
||||
irq = 0;
|
||||
if (irq == 9)
|
||||
irq = 2;
|
||||
|
||||
if (idx >= 0 && idx <= 7)
|
||||
cfg = 0x80 | idx | (irq << 4);
|
||||
outb(cfg, 0x379);
|
||||
}
|
||||
|
||||
static irqreturn_t legacy_empty_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int legacy_find_free_irq(int *irq_table)
|
||||
{
|
||||
while (*irq_table != -1) {
|
||||
if (!request_irq(*irq_table, legacy_empty_irq_handler,
|
||||
IRQF_PROBE_SHARED, "Test IRQ",
|
||||
(void *)irq_table)) {
|
||||
free_irq(*irq_table, (void *) irq_table);
|
||||
return *irq_table;
|
||||
}
|
||||
irq_table++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned int ncr_53c400a_ports[] = {
|
||||
0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
|
||||
};
|
||||
|
@ -101,6 +170,9 @@ static u8 ncr_53c400a_magic[] = { /* 53C400A & DTC436 */
|
|||
static u8 hp_c2502_magic[] = { /* HP C2502 */
|
||||
0x0f, 0x22, 0xf0, 0x20, 0x80
|
||||
};
|
||||
static int hp_c2502_irqs[] = {
|
||||
9, 5, 7, 3, 4, -1
|
||||
};
|
||||
|
||||
static int generic_NCR5380_init_one(struct scsi_host_template *tpnt,
|
||||
struct device *pdev, int base, int irq, int board)
|
||||
|
@ -248,6 +320,13 @@ static int generic_NCR5380_init_one(struct scsi_host_template *tpnt,
|
|||
}
|
||||
}
|
||||
|
||||
/* Check for vacant slot */
|
||||
NCR5380_write(MODE_REG, 0);
|
||||
if (NCR5380_read(MODE_REG) != 0) {
|
||||
ret = -ENODEV;
|
||||
goto out_unregister;
|
||||
}
|
||||
|
||||
ret = NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP);
|
||||
if (ret)
|
||||
goto out_unregister;
|
||||
|
@ -262,29 +341,57 @@ static int generic_NCR5380_init_one(struct scsi_host_template *tpnt,
|
|||
|
||||
NCR5380_maybe_reset_bus(instance);
|
||||
|
||||
if (irq != IRQ_AUTO)
|
||||
instance->irq = irq;
|
||||
else
|
||||
instance->irq = NCR5380_probe_irq(instance, 0xffff);
|
||||
|
||||
/* Compatibility with documented NCR5380 kernel parameters */
|
||||
if (instance->irq == 255)
|
||||
instance->irq = NO_IRQ;
|
||||
if (irq == 255 || irq == 0)
|
||||
irq = NO_IRQ;
|
||||
else if (irq == -1)
|
||||
irq = IRQ_AUTO;
|
||||
|
||||
if (instance->irq != NO_IRQ) {
|
||||
/* set IRQ for HP C2502 */
|
||||
if (board == BOARD_HP_C2502)
|
||||
magic_configure(port_idx, instance->irq, magic);
|
||||
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 = NO_IRQ;
|
||||
if (board == BOARD_HP_C2502) {
|
||||
int *irq_table = hp_c2502_irqs;
|
||||
int board_irq = -1;
|
||||
|
||||
switch (irq) {
|
||||
case NO_IRQ:
|
||||
board_irq = 0;
|
||||
break;
|
||||
case IRQ_AUTO:
|
||||
board_irq = legacy_find_free_irq(irq_table);
|
||||
break;
|
||||
default:
|
||||
while (*irq_table != -1)
|
||||
if (*irq_table++ == irq)
|
||||
board_irq = irq;
|
||||
}
|
||||
|
||||
if (board_irq <= 0) {
|
||||
board_irq = 0;
|
||||
irq = NO_IRQ;
|
||||
}
|
||||
|
||||
magic_configure(port_idx, board_irq, magic);
|
||||
}
|
||||
|
||||
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);
|
||||
if (irq == IRQ_AUTO) {
|
||||
instance->irq = g_NCR5380_probe_irq(instance);
|
||||
if (instance->irq == NO_IRQ)
|
||||
shost_printk(KERN_INFO, instance, "no irq detected\n");
|
||||
} else {
|
||||
instance->irq = irq;
|
||||
if (instance->irq == NO_IRQ)
|
||||
shost_printk(KERN_INFO, instance, "no irq provided\n");
|
||||
}
|
||||
|
||||
if (instance->irq != NO_IRQ) {
|
||||
if (request_irq(instance->irq, generic_NCR5380_intr,
|
||||
0, "NCR5380", instance)) {
|
||||
instance->irq = NO_IRQ;
|
||||
shost_printk(KERN_INFO, instance,
|
||||
"irq %d denied\n", instance->irq);
|
||||
} else {
|
||||
shost_printk(KERN_INFO, instance,
|
||||
"irq %d acquired\n", instance->irq);
|
||||
}
|
||||
}
|
||||
|
||||
ret = scsi_add_host(instance, pdev);
|
||||
|
@ -597,7 +704,7 @@ static int __init generic_NCR5380_init(void)
|
|||
int ret = 0;
|
||||
|
||||
/* compatibility with old-style parameters */
|
||||
if (irq[0] == 0 && base[0] == 0 && card[0] == -1) {
|
||||
if (irq[0] == -1 && base[0] == 0 && card[0] == -1) {
|
||||
irq[0] = ncr_irq;
|
||||
base[0] = ncr_addr;
|
||||
if (ncr_5380)
|
||||
|
|
|
@ -51,4 +51,6 @@
|
|||
#define BOARD_DTC3181E 3
|
||||
#define BOARD_HP_C2502 4
|
||||
|
||||
#define IRQ_AUTO 254
|
||||
|
||||
#endif /* GENERIC_NCR5380_H */
|
||||
|
|
|
@ -95,6 +95,7 @@ static int fast_fail = 1;
|
|||
static int client_reserve = 1;
|
||||
static char partition_name[97] = "UNKNOWN";
|
||||
static unsigned int partition_number = -1;
|
||||
static LIST_HEAD(ibmvscsi_head);
|
||||
|
||||
static struct scsi_transport_template *ibmvscsi_transport_template;
|
||||
|
||||
|
@ -232,6 +233,7 @@ static void ibmvscsi_task(void *data)
|
|||
while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) {
|
||||
ibmvscsi_handle_crq(crq, hostdata);
|
||||
crq->valid = VIOSRP_CRQ_FREE;
|
||||
wmb();
|
||||
}
|
||||
|
||||
vio_enable_interrupts(vdev);
|
||||
|
@ -240,6 +242,7 @@ static void ibmvscsi_task(void *data)
|
|||
vio_disable_interrupts(vdev);
|
||||
ibmvscsi_handle_crq(crq, hostdata);
|
||||
crq->valid = VIOSRP_CRQ_FREE;
|
||||
wmb();
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
|
@ -992,7 +995,7 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct)
|
|||
if (unlikely(rsp->opcode != SRP_RSP)) {
|
||||
if (printk_ratelimit())
|
||||
dev_warn(evt_struct->hostdata->dev,
|
||||
"bad SRP RSP type %d\n", rsp->opcode);
|
||||
"bad SRP RSP type %#02x\n", rsp->opcode);
|
||||
}
|
||||
|
||||
if (cmnd) {
|
||||
|
@ -2270,6 +2273,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
|
|||
}
|
||||
|
||||
dev_set_drvdata(&vdev->dev, hostdata);
|
||||
list_add_tail(&hostdata->host_list, &ibmvscsi_head);
|
||||
return 0;
|
||||
|
||||
add_srp_port_failed:
|
||||
|
@ -2291,6 +2295,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
|
|||
static int ibmvscsi_remove(struct vio_dev *vdev)
|
||||
{
|
||||
struct ibmvscsi_host_data *hostdata = dev_get_drvdata(&vdev->dev);
|
||||
list_del(&hostdata->host_list);
|
||||
unmap_persist_bufs(hostdata);
|
||||
release_event_pool(&hostdata->pool, hostdata);
|
||||
ibmvscsi_release_crq_queue(&hostdata->queue, hostdata,
|
||||
|
|
|
@ -90,6 +90,7 @@ struct event_pool {
|
|||
|
||||
/* all driver data associated with a host adapter */
|
||||
struct ibmvscsi_host_data {
|
||||
struct list_head host_list;
|
||||
atomic_t request_limit;
|
||||
int client_migrated;
|
||||
int reset_crq;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "unipro.h"
|
||||
#include "ufs-qcom.h"
|
||||
#include "ufshci.h"
|
||||
#include "ufs_quirks.h"
|
||||
#define UFS_QCOM_DEFAULT_DBG_PRINT_EN \
|
||||
(UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
|
||||
|
||||
|
@ -1031,6 +1032,34 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_quirk_host_pa_saveconfigtime(struct ufs_hba *hba)
|
||||
{
|
||||
int err;
|
||||
u32 pa_vs_config_reg1;
|
||||
|
||||
err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
|
||||
&pa_vs_config_reg1);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Allow extension of MSB bits of PA_SaveConfigTime attribute */
|
||||
err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
|
||||
(pa_vs_config_reg1 | (1 << 12)));
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME)
|
||||
err = ufs_qcom_quirk_host_pa_saveconfigtime(hba);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
@ -1194,7 +1223,16 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
|||
*/
|
||||
host->generic_phy = devm_phy_get(dev, "ufsphy");
|
||||
|
||||
if (IS_ERR(host->generic_phy)) {
|
||||
if (host->generic_phy == ERR_PTR(-EPROBE_DEFER)) {
|
||||
/*
|
||||
* UFS driver might be probed before the phy driver does.
|
||||
* In that case we would like to return EPROBE_DEFER code.
|
||||
*/
|
||||
err = -EPROBE_DEFER;
|
||||
dev_warn(dev, "%s: required phy device. hasn't probed yet. err = %d\n",
|
||||
__func__, err);
|
||||
goto out_variant_clear;
|
||||
} else if (IS_ERR(host->generic_phy)) {
|
||||
err = PTR_ERR(host->generic_phy);
|
||||
dev_err(dev, "%s: PHY get failed %d\n", __func__, err);
|
||||
goto out_variant_clear;
|
||||
|
@ -1432,7 +1470,8 @@ static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
|
|||
reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM);
|
||||
print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv);
|
||||
|
||||
ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1);
|
||||
/* clear bit 17 - UTP_DBG_RAMS_EN */
|
||||
ufshcd_rmwl(hba, UFS_BIT(17), 0, REG_UFS_CFG1);
|
||||
|
||||
reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM);
|
||||
print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv);
|
||||
|
@ -1609,6 +1648,7 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
|
|||
.hce_enable_notify = ufs_qcom_hce_enable_notify,
|
||||
.link_startup_notify = ufs_qcom_link_startup_notify,
|
||||
.pwr_change_notify = ufs_qcom_pwr_change_notify,
|
||||
.apply_dev_quirks = ufs_qcom_apply_dev_quirks,
|
||||
.suspend = ufs_qcom_suspend,
|
||||
.resume = ufs_qcom_resume,
|
||||
.dbg_register_dump = ufs_qcom_dump_dbg_regs,
|
||||
|
|
|
@ -142,6 +142,7 @@ enum ufs_qcom_phy_init_type {
|
|||
UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
|
||||
|
||||
/* QUniPro Vendor specific attributes */
|
||||
#define PA_VS_CONFIG_REG1 0x9000
|
||||
#define DME_VS_CORE_CLK_CTRL 0xD002
|
||||
/* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */
|
||||
#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8)
|
||||
|
|
|
@ -134,29 +134,17 @@ struct ufs_dev_fix {
|
|||
*/
|
||||
#define UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE (1 << 7)
|
||||
|
||||
/*
|
||||
* The max. value PA_SaveConfigTime is 250 (10us) but this is not enough for
|
||||
* some vendors.
|
||||
* Gear switch from PWM to HS may fail even with this max. PA_SaveConfigTime.
|
||||
* Gear switch can be issued by host controller as an error recovery and any
|
||||
* software delay will not help on this case so we need to increase
|
||||
* PA_SaveConfigTime to >32us as per vendor recommendation.
|
||||
*/
|
||||
#define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME (1 << 8)
|
||||
|
||||
struct ufs_hba;
|
||||
void ufs_advertise_fixup_device(struct ufs_hba *hba);
|
||||
|
||||
static struct ufs_dev_fix ufs_fixups[] = {
|
||||
/* UFS cards deviations table */
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_NO_FASTAUTO),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
|
||||
UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
|
||||
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
|
||||
UFS_DEVICE_QUIRK_PA_TACTIVATE),
|
||||
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
|
||||
UFS_DEVICE_QUIRK_PA_TACTIVATE),
|
||||
UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
|
||||
|
||||
END_FIX
|
||||
};
|
||||
#endif /* UFS_QUIRKS_H_ */
|
||||
|
|
|
@ -185,6 +185,30 @@ ufs_get_pm_lvl_to_link_pwr_state(enum ufs_pm_level lvl)
|
|||
return ufs_pm_lvl_states[lvl].link_state;
|
||||
}
|
||||
|
||||
static struct ufs_dev_fix ufs_fixups[] = {
|
||||
/* UFS cards deviations table */
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_NO_FASTAUTO),
|
||||
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
|
||||
UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
|
||||
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
|
||||
UFS_DEVICE_QUIRK_PA_TACTIVATE),
|
||||
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
|
||||
UFS_DEVICE_QUIRK_PA_TACTIVATE),
|
||||
UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
|
||||
UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL,
|
||||
UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME),
|
||||
|
||||
END_FIX
|
||||
};
|
||||
|
||||
static void ufshcd_tmc_handler(struct ufs_hba *hba);
|
||||
static void ufshcd_async_scan(void *data, async_cookie_t cookie);
|
||||
static int ufshcd_reset_and_restore(struct ufs_hba *hba);
|
||||
|
@ -288,10 +312,24 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
|
|||
*/
|
||||
static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
|
||||
{
|
||||
if (hba->ufs_version == UFSHCI_VERSION_10)
|
||||
return INTERRUPT_MASK_ALL_VER_10;
|
||||
else
|
||||
return INTERRUPT_MASK_ALL_VER_11;
|
||||
u32 intr_mask = 0;
|
||||
|
||||
switch (hba->ufs_version) {
|
||||
case UFSHCI_VERSION_10:
|
||||
intr_mask = INTERRUPT_MASK_ALL_VER_10;
|
||||
break;
|
||||
/* allow fall through */
|
||||
case UFSHCI_VERSION_11:
|
||||
case UFSHCI_VERSION_20:
|
||||
intr_mask = INTERRUPT_MASK_ALL_VER_11;
|
||||
break;
|
||||
/* allow fall through */
|
||||
case UFSHCI_VERSION_21:
|
||||
default:
|
||||
intr_mask = INTERRUPT_MASK_ALL_VER_21;
|
||||
}
|
||||
|
||||
return intr_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5199,6 +5237,8 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba)
|
|||
|
||||
if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE)
|
||||
ufshcd_quirk_tune_host_pa_tactivate(hba);
|
||||
|
||||
ufshcd_vops_apply_dev_quirks(hba);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6667,6 +6707,13 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
|||
/* Get UFS version supported by the controller */
|
||||
hba->ufs_version = ufshcd_get_ufs_version(hba);
|
||||
|
||||
if ((hba->ufs_version != UFSHCI_VERSION_10) &&
|
||||
(hba->ufs_version != UFSHCI_VERSION_11) &&
|
||||
(hba->ufs_version != UFSHCI_VERSION_20) &&
|
||||
(hba->ufs_version != UFSHCI_VERSION_21))
|
||||
dev_err(hba->dev, "invalid UFS version 0x%x\n",
|
||||
hba->ufs_version);
|
||||
|
||||
/* Get Interrupt bit mask per version */
|
||||
hba->intr_mask = ufshcd_get_intr_mask(hba);
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ struct ufs_pwr_mode_info {
|
|||
* @setup_task_mgmt: called before any task management request is issued
|
||||
* to set some things
|
||||
* @hibern8_notify: called around hibern8 enter/exit
|
||||
* to configure some things
|
||||
* @apply_dev_quirks: called to apply device specific quirks
|
||||
* @suspend: called during host controller PM callback
|
||||
* @resume: called during host controller PM callback
|
||||
* @dbg_register_dump: used to dump controller debug information
|
||||
|
@ -293,7 +293,8 @@ struct ufs_hba_variant_ops {
|
|||
void (*setup_xfer_req)(struct ufs_hba *, int, bool);
|
||||
void (*setup_task_mgmt)(struct ufs_hba *, int, u8);
|
||||
void (*hibern8_notify)(struct ufs_hba *, enum uic_cmd_dme,
|
||||
enum ufs_notify_change_status);
|
||||
enum ufs_notify_change_status);
|
||||
int (*apply_dev_quirks)(struct ufs_hba *);
|
||||
int (*suspend)(struct ufs_hba *, enum ufs_pm_op);
|
||||
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
|
||||
void (*dbg_register_dump)(struct ufs_hba *hba);
|
||||
|
@ -839,6 +840,13 @@ static inline void ufshcd_vops_hibern8_notify(struct ufs_hba *hba,
|
|||
return hba->vops->hibern8_notify(hba, cmd, status);
|
||||
}
|
||||
|
||||
static inline int ufshcd_vops_apply_dev_quirks(struct ufs_hba *hba)
|
||||
{
|
||||
if (hba->vops && hba->vops->apply_dev_quirks)
|
||||
return hba->vops->apply_dev_quirks(hba);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op)
|
||||
{
|
||||
if (hba->vops && hba->vops->suspend)
|
||||
|
|
|
@ -72,6 +72,10 @@ enum {
|
|||
REG_UIC_COMMAND_ARG_1 = 0x94,
|
||||
REG_UIC_COMMAND_ARG_2 = 0x98,
|
||||
REG_UIC_COMMAND_ARG_3 = 0x9C,
|
||||
REG_UFS_CCAP = 0x100,
|
||||
REG_UFS_CRYPTOCAP = 0x104,
|
||||
|
||||
UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400,
|
||||
};
|
||||
|
||||
/* Controller capability masks */
|
||||
|
@ -275,6 +279,9 @@ enum {
|
|||
|
||||
/* Interrupt disable mask for UFSHCI v1.1 */
|
||||
INTERRUPT_MASK_ALL_VER_11 = 0x31FFF,
|
||||
|
||||
/* Interrupt disable mask for UFSHCI v2.1 */
|
||||
INTERRUPT_MASK_ALL_VER_21 = 0x71FFF,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue