Skip to content

Commit

Permalink
[SCSI] ipr: add hardware assisted smart dump functionality
Browse files Browse the repository at this point in the history
This patch adds the hardware assisted smart dump functionality for the next
generation IOA PCI interface chip.

Signea-off-by: Wayne Boyer <wayneb@linux.vnet.ibm.com>

Signed-off-by: James Bottomley <James.Bottomley@suse.de>
  • Loading branch information
Wayne Boyer authored and James Bottomley committed Mar 3, 2010
1 parent 4565e37 commit dcbad00
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 22 deletions.
81 changes: 65 additions & 16 deletions drivers/scsi/ipr.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
.ioarrin_reg = 0x00070,
.sense_uproc_interrupt_reg = 0x00020,
.set_uproc_interrupt_reg = 0x00020,
.clr_uproc_interrupt_reg = 0x00028
.clr_uproc_interrupt_reg = 0x00028,
.dump_addr_reg = 0x00064,
.dump_data_reg = 0x00068
}
},
};
Expand Down Expand Up @@ -2513,6 +2515,31 @@ static int ipr_wait_iodbg_ack(struct ipr_ioa_cfg *ioa_cfg, int max_delay)
return -EIO;
}

/**
* ipr_get_sis64_dump_data_section - Dump IOA memory
* @ioa_cfg: ioa config struct
* @start_addr: adapter address to dump
* @dest: destination kernel buffer
* @length_in_words: length to dump in 4 byte words
*
* Return value:
* 0 on success
**/
static int ipr_get_sis64_dump_data_section(struct ipr_ioa_cfg *ioa_cfg,
u32 start_addr,
__be32 *dest, u32 length_in_words)
{
int i;

for (i = 0; i < length_in_words; i++) {
writel(start_addr+(i*4), ioa_cfg->regs.dump_addr_reg);
*dest = cpu_to_be32(readl(ioa_cfg->regs.dump_data_reg));
dest++;
}

return 0;
}

/**
* ipr_get_ldump_data_section - Dump IOA memory
* @ioa_cfg: ioa config struct
Expand All @@ -2530,6 +2557,10 @@ static int ipr_get_ldump_data_section(struct ipr_ioa_cfg *ioa_cfg,
volatile u32 temp_pcii_reg;
int i, delay = 0;

if (ioa_cfg->sis64)
return ipr_get_sis64_dump_data_section(ioa_cfg, start_addr,
dest, length_in_words);

/* Write IOA interrupt reg starting LDUMP state */
writel((IPR_UPROCI_RESET_ALERT | IPR_UPROCI_IO_DEBUG_ALERT),
ioa_cfg->regs.set_uproc_interrupt_reg);
Expand Down Expand Up @@ -2787,6 +2818,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
u32 num_entries, start_off, end_off;
u32 bytes_to_copy, bytes_copied, rc;
struct ipr_sdt *sdt;
int valid = 1;
int i;

ENTER;
Expand All @@ -2800,7 +2832,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)

start_addr = readl(ioa_cfg->ioa_mailbox);

if (!ipr_sdt_is_fmt2(start_addr)) {
if (!ioa_cfg->sis64 && !ipr_sdt_is_fmt2(start_addr)) {
dev_err(&ioa_cfg->pdev->dev,
"Invalid dump table format: %lx\n", start_addr);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
Expand Down Expand Up @@ -2829,7 +2861,6 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)

/* IOA Dump entry */
ipr_init_dump_entry_hdr(&ioa_dump->hdr);
ioa_dump->format = IPR_SDT_FMT2;
ioa_dump->hdr.len = 0;
ioa_dump->hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
ioa_dump->hdr.id = IPR_DUMP_IOA_DUMP_ID;
Expand All @@ -2844,7 +2875,8 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
sizeof(struct ipr_sdt) / sizeof(__be32));

/* Smart Dump table is ready to use and the first entry is valid */
if (rc || (be32_to_cpu(sdt->hdr.state) != IPR_FMT2_SDT_READY_TO_USE)) {
if (rc || ((be32_to_cpu(sdt->hdr.state) != IPR_FMT3_SDT_READY_TO_USE) &&
(be32_to_cpu(sdt->hdr.state) != IPR_FMT2_SDT_READY_TO_USE))) {
dev_err(&ioa_cfg->pdev->dev,
"Dump of IOA failed. Dump table not valid: %d, %X.\n",
rc, be32_to_cpu(sdt->hdr.state));
Expand All @@ -2868,12 +2900,19 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
}

if (sdt->entry[i].flags & IPR_SDT_VALID_ENTRY) {
sdt_word = be32_to_cpu(sdt->entry[i].bar_str_offset);
start_off = sdt_word & IPR_FMT2_MBX_ADDR_MASK;
end_off = be32_to_cpu(sdt->entry[i].end_offset);

if (ipr_sdt_is_fmt2(sdt_word) && sdt_word) {
bytes_to_copy = end_off - start_off;
sdt_word = be32_to_cpu(sdt->entry[i].start_token);
if (ioa_cfg->sis64)
bytes_to_copy = be32_to_cpu(sdt->entry[i].end_token);
else {
start_off = sdt_word & IPR_FMT2_MBX_ADDR_MASK;
end_off = be32_to_cpu(sdt->entry[i].end_token);

if (ipr_sdt_is_fmt2(sdt_word) && sdt_word)
bytes_to_copy = end_off - start_off;
else
valid = 0;
}
if (valid) {
if (bytes_to_copy > IPR_MAX_IOA_DUMP_SIZE) {
sdt->entry[i].flags &= ~IPR_SDT_VALID_ENTRY;
continue;
Expand Down Expand Up @@ -7202,7 +7241,7 @@ static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg)

mailbox = readl(ioa_cfg->ioa_mailbox);

if (!ipr_sdt_is_fmt2(mailbox)) {
if (!ioa_cfg->sis64 && !ipr_sdt_is_fmt2(mailbox)) {
ipr_unit_check_no_data(ioa_cfg);
return;
}
Expand All @@ -7211,23 +7250,28 @@ static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg)
rc = ipr_get_ldump_data_section(ioa_cfg, mailbox, (__be32 *) &sdt,
(sizeof(struct ipr_uc_sdt)) / sizeof(__be32));

if (rc || (be32_to_cpu(sdt.hdr.state) != IPR_FMT2_SDT_READY_TO_USE) ||
!(sdt.entry[0].flags & IPR_SDT_VALID_ENTRY)) {
if (rc || !(sdt.entry[0].flags & IPR_SDT_VALID_ENTRY) ||
((be32_to_cpu(sdt.hdr.state) != IPR_FMT3_SDT_READY_TO_USE) &&
(be32_to_cpu(sdt.hdr.state) != IPR_FMT2_SDT_READY_TO_USE))) {
ipr_unit_check_no_data(ioa_cfg);
return;
}

/* Find length of the first sdt entry (UC buffer) */
length = (be32_to_cpu(sdt.entry[0].end_offset) -
be32_to_cpu(sdt.entry[0].bar_str_offset)) & IPR_FMT2_MBX_ADDR_MASK;
if (be32_to_cpu(sdt.hdr.state) == IPR_FMT3_SDT_READY_TO_USE)
length = be32_to_cpu(sdt.entry[0].end_token);
else
length = (be32_to_cpu(sdt.entry[0].end_token) -
be32_to_cpu(sdt.entry[0].start_token)) &
IPR_FMT2_MBX_ADDR_MASK;

hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next,
struct ipr_hostrcb, queue);
list_del(&hostrcb->queue);
memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam));

rc = ipr_get_ldump_data_section(ioa_cfg,
be32_to_cpu(sdt.entry[0].bar_str_offset),
be32_to_cpu(sdt.entry[0].start_token),
(__be32 *)&hostrcb->hcam,
min(length, (int)sizeof(hostrcb->hcam)) / sizeof(__be32));

Expand Down Expand Up @@ -8202,6 +8246,11 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
t->sense_uproc_interrupt_reg = base + p->sense_uproc_interrupt_reg;
t->set_uproc_interrupt_reg = base + p->set_uproc_interrupt_reg;
t->clr_uproc_interrupt_reg = base + p->clr_uproc_interrupt_reg;

if (ioa_cfg->sis64) {
t->dump_addr_reg = base + p->dump_addr_reg;
t->dump_data_reg = base + p->dump_data_reg;
}
}

/**
Expand Down
16 changes: 10 additions & 6 deletions drivers/scsi/ipr.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@
#define IPR_SDT_FMT2_BAR5_SEL 0x5
#define IPR_SDT_FMT2_EXP_ROM_SEL 0x8
#define IPR_FMT2_SDT_READY_TO_USE 0xC4D4E3F2
#define IPR_FMT3_SDT_READY_TO_USE 0xC4D4E3F3
#define IPR_DOORBELL 0x82800000
#define IPR_RUNTIME_RESET 0x40000000

Expand Down Expand Up @@ -1093,10 +1094,9 @@ struct ipr_hostrcb {

/* IPR smart dump table structures */
struct ipr_sdt_entry {
__be32 bar_str_offset;
__be32 end_offset;
u8 entry_byte;
u8 reserved[3];
__be32 start_token;
__be32 end_token;
u8 reserved[4];

u8 flags;
#define IPR_SDT_ENDIAN 0x80
Expand Down Expand Up @@ -1204,6 +1204,9 @@ struct ipr_interrupt_offsets {
unsigned long sense_uproc_interrupt_reg;
unsigned long set_uproc_interrupt_reg;
unsigned long clr_uproc_interrupt_reg;

unsigned long dump_addr_reg;
unsigned long dump_data_reg;
};

struct ipr_interrupts {
Expand All @@ -1217,6 +1220,9 @@ struct ipr_interrupts {
void __iomem *sense_uproc_interrupt_reg;
void __iomem *set_uproc_interrupt_reg;
void __iomem *clr_uproc_interrupt_reg;

void __iomem *dump_addr_reg;
void __iomem *dump_data_reg;
};

struct ipr_chip_cfg_t {
Expand Down Expand Up @@ -1536,8 +1542,6 @@ struct ipr_ioa_dump {
u32 next_page_index;
u32 page_offset;
u32 format;
#define IPR_SDT_FMT2 2
#define IPR_SDT_UNKNOWN 3
}__attribute__((packed, aligned (4)));

struct ipr_dump {
Expand Down

0 comments on commit dcbad00

Please sign in to comment.