mtip32xx: Add new sysfs entry 'status'
* Add support for detecting the following device status - write protect - over temp (thermal shutdown) * Add new sysfs entry 'status', possible values - online, write_protect, thermal_shutdown * Add new file 'sysfs-block-rssd' to document ABI (Reported-by: Greg Kroah-Hartman) Signed-off-by: Asai Thambi S P <asamymuthupa@micron.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
dad40f16ff
commit
f65872177d
|
@ -0,0 +1,18 @@
|
||||||
|
What: /sys/block/rssd*/registers
|
||||||
|
Date: March 2012
|
||||||
|
KernelVersion: 3.3
|
||||||
|
Contact: Asai Thambi S P <asamymuthupa@micron.com>
|
||||||
|
Description: This is a read-only file. Dumps below driver information and
|
||||||
|
hardware registers.
|
||||||
|
- S ACTive
|
||||||
|
- Command Issue
|
||||||
|
- Allocated
|
||||||
|
- Completed
|
||||||
|
- PORT IRQ STAT
|
||||||
|
- HOST IRQ STAT
|
||||||
|
|
||||||
|
What: /sys/block/rssd*/status
|
||||||
|
Date: April 2012
|
||||||
|
KernelVersion: 3.4
|
||||||
|
Contact: Asai Thambi S P <asamymuthupa@micron.com>
|
||||||
|
Description: This is a read-only file. Indicates the status of the device.
|
|
@ -725,6 +725,10 @@ static void print_tags(struct driver_data *dd,
|
||||||
dev_info(&dd->pdev->dev, "%s [%i tags]\n", msg, count);
|
dev_info(&dd->pdev->dev, "%s [%i tags]\n", msg, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer,
|
||||||
|
dma_addr_t buffer_dma, unsigned int sectors);
|
||||||
|
static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id,
|
||||||
|
struct smart_attr *attrib);
|
||||||
/*
|
/*
|
||||||
* Handle an error.
|
* Handle an error.
|
||||||
*
|
*
|
||||||
|
@ -735,12 +739,15 @@ static void print_tags(struct driver_data *dd,
|
||||||
*/
|
*/
|
||||||
static void mtip_handle_tfe(struct driver_data *dd)
|
static void mtip_handle_tfe(struct driver_data *dd)
|
||||||
{
|
{
|
||||||
int group, tag, bit, reissue;
|
int group, tag, bit, reissue, rv;
|
||||||
struct mtip_port *port;
|
struct mtip_port *port;
|
||||||
struct mtip_cmd *command;
|
struct mtip_cmd *cmd;
|
||||||
u32 completed;
|
u32 completed;
|
||||||
struct host_to_dev_fis *fis;
|
struct host_to_dev_fis *fis;
|
||||||
unsigned long tagaccum[SLOTBITS_IN_LONGS];
|
unsigned long tagaccum[SLOTBITS_IN_LONGS];
|
||||||
|
unsigned char *buf;
|
||||||
|
char *fail_reason = NULL;
|
||||||
|
int fail_all_ncq_write = 0, fail_all_ncq_cmds = 0;
|
||||||
|
|
||||||
dev_warn(&dd->pdev->dev, "Taskfile error\n");
|
dev_warn(&dd->pdev->dev, "Taskfile error\n");
|
||||||
|
|
||||||
|
@ -772,13 +779,13 @@ static void mtip_handle_tfe(struct driver_data *dd)
|
||||||
if (tag == MTIP_TAG_INTERNAL)
|
if (tag == MTIP_TAG_INTERNAL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
command = &port->commands[tag];
|
cmd = &port->commands[tag];
|
||||||
if (likely(command->comp_func)) {
|
if (likely(cmd->comp_func)) {
|
||||||
set_bit(tag, tagaccum);
|
set_bit(tag, tagaccum);
|
||||||
atomic_set(&port->commands[tag].active, 0);
|
atomic_set(&cmd->active, 0);
|
||||||
command->comp_func(port,
|
cmd->comp_func(port,
|
||||||
tag,
|
tag,
|
||||||
command->comp_data,
|
cmd->comp_data,
|
||||||
0);
|
0);
|
||||||
} else {
|
} else {
|
||||||
dev_err(&port->dd->pdev->dev,
|
dev_err(&port->dd->pdev->dev,
|
||||||
|
@ -798,6 +805,38 @@ static void mtip_handle_tfe(struct driver_data *dd)
|
||||||
mdelay(20);
|
mdelay(20);
|
||||||
mtip_restart_port(port);
|
mtip_restart_port(port);
|
||||||
|
|
||||||
|
/* Trying to determine the cause of the error */
|
||||||
|
rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ,
|
||||||
|
dd->port->log_buf,
|
||||||
|
dd->port->log_buf_dma, 1);
|
||||||
|
if (rv) {
|
||||||
|
dev_warn(&dd->pdev->dev,
|
||||||
|
"Error in READ LOG EXT (10h) command\n");
|
||||||
|
/* non-critical error, don't fail the load */
|
||||||
|
} else {
|
||||||
|
buf = (unsigned char *)dd->port->log_buf;
|
||||||
|
if (buf[259] & 0x1) {
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Write protect bit is set.\n");
|
||||||
|
set_bit(MTIP_DD_FLAG_WRITE_PROTECT_BIT, &dd->dd_flag);
|
||||||
|
fail_all_ncq_write = 1;
|
||||||
|
fail_reason = "write protect";
|
||||||
|
}
|
||||||
|
if (buf[288] == 0xF7) {
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Exceeded Tmax, drive in thermal shutdown.\n");
|
||||||
|
set_bit(MTIP_DD_FLAG_OVER_TEMP_BIT, &dd->dd_flag);
|
||||||
|
fail_all_ncq_cmds = 1;
|
||||||
|
fail_reason = "thermal shutdown";
|
||||||
|
}
|
||||||
|
if (buf[288] == 0xBF) {
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Drive indicates rebuild has failed.\n");
|
||||||
|
fail_all_ncq_cmds = 1;
|
||||||
|
fail_reason = "rebuild failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* clear the tag accumulator */
|
/* clear the tag accumulator */
|
||||||
memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long));
|
memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long));
|
||||||
|
|
||||||
|
@ -806,25 +845,44 @@ static void mtip_handle_tfe(struct driver_data *dd)
|
||||||
for (bit = 0; bit < 32; bit++) {
|
for (bit = 0; bit < 32; bit++) {
|
||||||
reissue = 1;
|
reissue = 1;
|
||||||
tag = (group << 5) + bit;
|
tag = (group << 5) + bit;
|
||||||
|
cmd = &port->commands[tag];
|
||||||
|
|
||||||
/* If the active bit is set re-issue the command */
|
/* If the active bit is set re-issue the command */
|
||||||
if (atomic_read(&port->commands[tag].active) == 0)
|
if (atomic_read(&cmd->active) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
fis = (struct host_to_dev_fis *)
|
fis = (struct host_to_dev_fis *)cmd->command;
|
||||||
port->commands[tag].command;
|
|
||||||
|
|
||||||
/* Should re-issue? */
|
/* Should re-issue? */
|
||||||
if (tag == MTIP_TAG_INTERNAL ||
|
if (tag == MTIP_TAG_INTERNAL ||
|
||||||
fis->command == ATA_CMD_SET_FEATURES)
|
fis->command == ATA_CMD_SET_FEATURES)
|
||||||
reissue = 0;
|
reissue = 0;
|
||||||
|
else {
|
||||||
|
if (fail_all_ncq_cmds ||
|
||||||
|
(fail_all_ncq_write &&
|
||||||
|
fis->command == ATA_CMD_FPDMA_WRITE)) {
|
||||||
|
dev_warn(&dd->pdev->dev,
|
||||||
|
" Fail: %s w/tag %d [%s].\n",
|
||||||
|
fis->command == ATA_CMD_FPDMA_WRITE ?
|
||||||
|
"write" : "read",
|
||||||
|
tag,
|
||||||
|
fail_reason != NULL ?
|
||||||
|
fail_reason : "unknown");
|
||||||
|
atomic_set(&cmd->active, 0);
|
||||||
|
if (cmd->comp_func) {
|
||||||
|
cmd->comp_func(port, tag,
|
||||||
|
cmd->comp_data,
|
||||||
|
-ENODATA);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First check if this command has
|
* First check if this command has
|
||||||
* exceeded its retries.
|
* exceeded its retries.
|
||||||
*/
|
*/
|
||||||
if (reissue &&
|
if (reissue && (cmd->retries-- > 0)) {
|
||||||
(port->commands[tag].retries-- > 0)) {
|
|
||||||
|
|
||||||
set_bit(tag, tagaccum);
|
set_bit(tag, tagaccum);
|
||||||
|
|
||||||
|
@ -837,13 +895,13 @@ static void mtip_handle_tfe(struct driver_data *dd)
|
||||||
/* Retire a command that will not be reissued */
|
/* Retire a command that will not be reissued */
|
||||||
dev_warn(&port->dd->pdev->dev,
|
dev_warn(&port->dd->pdev->dev,
|
||||||
"retiring tag %d\n", tag);
|
"retiring tag %d\n", tag);
|
||||||
atomic_set(&port->commands[tag].active, 0);
|
atomic_set(&cmd->active, 0);
|
||||||
|
|
||||||
if (port->commands[tag].comp_func)
|
if (cmd->comp_func)
|
||||||
port->commands[tag].comp_func(
|
cmd->comp_func(
|
||||||
port,
|
port,
|
||||||
tag,
|
tag,
|
||||||
port->commands[tag].comp_data,
|
cmd->comp_data,
|
||||||
PORT_IRQ_TF_ERR);
|
PORT_IRQ_TF_ERR);
|
||||||
else
|
else
|
||||||
dev_warn(&port->dd->pdev->dev,
|
dev_warn(&port->dd->pdev->dev,
|
||||||
|
@ -1374,6 +1432,7 @@ static int mtip_standby_immediate(struct mtip_port *port)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
struct host_to_dev_fis fis;
|
struct host_to_dev_fis fis;
|
||||||
|
unsigned long start;
|
||||||
|
|
||||||
/* Build the FIS. */
|
/* Build the FIS. */
|
||||||
memset(&fis, 0, sizeof(struct host_to_dev_fis));
|
memset(&fis, 0, sizeof(struct host_to_dev_fis));
|
||||||
|
@ -1381,15 +1440,150 @@ static int mtip_standby_immediate(struct mtip_port *port)
|
||||||
fis.opts = 1 << 7;
|
fis.opts = 1 << 7;
|
||||||
fis.command = ATA_CMD_STANDBYNOW1;
|
fis.command = ATA_CMD_STANDBYNOW1;
|
||||||
|
|
||||||
/* Execute the command. Use a 15-second timeout for large drives. */
|
start = jiffies;
|
||||||
rv = mtip_exec_internal_command(port,
|
rv = mtip_exec_internal_command(port,
|
||||||
&fis,
|
&fis,
|
||||||
5,
|
5,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
GFP_KERNEL,
|
GFP_ATOMIC,
|
||||||
15000);
|
15000);
|
||||||
|
dbg_printk(MTIP_DRV_NAME "Time taken to complete standby cmd: %d ms\n",
|
||||||
|
jiffies_to_msecs(jiffies - start));
|
||||||
|
if (rv)
|
||||||
|
dev_warn(&port->dd->pdev->dev,
|
||||||
|
"STANDBY IMMEDIATE command failed.\n");
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Issue a READ LOG EXT command to the device.
|
||||||
|
*
|
||||||
|
* @port pointer to the port structure.
|
||||||
|
* @page page number to fetch
|
||||||
|
* @buffer pointer to buffer
|
||||||
|
* @buffer_dma dma address corresponding to @buffer
|
||||||
|
* @sectors page length to fetch, in sectors
|
||||||
|
*
|
||||||
|
* return value
|
||||||
|
* @rv return value from mtip_exec_internal_command()
|
||||||
|
*/
|
||||||
|
static int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer,
|
||||||
|
dma_addr_t buffer_dma, unsigned int sectors)
|
||||||
|
{
|
||||||
|
struct host_to_dev_fis fis;
|
||||||
|
|
||||||
|
memset(&fis, 0, sizeof(struct host_to_dev_fis));
|
||||||
|
fis.type = 0x27;
|
||||||
|
fis.opts = 1 << 7;
|
||||||
|
fis.command = ATA_CMD_READ_LOG_EXT;
|
||||||
|
fis.sect_count = sectors & 0xFF;
|
||||||
|
fis.sect_cnt_ex = (sectors >> 8) & 0xFF;
|
||||||
|
fis.lba_low = page;
|
||||||
|
fis.lba_mid = 0;
|
||||||
|
fis.device = ATA_DEVICE_OBS;
|
||||||
|
|
||||||
|
memset(buffer, 0, sectors * ATA_SECT_SIZE);
|
||||||
|
|
||||||
|
return mtip_exec_internal_command(port,
|
||||||
|
&fis,
|
||||||
|
5,
|
||||||
|
buffer_dma,
|
||||||
|
sectors * ATA_SECT_SIZE,
|
||||||
|
0,
|
||||||
|
GFP_ATOMIC,
|
||||||
|
MTIP_INTERNAL_COMMAND_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Issue a SMART READ DATA command to the device.
|
||||||
|
*
|
||||||
|
* @port pointer to the port structure.
|
||||||
|
* @buffer pointer to buffer
|
||||||
|
* @buffer_dma dma address corresponding to @buffer
|
||||||
|
*
|
||||||
|
* return value
|
||||||
|
* @rv return value from mtip_exec_internal_command()
|
||||||
|
*/
|
||||||
|
static int mtip_get_smart_data(struct mtip_port *port, u8 *buffer,
|
||||||
|
dma_addr_t buffer_dma)
|
||||||
|
{
|
||||||
|
struct host_to_dev_fis fis;
|
||||||
|
|
||||||
|
memset(&fis, 0, sizeof(struct host_to_dev_fis));
|
||||||
|
fis.type = 0x27;
|
||||||
|
fis.opts = 1 << 7;
|
||||||
|
fis.command = ATA_CMD_SMART;
|
||||||
|
fis.features = 0xD0;
|
||||||
|
fis.sect_count = 1;
|
||||||
|
fis.lba_mid = 0x4F;
|
||||||
|
fis.lba_hi = 0xC2;
|
||||||
|
fis.device = ATA_DEVICE_OBS;
|
||||||
|
|
||||||
|
return mtip_exec_internal_command(port,
|
||||||
|
&fis,
|
||||||
|
5,
|
||||||
|
buffer_dma,
|
||||||
|
ATA_SECT_SIZE,
|
||||||
|
0,
|
||||||
|
GFP_ATOMIC,
|
||||||
|
15000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the value of a smart attribute
|
||||||
|
*
|
||||||
|
* @port pointer to the port structure
|
||||||
|
* @id attribute number
|
||||||
|
* @attrib pointer to return attrib information corresponding to @id
|
||||||
|
*
|
||||||
|
* return value
|
||||||
|
* -EINVAL NULL buffer passed or unsupported attribute @id.
|
||||||
|
* -EPERM Identify data not valid, SMART not supported or not enabled
|
||||||
|
*/
|
||||||
|
static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id,
|
||||||
|
struct smart_attr *attrib)
|
||||||
|
{
|
||||||
|
int rv, i;
|
||||||
|
struct smart_attr *pattr;
|
||||||
|
|
||||||
|
if (!attrib)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!port->identify_valid) {
|
||||||
|
dev_warn(&port->dd->pdev->dev, "IDENTIFY DATA not valid\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
if (!(port->identify[82] & 0x1)) {
|
||||||
|
dev_warn(&port->dd->pdev->dev, "SMART not supported\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
if (!(port->identify[85] & 0x1)) {
|
||||||
|
dev_warn(&port->dd->pdev->dev, "SMART not enabled\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(port->smart_buf, 0, ATA_SECT_SIZE);
|
||||||
|
rv = mtip_get_smart_data(port, port->smart_buf, port->smart_buf_dma);
|
||||||
|
if (rv) {
|
||||||
|
dev_warn(&port->dd->pdev->dev, "Failed to ge SMART data\n");
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattr = (struct smart_attr *)(port->smart_buf + 2);
|
||||||
|
for (i = 0; i < 29; i++, pattr++)
|
||||||
|
if (pattr->attr_id == id) {
|
||||||
|
memcpy(attrib, pattr, sizeof(struct smart_attr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 29) {
|
||||||
|
dev_warn(&port->dd->pdev->dev,
|
||||||
|
"Query for invalid SMART attribute ID\n");
|
||||||
|
rv = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -2272,7 +2466,7 @@ static ssize_t mtip_hw_show_registers(struct device *dev,
|
||||||
int size = 0;
|
int size = 0;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
size += sprintf(&buf[size], "%s:\ns_active:\n", __func__);
|
size += sprintf(&buf[size], "S ACTive:\n");
|
||||||
|
|
||||||
for (n = 0; n < dd->slot_groups; n++)
|
for (n = 0; n < dd->slot_groups; n++)
|
||||||
size += sprintf(&buf[size], "0x%08x\n",
|
size += sprintf(&buf[size], "0x%08x\n",
|
||||||
|
@ -2296,20 +2490,39 @@ static ssize_t mtip_hw_show_registers(struct device *dev,
|
||||||
group_allocated);
|
group_allocated);
|
||||||
}
|
}
|
||||||
|
|
||||||
size += sprintf(&buf[size], "completed:\n");
|
size += sprintf(&buf[size], "Completed:\n");
|
||||||
|
|
||||||
for (n = 0; n < dd->slot_groups; n++)
|
for (n = 0; n < dd->slot_groups; n++)
|
||||||
size += sprintf(&buf[size], "0x%08x\n",
|
size += sprintf(&buf[size], "0x%08x\n",
|
||||||
readl(dd->port->completed[n]));
|
readl(dd->port->completed[n]));
|
||||||
|
|
||||||
size += sprintf(&buf[size], "PORT_IRQ_STAT 0x%08x\n",
|
size += sprintf(&buf[size], "PORT IRQ STAT : 0x%08x\n",
|
||||||
readl(dd->port->mmio + PORT_IRQ_STAT));
|
readl(dd->port->mmio + PORT_IRQ_STAT));
|
||||||
size += sprintf(&buf[size], "HOST_IRQ_STAT 0x%08x\n",
|
size += sprintf(&buf[size], "HOST IRQ STAT : 0x%08x\n",
|
||||||
readl(dd->mmio + HOST_IRQ_STAT));
|
readl(dd->mmio + HOST_IRQ_STAT));
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t mtip_hw_show_status(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct driver_data *dd = dev_to_disk(dev)->private_data;
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
if (test_bit(MTIP_DD_FLAG_OVER_TEMP_BIT, &dd->dd_flag))
|
||||||
|
size += sprintf(buf, "%s", "thermal_shutdown\n");
|
||||||
|
else if (test_bit(MTIP_DD_FLAG_WRITE_PROTECT_BIT, &dd->dd_flag))
|
||||||
|
size += sprintf(buf, "%s", "write_protect\n");
|
||||||
|
else
|
||||||
|
size += sprintf(buf, "%s", "online\n");
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL);
|
static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL);
|
||||||
|
static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the sysfs related attributes.
|
* Create the sysfs related attributes.
|
||||||
|
@ -2328,7 +2541,10 @@ static int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj)
|
||||||
|
|
||||||
if (sysfs_create_file(kobj, &dev_attr_registers.attr))
|
if (sysfs_create_file(kobj, &dev_attr_registers.attr))
|
||||||
dev_warn(&dd->pdev->dev,
|
dev_warn(&dd->pdev->dev,
|
||||||
"Error creating registers sysfs entry\n");
|
"Error creating 'registers' sysfs entry\n");
|
||||||
|
if (sysfs_create_file(kobj, &dev_attr_status.attr))
|
||||||
|
dev_warn(&dd->pdev->dev,
|
||||||
|
"Error creating 'status' sysfs entry\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2348,6 +2564,7 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
sysfs_remove_file(kobj, &dev_attr_registers.attr);
|
sysfs_remove_file(kobj, &dev_attr_registers.attr);
|
||||||
|
sysfs_remove_file(kobj, &dev_attr_status.attr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2566,6 +2783,8 @@ static int mtip_hw_init(struct driver_data *dd)
|
||||||
int rv;
|
int rv;
|
||||||
unsigned int num_command_slots;
|
unsigned int num_command_slots;
|
||||||
unsigned long timeout, timetaken;
|
unsigned long timeout, timetaken;
|
||||||
|
unsigned char *buf;
|
||||||
|
struct smart_attr attr242;
|
||||||
|
|
||||||
dd->mmio = pcim_iomap_table(dd->pdev)[MTIP_ABAR];
|
dd->mmio = pcim_iomap_table(dd->pdev)[MTIP_ABAR];
|
||||||
|
|
||||||
|
@ -2600,7 +2819,7 @@ static int mtip_hw_init(struct driver_data *dd)
|
||||||
/* Allocate memory for the command list. */
|
/* Allocate memory for the command list. */
|
||||||
dd->port->command_list =
|
dd->port->command_list =
|
||||||
dmam_alloc_coherent(&dd->pdev->dev,
|
dmam_alloc_coherent(&dd->pdev->dev,
|
||||||
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2),
|
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4),
|
||||||
&dd->port->command_list_dma,
|
&dd->port->command_list_dma,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!dd->port->command_list) {
|
if (!dd->port->command_list) {
|
||||||
|
@ -2613,7 +2832,7 @@ static int mtip_hw_init(struct driver_data *dd)
|
||||||
/* Clear the memory we have allocated. */
|
/* Clear the memory we have allocated. */
|
||||||
memset(dd->port->command_list,
|
memset(dd->port->command_list,
|
||||||
0,
|
0,
|
||||||
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2));
|
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4));
|
||||||
|
|
||||||
/* Setup the addresse of the RX FIS. */
|
/* Setup the addresse of the RX FIS. */
|
||||||
dd->port->rxfis = dd->port->command_list + HW_CMD_SLOT_SZ;
|
dd->port->rxfis = dd->port->command_list + HW_CMD_SLOT_SZ;
|
||||||
|
@ -2629,10 +2848,19 @@ static int mtip_hw_init(struct driver_data *dd)
|
||||||
dd->port->identify_dma = dd->port->command_tbl_dma +
|
dd->port->identify_dma = dd->port->command_tbl_dma +
|
||||||
HW_CMD_TBL_AR_SZ;
|
HW_CMD_TBL_AR_SZ;
|
||||||
|
|
||||||
/* Setup the address of the sector buffer. */
|
/* Setup the address of the sector buffer - for some non-ncq cmds */
|
||||||
dd->port->sector_buffer = (void *) dd->port->identify + ATA_SECT_SIZE;
|
dd->port->sector_buffer = (void *) dd->port->identify + ATA_SECT_SIZE;
|
||||||
dd->port->sector_buffer_dma = dd->port->identify_dma + ATA_SECT_SIZE;
|
dd->port->sector_buffer_dma = dd->port->identify_dma + ATA_SECT_SIZE;
|
||||||
|
|
||||||
|
/* Setup the address of the log buf - for read log command */
|
||||||
|
dd->port->log_buf = (void *)dd->port->sector_buffer + ATA_SECT_SIZE;
|
||||||
|
dd->port->log_buf_dma = dd->port->sector_buffer_dma + ATA_SECT_SIZE;
|
||||||
|
|
||||||
|
/* Setup the address of the smart buf - for smart read data command */
|
||||||
|
dd->port->smart_buf = (void *)dd->port->log_buf + ATA_SECT_SIZE;
|
||||||
|
dd->port->smart_buf_dma = dd->port->log_buf_dma + ATA_SECT_SIZE;
|
||||||
|
|
||||||
|
|
||||||
/* Point the command headers at the command tables. */
|
/* Point the command headers at the command tables. */
|
||||||
for (i = 0; i < num_command_slots; i++) {
|
for (i = 0; i < num_command_slots; i++) {
|
||||||
dd->port->commands[i].command_header =
|
dd->port->commands[i].command_header =
|
||||||
|
@ -2759,6 +2987,43 @@ static int mtip_hw_init(struct driver_data *dd)
|
||||||
return MTIP_FTL_REBUILD_MAGIC;
|
return MTIP_FTL_REBUILD_MAGIC;
|
||||||
}
|
}
|
||||||
mtip_dump_identify(dd->port);
|
mtip_dump_identify(dd->port);
|
||||||
|
|
||||||
|
/* check write protect, over temp and rebuild statuses */
|
||||||
|
rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ,
|
||||||
|
dd->port->log_buf,
|
||||||
|
dd->port->log_buf_dma, 1);
|
||||||
|
if (rv) {
|
||||||
|
dev_warn(&dd->pdev->dev,
|
||||||
|
"Error in READ LOG EXT (10h) command\n");
|
||||||
|
/* non-critical error, don't fail the load */
|
||||||
|
} else {
|
||||||
|
buf = (unsigned char *)dd->port->log_buf;
|
||||||
|
if (buf[259] & 0x1) {
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Write protect bit is set.\n");
|
||||||
|
set_bit(MTIP_DD_FLAG_WRITE_PROTECT_BIT, &dd->dd_flag);
|
||||||
|
}
|
||||||
|
if (buf[288] == 0xF7) {
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Exceeded Tmax, drive in thermal shutdown.\n");
|
||||||
|
set_bit(MTIP_DD_FLAG_OVER_TEMP_BIT, &dd->dd_flag);
|
||||||
|
}
|
||||||
|
if (buf[288] == 0xBF) {
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Drive indicates rebuild has failed.\n");
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get write protect progess */
|
||||||
|
memset(&attr242, 0, sizeof(struct smart_attr));
|
||||||
|
if (mtip_get_smart_attr(dd->port, 242, &attr242))
|
||||||
|
dev_warn(&dd->pdev->dev,
|
||||||
|
"Unable to check write protect progress\n");
|
||||||
|
else
|
||||||
|
dev_info(&dd->pdev->dev,
|
||||||
|
"Write protect progress: %d%% (%d blocks)\n",
|
||||||
|
attr242.cur, attr242.data);
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
out3:
|
out3:
|
||||||
|
@ -2776,7 +3041,7 @@ out2:
|
||||||
|
|
||||||
/* Free the command/command header memory. */
|
/* Free the command/command header memory. */
|
||||||
dmam_free_coherent(&dd->pdev->dev,
|
dmam_free_coherent(&dd->pdev->dev,
|
||||||
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2),
|
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4),
|
||||||
dd->port->command_list,
|
dd->port->command_list,
|
||||||
dd->port->command_list_dma);
|
dd->port->command_list_dma);
|
||||||
out1:
|
out1:
|
||||||
|
@ -2825,7 +3090,7 @@ static int mtip_hw_exit(struct driver_data *dd)
|
||||||
|
|
||||||
/* Free the command/command header memory. */
|
/* Free the command/command header memory. */
|
||||||
dmam_free_coherent(&dd->pdev->dev,
|
dmam_free_coherent(&dd->pdev->dev,
|
||||||
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2),
|
HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4),
|
||||||
dd->port->command_list,
|
dd->port->command_list,
|
||||||
dd->port->command_list_dma);
|
dd->port->command_list_dma);
|
||||||
/* Free the memory allocated for the for structure. */
|
/* Free the memory allocated for the for structure. */
|
||||||
|
@ -3378,7 +3643,7 @@ static int mtip_block_remove(struct driver_data *dd)
|
||||||
kthread_stop(dd->mtip_svc_handler);
|
kthread_stop(dd->mtip_svc_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clean up the sysfs attributes managed by the protocol layer. */
|
/* Clean up the sysfs attributes, if created */
|
||||||
if (test_bit(MTIP_DD_FLAG_INIT_DONE_BIT, &dd->dd_flag)) {
|
if (test_bit(MTIP_DD_FLAG_INIT_DONE_BIT, &dd->dd_flag)) {
|
||||||
kobj = kobject_get(&disk_to_dev(dd->disk)->kobj);
|
kobj = kobject_get(&disk_to_dev(dd->disk)->kobj);
|
||||||
if (kobj) {
|
if (kobj) {
|
||||||
|
|
|
@ -127,6 +127,19 @@
|
||||||
#define MTIP_DD_FLAG_CLEANUP_BIT 3
|
#define MTIP_DD_FLAG_CLEANUP_BIT 3
|
||||||
#define MTIP_DD_FLAG_INIT_DONE_BIT 4
|
#define MTIP_DD_FLAG_INIT_DONE_BIT 4
|
||||||
|
|
||||||
|
#define MTIP_DD_FLAG_WRITE_PROTECT_BIT 5
|
||||||
|
#define MTIP_DD_FLAG_OVER_TEMP_BIT 6
|
||||||
|
#define MTIP_DD_FLAG_REBUILD_FAILED_BIT 7
|
||||||
|
|
||||||
|
__packed struct smart_attr{
|
||||||
|
u8 attr_id;
|
||||||
|
u16 flags;
|
||||||
|
u8 cur;
|
||||||
|
u8 worst;
|
||||||
|
u32 data;
|
||||||
|
u8 res[3];
|
||||||
|
};
|
||||||
|
|
||||||
/* Register Frame Information Structure (FIS), host to device. */
|
/* Register Frame Information Structure (FIS), host to device. */
|
||||||
struct host_to_dev_fis {
|
struct host_to_dev_fis {
|
||||||
/*
|
/*
|
||||||
|
@ -351,6 +364,12 @@ struct mtip_port {
|
||||||
* when the command slot and all associated data structures
|
* when the command slot and all associated data structures
|
||||||
* are no longer needed.
|
* are no longer needed.
|
||||||
*/
|
*/
|
||||||
|
u16 *log_buf;
|
||||||
|
dma_addr_t log_buf_dma;
|
||||||
|
|
||||||
|
u8 *smart_buf;
|
||||||
|
dma_addr_t smart_buf_dma;
|
||||||
|
|
||||||
unsigned long allocated[SLOTBITS_IN_LONGS];
|
unsigned long allocated[SLOTBITS_IN_LONGS];
|
||||||
/*
|
/*
|
||||||
* used to queue commands when an internal command is in progress
|
* used to queue commands when an internal command is in progress
|
||||||
|
|
Loading…
Reference in New Issue