#include "opt_ata.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/taskqueue.h>
#include <sys/mplock2.h>
#include "ata-all.h"
#include "ata-pci.h"
#include "ata_if.h"
void
ata_sata_phy_event(void *context, int dummy)
{
struct ata_connect_task *tp = (struct ata_connect_task *)context;
struct ata_channel *ch = device_get_softc(tp->dev);
device_t *children;
int nchildren, i;
get_mplock();
if (tp->action == ATA_C_ATTACH) {
if (bootverbose)
device_printf(tp->dev, "CONNECTED\n");
ATA_RESET(tp->dev);
ata_identify(tp->dev);
}
if (tp->action == ATA_C_DETACH) {
if (!device_get_children(tp->dev, &children, &nchildren)) {
for (i = 0; i < nchildren; i++)
if (children[i])
device_delete_child(tp->dev, children[i]);
kfree(children, M_TEMP);
}
lockmgr(&ch->state_mtx, LK_EXCLUSIVE);
ch->state = ATA_IDLE;
lockmgr(&ch->state_mtx, LK_RELEASE);
if (bootverbose)
device_printf(tp->dev, "DISCONNECTED\n");
}
rel_mplock();
kfree(tp, M_ATA);
}
void
ata_sata_phy_check_events(device_t dev)
{
struct ata_channel *ch = device_get_softc(dev);
u_int32_t error = ATA_IDX_INL(ch, ATA_SERROR);
ATA_IDX_OUTL(ch, ATA_SERROR, error);
if (error) {
struct ata_connect_task *tp;
u_int32_t status = ATA_IDX_INL(ch, ATA_SSTATUS);
if ((error & ATA_SE_PHY_CHANGED) &&
(tp = (struct ata_connect_task *)
kmalloc(sizeof(struct ata_connect_task),
M_ATA, M_INTWAIT | M_ZERO))) {
if (((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1) ||
((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN2)) {
if (bootverbose)
device_printf(ch->dev, "CONNECT requested\n");
tp->action = ATA_C_ATTACH;
}
else {
if (bootverbose)
device_printf(ch->dev, "DISCONNECT requested\n");
tp->action = ATA_C_DETACH;
}
tp->dev = ch->dev;
TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
taskqueue_enqueue(taskqueue_thread[mycpuid], &tp->task);
}
}
}
static int
ata_sata_connect(struct ata_channel *ch)
{
u_int32_t status;
int timeout;
for (timeout = 0; timeout < 100 ; timeout++) {
status = ATA_IDX_INL(ch, ATA_SSTATUS);
if ((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1 ||
(status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN2)
break;
ata_udelay(10000);
}
if (timeout >= 100) {
if (bootverbose)
device_printf(ch->dev, "SATA connect status=%08x\n", status);
return 0;
}
if (bootverbose)
device_printf(ch->dev, "SATA connect time=%dms\n", timeout * 10);
ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
return 1;
}
int
ata_sata_phy_reset(device_t dev)
{
struct ata_channel *ch = device_get_softc(dev);
int loop, retry;
if ((ATA_IDX_INL(ch, ATA_SCONTROL) & ATA_SC_DET_MASK) == ATA_SC_DET_IDLE)
return ata_sata_connect(ch);
for (retry = 0; retry < 10; retry++) {
for (loop = 0; loop < 10; loop++) {
ATA_IDX_OUTL(ch, ATA_SCONTROL, ATA_SC_DET_RESET);
ata_udelay(100);
if ((ATA_IDX_INL(ch, ATA_SCONTROL) & ATA_SC_DET_MASK) ==
ATA_SC_DET_RESET)
break;
}
ata_udelay(5000);
for (loop = 0; loop < 10; loop++) {
ATA_IDX_OUTL(ch, ATA_SCONTROL, ATA_SC_DET_IDLE |
ATA_SC_IPM_DIS_PARTIAL |
ATA_SC_IPM_DIS_SLUMBER);
ata_udelay(100);
if ((ATA_IDX_INL(ch, ATA_SCONTROL) & ATA_SC_DET_MASK) == 0)
return ata_sata_connect(ch);
}
}
return 0;
}
void
ata_sata_setmode(device_t dev, int mode)
{
struct ata_device *atadev = device_get_softc(dev);
if (atadev->param.satacapabilities != 0x0000 &&
atadev->param.satacapabilities != 0xffff) {
struct ata_channel *ch = device_get_softc(device_get_parent(dev));
ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0,
ata_limit_mode(dev, mode, ATA_UDMA6));
if (ch->r_io[ATA_SSTATUS].res &&
((ATA_IDX_INL(ch, ATA_SSTATUS) & ATA_SS_CONWELL_MASK) ==
ATA_SS_CONWELL_GEN2))
atadev->mode = ATA_SA300;
else
atadev->mode = ATA_SA150;
}
else {
mode = ata_limit_mode(dev, mode, ATA_UDMA5);
if (!ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode))
atadev->mode = mode;
}
}
int
ata_request2fis_h2d(struct ata_request *request, u_int8_t *fis)
{
struct ata_device *atadev = device_get_softc(request->dev);
if (request->flags & ATA_R_ATAPI) {
fis[0] = 0x27;
fis[1] = 0x80 | (atadev->unit & 0x0f);
fis[2] = ATA_PACKET_CMD;
if (request->flags & (ATA_R_READ | ATA_R_WRITE))
fis[3] = ATA_F_DMA;
else {
fis[5] = request->transfersize;
fis[6] = request->transfersize >> 8;
}
fis[7] = ATA_D_LBA;
fis[15] = ATA_A_4BIT;
return 20;
}
else {
ata_modify_if_48bit(request);
fis[0] = 0x27;
fis[1] = 0x80 | (atadev->unit & 0x0f);
fis[2] = request->u.ata.command;
fis[3] = request->u.ata.feature;
fis[4] = request->u.ata.lba;
fis[5] = request->u.ata.lba >> 8;
fis[6] = request->u.ata.lba >> 16;
fis[7] = ATA_D_LBA;
if (!(atadev->flags & ATA_D_48BIT_ACTIVE))
fis[7] |= (ATA_D_IBM | (request->u.ata.lba >> 24 & 0x0f));
fis[8] = request->u.ata.lba >> 24;
fis[9] = request->u.ata.lba >> 32;
fis[10] = request->u.ata.lba >> 40;
fis[11] = request->u.ata.feature >> 8;
fis[12] = request->u.ata.count;
fis[13] = request->u.ata.count >> 8;
fis[15] = ATA_A_4BIT;
return 20;
}
return 0;
}