at_wini/ahci: write cache ioctls
This commit is contained in:
parent
5998a4b2af
commit
484b2f43d6
5 changed files with 188 additions and 9 deletions
|
@ -178,6 +178,7 @@ PRIVATE long ahci_sig_timeout = SIG_TIMEOUT;
|
|||
PRIVATE long ahci_sig_checks = NR_SIG_CHECKS;
|
||||
PRIVATE long ahci_command_timeout = COMMAND_TIMEOUT;
|
||||
PRIVATE long ahci_transfer_timeout = TRANSFER_TIMEOUT;
|
||||
PRIVATE long ahci_flush_timeout = FLUSH_TIMEOUT;
|
||||
|
||||
PRIVATE int ahci_map[MAX_DRIVES]; /* device-to-port mapping */
|
||||
|
||||
|
@ -437,6 +438,19 @@ PRIVATE int atapi_id_check(struct port_state *ps, u16_t *buf)
|
|||
ATA_ID_GCAP_TYPE_SHIFT) == ATAPI_TYPE_CDROM)
|
||||
ps->flags |= FLAG_READONLY;
|
||||
|
||||
if ((buf[ATA_ID_SUP1] & ATA_ID_SUP1_VALID_MASK) == ATA_ID_SUP1_VALID &&
|
||||
!(ps->flags & FLAG_READONLY)) {
|
||||
/* Save write cache related capabilities of the device. It is
|
||||
* possible, although unlikely, that a device has support for
|
||||
* either of these but not both.
|
||||
*/
|
||||
if (buf[ATA_ID_SUP0] & ATA_ID_SUP0_WCACHE)
|
||||
ps->flags |= FLAG_HAS_WCACHE;
|
||||
|
||||
if (buf[ATA_ID_SUP1] & ATA_ID_SUP1_FLUSH)
|
||||
ps->flags |= FLAG_HAS_FLUSH;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -486,15 +500,16 @@ PRIVATE int ata_id_check(struct port_state *ps, u16_t *buf)
|
|||
*/
|
||||
|
||||
/* This must be an ATA device; it must not have removable media;
|
||||
* it must support LBA and DMA; it must support 48-bit addressing.
|
||||
* it must support LBA and DMA; it must support the FLUSH CACHE
|
||||
* command; it must support 48-bit addressing.
|
||||
*/
|
||||
if ((buf[ATA_ID_GCAP] & (ATA_ID_GCAP_ATA_MASK | ATA_ID_GCAP_REMOVABLE |
|
||||
ATA_ID_GCAP_INCOMPLETE)) != ATA_ID_GCAP_ATA ||
|
||||
(buf[ATA_ID_CAP] & (ATA_ID_CAP_LBA | ATA_ID_CAP_DMA)) !=
|
||||
(ATA_ID_CAP_LBA | ATA_ID_CAP_DMA) ||
|
||||
(buf[ATA_ID_SUP1] &
|
||||
(ATA_ID_SUP1_VALID_MASK | ATA_ID_SUP1_LBA48)) !=
|
||||
(ATA_ID_SUP1_VALID | ATA_ID_SUP1_LBA48)) {
|
||||
(buf[ATA_ID_SUP1] & (ATA_ID_SUP1_VALID_MASK |
|
||||
ATA_ID_SUP1_FLUSH | ATA_ID_SUP1_LBA48)) !=
|
||||
(ATA_ID_SUP1_VALID | ATA_ID_SUP1_FLUSH | ATA_ID_SUP1_LBA48)) {
|
||||
|
||||
dprintf(V_ERR, ("%s: unsupported ATA device\n",
|
||||
ahci_portname(ps)));
|
||||
|
@ -527,7 +542,11 @@ PRIVATE int ata_id_check(struct port_state *ps, u16_t *buf)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
ps->flags |= FLAG_HAS_MEDIUM;
|
||||
ps->flags |= FLAG_HAS_MEDIUM | FLAG_HAS_FLUSH;
|
||||
|
||||
/* FLUSH CACHE is mandatory for ATA devices; write caches are not. */
|
||||
if (buf[ATA_ID_SUP0] & ATA_ID_SUP0_WCACHE)
|
||||
ps->flags |= FLAG_HAS_WCACHE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -566,9 +585,10 @@ PRIVATE int ata_transfer(struct port_state *ps, int cmd, u64_t start_lba,
|
|||
/*===========================================================================*
|
||||
* gen_identify *
|
||||
*===========================================================================*/
|
||||
PRIVATE void gen_identify(struct port_state *ps, int cmd)
|
||||
PRIVATE int gen_identify(struct port_state *ps, int cmd, int blocking)
|
||||
{
|
||||
/* Identify an ATA or ATAPI device.
|
||||
/* Identify an ATA or ATAPI device. If the blocking flag is set, block
|
||||
* until the command has completed; otherwise return immediately.
|
||||
*/
|
||||
cmd_fis_t fis;
|
||||
prd_t prd;
|
||||
|
@ -584,10 +604,102 @@ PRIVATE void gen_identify(struct port_state *ps, int cmd)
|
|||
prd.prd_phys = ps->tmp_phys;
|
||||
prd.prd_size = ATA_ID_SIZE;
|
||||
|
||||
/* Start the command, but do not wait for the result. */
|
||||
/* Start the command, and possibly wait for the result. */
|
||||
port_set_cmd(ps, cmd, &fis, NULL /*packet*/, &prd, 1, FALSE /*write*/);
|
||||
|
||||
if (blocking)
|
||||
return port_exec(ps, cmd, ahci_command_timeout);
|
||||
|
||||
port_issue(ps, cmd, ahci_command_timeout);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* gen_flush_wcache *
|
||||
*===========================================================================*/
|
||||
PRIVATE int gen_flush_wcache(struct port_state *ps, int cmd)
|
||||
{
|
||||
/* Flush the device's write cache.
|
||||
*/
|
||||
cmd_fis_t fis;
|
||||
|
||||
/* The FLUSH CACHE command may not be supported by all (writable ATAPI)
|
||||
* devices.
|
||||
*/
|
||||
if (!(ps->flags & FLAG_HAS_FLUSH))
|
||||
return EINVAL;
|
||||
|
||||
/* Use the FLUSH CACHE command for both ATA and ATAPI. We are not
|
||||
* interested in the disk location of a failure, so there is no reason
|
||||
* to use the ATA-only FLUSH CACHE EXT command. Either way, the command
|
||||
* may indeed fail due to a disk error, in which case it should be
|
||||
* repeated. For now, we shift this responsibility onto the caller.
|
||||
*/
|
||||
memset(&fis, 0, sizeof(fis));
|
||||
fis.cf_cmd = ATA_CMD_FLUSH_CACHE;
|
||||
|
||||
/* Start the command, and wait for it to complete or fail.
|
||||
* The flush command may take longer than regular I/O commands.
|
||||
*/
|
||||
port_set_cmd(ps, cmd, &fis, NULL /*packet*/, NULL /*prdt*/, 0,
|
||||
FALSE /*write*/);
|
||||
|
||||
return port_exec(ps, cmd, ahci_flush_timeout);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* gen_get_wcache *
|
||||
*===========================================================================*/
|
||||
PRIVATE int gen_get_wcache(struct port_state *ps, int cmd, int *val)
|
||||
{
|
||||
/* Retrieve the status of the device's write cache.
|
||||
*/
|
||||
int r;
|
||||
|
||||
/* Write caches are not mandatory. */
|
||||
if (!(ps->flags & FLAG_HAS_WCACHE))
|
||||
return EINVAL;
|
||||
|
||||
/* Retrieve information about the device. */
|
||||
if ((r = gen_identify(ps, cmd, TRUE /*blocking*/)) != OK)
|
||||
return r;
|
||||
|
||||
/* Return the current setting. */
|
||||
*val = !!(((u16_t *) ps->tmp_base)[ATA_ID_ENA0] & ATA_ID_ENA0_WCACHE);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* gen_set_wcache *
|
||||
*===========================================================================*/
|
||||
PRIVATE int gen_set_wcache(struct port_state *ps, int cmd, int enable)
|
||||
{
|
||||
/* Enable or disable the device's write cache.
|
||||
*/
|
||||
cmd_fis_t fis;
|
||||
clock_t timeout;
|
||||
|
||||
/* Write caches are not mandatory. */
|
||||
if (!(ps->flags & FLAG_HAS_WCACHE))
|
||||
return EINVAL;
|
||||
|
||||
/* Disabling the write cache causes a (blocking) cache flush. Cache
|
||||
* flushes may take much longer than regular commands.
|
||||
*/
|
||||
timeout = enable ? ahci_command_timeout : ahci_flush_timeout;
|
||||
|
||||
/* Set up a command. */
|
||||
memset(&fis, 0, sizeof(fis));
|
||||
fis.cf_cmd = ATA_CMD_SET_FEATURES;
|
||||
fis.cf_feat = enable ? ATA_SF_EN_WCACHE : ATA_SF_DI_WCACHE;
|
||||
|
||||
/* Start the command, and wait for it to complete or fail. */
|
||||
port_set_cmd(ps, cmd, &fis, NULL /*packet*/, NULL /*prdt*/, 0,
|
||||
FALSE /*write*/);
|
||||
|
||||
return port_exec(ps, cmd, timeout);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
|
@ -1121,7 +1233,7 @@ PRIVATE void port_sig_check(struct port_state *ps)
|
|||
ps->state = STATE_WAIT_ID;
|
||||
ps->reg[AHCI_PORT_IE] = AHCI_PORT_IE_MASK;
|
||||
|
||||
gen_identify(ps, 0);
|
||||
(void) gen_identify(ps, 0, FALSE /*blocking*/);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
|
@ -1799,6 +1911,9 @@ PRIVATE void ahci_stop(void)
|
|||
|
||||
for (port = 0; port < hba_state.nr_ports; port++) {
|
||||
if (port_state[port].state != STATE_NO_PORT) {
|
||||
if (port_state[port].state == STATE_GOOD_DEV)
|
||||
(void) gen_flush_wcache(&port_state[port], 0);
|
||||
|
||||
port_stop(&port_state[port]);
|
||||
|
||||
port_free(&port_state[port]);
|
||||
|
@ -1896,6 +2011,7 @@ PRIVATE void ahci_get_params(void)
|
|||
ahci_get_var("ahci_sig_checks", &ahci_sig_checks, FALSE);
|
||||
ahci_get_var("ahci_cmd_timeout", &ahci_command_timeout, TRUE);
|
||||
ahci_get_var("ahci_io_timeout", &ahci_transfer_timeout, TRUE);
|
||||
ahci_get_var("ahci_flush_timeout", &ahci_flush_timeout, TRUE);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
|
@ -2269,6 +2385,7 @@ PRIVATE int ahci_other(struct driver *UNUSED(dp), message *m)
|
|||
/* Process any messages not covered by the other calls.
|
||||
* This function only implements IOCTLs.
|
||||
*/
|
||||
int r, val;
|
||||
|
||||
if (m->m_type != DEV_IOCTL_S)
|
||||
return EINVAL;
|
||||
|
@ -2290,6 +2407,33 @@ PRIVATE int ahci_other(struct driver *UNUSED(dp), message *m)
|
|||
return sys_safecopyto(m->IO_ENDPT, (cp_grant_id_t) m->IO_GRANT,
|
||||
0, (vir_bytes) ¤t_port->open_count,
|
||||
sizeof(current_port->open_count), D);
|
||||
|
||||
case DIOCFLUSH:
|
||||
if (current_port->state != STATE_GOOD_DEV)
|
||||
return EIO;
|
||||
|
||||
return gen_flush_wcache(current_port, 0);
|
||||
|
||||
case DIOCSETWC:
|
||||
if (current_port->state != STATE_GOOD_DEV)
|
||||
return EIO;
|
||||
|
||||
if ((r = sys_safecopyfrom(m->IO_ENDPT,
|
||||
(cp_grant_id_t) m->IO_GRANT, 0, (vir_bytes) &val,
|
||||
sizeof(val), D)) != OK)
|
||||
return r;
|
||||
|
||||
return gen_set_wcache(current_port, 0, val);
|
||||
|
||||
case DIOCGETWC:
|
||||
if (current_port->state != STATE_GOOD_DEV)
|
||||
return EIO;
|
||||
|
||||
if ((r = gen_get_wcache(current_port, 0, &val)) != OK)
|
||||
return r;
|
||||
|
||||
return sys_safecopyto(m->IO_ENDPT, (cp_grant_id_t) m->IO_GRANT,
|
||||
0, (vir_bytes) &val, sizeof(val), D);
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#define NR_SIG_CHECKS 60 /* maximum number of times to check */
|
||||
#define COMMAND_TIMEOUT 5000 /* time to wait for non-I/O cmd (ms) */
|
||||
#define TRANSFER_TIMEOUT 30000 /* time to wait for I/O cmd (ms) */
|
||||
#define FLUSH_TIMEOUT 60000 /* time to wait for flush cmd (ms) */
|
||||
|
||||
/* Time values that are defined by the standards. */
|
||||
#define SPINUP_DELAY 1 /* time to assert spin-up flag (ms) */
|
||||
|
@ -31,7 +32,9 @@
|
|||
#define ATA_CMD_WRITE_DMA_EXT 0x35 /* WRITE DMA EXT */
|
||||
#define ATA_CMD_PACKET 0xA0 /* PACKET */
|
||||
#define ATA_CMD_IDENTIFY_PACKET 0xA1 /* IDENTIFY PACKET DEVICE */
|
||||
#define ATA_CMD_FLUSH_CACHE 0xE7 /* FLUSH CACHE */
|
||||
#define ATA_CMD_IDENTIFY 0xEC /* IDENTIFY DEVICE */
|
||||
#define ATA_CMD_SET_FEATURES 0xEF /* SET FEATURES */
|
||||
#define ATA_H2D_FEAT 3 /* Features */
|
||||
#define ATA_FEAT_PACKET_DMA 0x01 /* use DMA */
|
||||
#define ATA_FEAT_PACKET_DMADIR 0x03 /* DMA is inbound */
|
||||
|
@ -69,10 +72,18 @@
|
|||
#define ATA_ID_DMADIR 62 /* DMADIR */
|
||||
#define ATA_ID_DMADIR_DMADIR 0x8000 /* DMADIR required */
|
||||
#define ATA_ID_DMADIR_DMA 0x0400 /* DMA supported (DMADIR) */
|
||||
#define ATA_ID_SUP0 82 /* Features supported (1/3) */
|
||||
#define ATA_ID_SUP0_WCACHE 0x0020 /* Write cache supported */
|
||||
#define ATA_ID_SUP1 83 /* Features supported (2/3) */
|
||||
#define ATA_ID_SUP1_VALID_MASK 0xC000 /* Word validity mask */
|
||||
#define ATA_ID_SUP1_VALID 0x4000 /* Word contents are valid */
|
||||
#define ATA_ID_SUP1_FLUSH 0x1000 /* FLUSH CACHE supported */
|
||||
#define ATA_ID_SUP1_LBA48 0x0400 /* 48-bit LBA supported */
|
||||
#define ATA_ID_ENA0 85 /* Features enabled (1/3) */
|
||||
#define ATA_ID_ENA0_WCACHE 0x0020 /* Write cache enabled */
|
||||
#define ATA_ID_ENA2 87 /* Features enabled (3/3) */
|
||||
#define ATA_ID_ENA2_VALID_MASK 0xC000 /* Word validity mask */
|
||||
#define ATA_ID_ENA2_VALID 0x4000 /* Word contents are valid */
|
||||
#define ATA_ID_LBA0 100 /* Max. LBA48 address (LSW) */
|
||||
#define ATA_ID_LBA1 101 /* Max. LBA48 address */
|
||||
#define ATA_ID_LBA2 102 /* Max. LBA48 address */
|
||||
|
@ -84,6 +95,9 @@
|
|||
#define ATA_ID_LSS0 118 /* Logical sector size (LSW) */
|
||||
#define ATA_ID_LSS1 119 /* Logical sector size (MSW) */
|
||||
|
||||
#define ATA_SF_EN_WCACHE 0x02 /* Enable write cache */
|
||||
#define ATA_SF_DI_WCACHE 0x82 /* Disable write cache */
|
||||
|
||||
/* ATAPI constants. */
|
||||
#define ATAPI_PACKET_SIZE 16 /* ATAPI packet size */
|
||||
|
||||
|
@ -254,6 +268,8 @@ enum {
|
|||
#define FLAG_BUSY 0x00000010 /* is an operation ongoing? */
|
||||
#define FLAG_FAILURE 0x00000020 /* did the operation fail? */
|
||||
#define FLAG_BARRIER 0x00000040 /* no access until unset */
|
||||
#define FLAG_HAS_WCACHE 0x00000080 /* is a write cache present? */
|
||||
#define FLAG_HAS_FLUSH 0x00000100 /* is FLUSH CACHE supported? */
|
||||
|
||||
/* Mapping between devices and ports. */
|
||||
#define NO_PORT -1 /* this device maps to no port */
|
||||
|
|
|
@ -2297,6 +2297,7 @@ struct driver *dr;
|
|||
message *m;
|
||||
{
|
||||
int r, timeout, prev;
|
||||
struct command cmd;
|
||||
|
||||
if (m->m_type != DEV_IOCTL_S )
|
||||
return EINVAL;
|
||||
|
@ -2353,6 +2354,20 @@ message *m;
|
|||
return r;
|
||||
|
||||
return OK;
|
||||
} else if (m->REQUEST == DIOCFLUSH) {
|
||||
if (w_prepare(m->DEVICE) == NULL) return ENXIO;
|
||||
|
||||
if (w_wn->state & ATAPI) return EINVAL;
|
||||
|
||||
if (!(w_wn->state & INITIALIZED) && w_specify() != OK)
|
||||
return EIO;
|
||||
|
||||
cmd.command = CMD_FLUSH_CACHE;
|
||||
|
||||
if (com_simple(&cmd) != OK || !w_waitfor(STATUS_BSY, 0))
|
||||
return EIO;
|
||||
|
||||
return (w_wn->w_status & (STATUS_ERR|STATUS_WF)) ? EIO : OK;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
#define CMD_SPECIFY 0x91 /* specify parameters */
|
||||
#define CMD_READ_DMA 0xC8 /* read data using DMA */
|
||||
#define CMD_WRITE_DMA 0xCA /* write data using DMA */
|
||||
#define CMD_FLUSH_CACHE 0xE7 /* flush the write cache */
|
||||
#define ATA_IDENTIFY 0xEC /* identify drive */
|
||||
/* #define REG_CTL 0x206 */ /* control register */
|
||||
#define REG_CTL 0 /* control register */
|
||||
|
|
|
@ -13,5 +13,8 @@
|
|||
#define DIOCEJECT _IO ('d', 5)
|
||||
#define DIOCTIMEOUT _IORW('d', 6, int)
|
||||
#define DIOCOPENCT _IOR('d', 7, int)
|
||||
#define DIOCFLUSH _IO ('d', 8)
|
||||
#define DIOCSETWC _IOW('d', 9, int)
|
||||
#define DIOCGETWC _IOR('d', 10, int)
|
||||
|
||||
#endif /* _S_I_DISK_H */
|
||||
|
|
Loading…
Reference in a new issue