savver писал(а):
Дублирую и сюда код.
В main():
Код:
// 1. HBA reset & rebase
ahci_probe( devs, dev_cnt, (ahci_dev_t*)&ahci_devs, AHCI_PROBE_DEVS, &ports_map);
ahci_hba_reset(&ahci_devs[0]);
ahci_port_rebase_mix(&ahci_devs[0], ahci_devs[0].port, 0, (uint32_t)0xffdf800);
// 2. ATA Identify - work ok
ahci_identify(ahci_devs[0].port, dbuf_stat);
// 3. Test Reading - work ok
memset(dbuf_stat, 0, sizeof(dbuf_stat));
ahci_res = ahci_read(ahci_devs[0].port, WR_SECTOR_ADDR, 0, DATA_SEC_CNT, dbuf_stat);
assert (ahci_res == 0, "ahci_read_res = %d", ahci_res);
// 4. Cycle of writing
ahci_res = 0;
for(int j = 0; j < 5; j++)
{
ahci_res |= ahci_write( ahci_devs[0].port, WR_SECTOR_ADDR + j, 0, DATA_SEC_CNT, dbufw_stat );
//__ahci_flush_cache(ahci_devs[0].port); - not shure, that it works correct
ahci_fis_dump( ahci_devs[0].port );
ahci_port_info(ahci_devs[0].port);
printf("ahci_write_res = %d", ahci_res);
delay_ms(3*1000);
dbufw_stat[0] *= 2;
}
Функция записи (с последними изменениями, уже чуть-чуть отличается от варианта SBUnix за счет вызова start/stop_engine и вариаций флагов w,c,p,control):
Код:
int ahci_write(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_t count, uint16_t * buf)
{
printf("\n=== write (%08x) ===\n", startl);
uintptr_t p;
port->is = 0xffff; // Clear pending interrupt bits
int spin = 0; // Spin lock timeout counter
int slot = find_cmdslot(port);
printf("W0:slot=%d,clf=%d ", slot, sizeof(FIS_REG_H2D)/sizeof(uint32_t));
if (slot == -1)
return EINVAL;
p = port->clb | (port->clbu << 32);
HBA_CMD_HEADER * cmdheader = (HBA_CMD_HEADER *)(p);
cmdheader += slot;
cmdheader->cfl = sizeof(FIS_REG_H2D)/sizeof(uint32_t); // Command FIS size
cmdheader->w = 1; //
cmdheader->c = 1; /** mom_1, must be == 1 (!!!) **/
cmdheader->p = 1; /** mom_2, if==0 hang in 'while(ci != 0)s' (!!!) **/
// 8K bytes (16 sectors) per PRDT
cmdheader->prdtl = (uint16_t)((count-1)>>4) + 1; // PRDT entries count
p = cmdheader->ctba | (cmdheader->ctbau << 32);
HBA_CMD_TBL * cmdtbl = (HBA_CMD_TBL *)(p);
memset(cmdtbl, 0, sizeof(HBA_CMD_TBL) + (cmdheader->prdtl-1)*sizeof(HBA_PRDT_ENTRY));
int i = 0;
// 8K bytes (16 sectors) per PRDT
for (i=0; i<cmdheader->prdtl-1; i++)
{
cmdtbl->prdt_entry[i].dba = (uint32_t)((uint64_t)buf & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbau = (uint32_t)(((uint64_t)buf >> 32) & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbc = 8*1024-1; // 8K bytes
cmdtbl->prdt_entry[i].i = 0;
buf += 4*1024; // 4K words
count -= 16; // 16 sectors
}
/**If the final Data FIS transfer in a command is for an odd number of 16-bit words, the transmitter�s
Transport layer is responsible for padding the final Dword of a FIS with zeros. If the HBA receives one
more word than is indicated in the PRD table due to this padding requirement, the HBA shall not signal
this as an overflow condition. In addition, if the HBA inserts padding as required in a FIS it is transmitting,
an overflow error shall not be indicated. The PRD Byte Count field shall be updated based on the
number of words specified in the PRD table, ignoring any additional padding.**/
// Last entry
cmdtbl->prdt_entry[i].dba = (uint32_t)((uint64_t)buf & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbau = (uint32_t)(((uint64_t)buf >> 32) & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbc = count << 9 ; // 512 bytes per sector
cmdtbl->prdt_entry[i].i = 1; // interrupt gen
// Setup command
FIS_REG_H2D * cmdfis = (FIS_REG_H2D *)(&cmdtbl->cfis);
memset(cmdfis, 0, sizeof(FIS_REG_H2D));
cmdfis->fis_type = FIS_TYPE_REG_H2D;
cmdfis->c = 1; // Command
cmdfis->command = ATA_CMD_WRITE_DMA_EX;
cmdfis->lba0 = (uint8_t)startl;
cmdfis->lba1 = (uint8_t)(startl >> 8);
cmdfis->lba2 = (uint8_t)(startl >> 16);
cmdfis->device = 1 << 6 ; // LBA mode //0xE0;
cmdfis->lba3 = (uint8_t)(startl >> 24);
cmdfis->lba4 = (uint8_t)starth;
cmdfis->lba5 = (uint8_t)(starth >> 8);
cmdfis->countl = count & 0xff;
cmdfis->counth = count >> 8;
cmdfis->control = 8; /** mom_3. also try write 0x08, 0x00 (!!!) **/
// The below loop waits until the port is no longer busy before issuing
// a new command
while((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 10000000)
{
spin++;
}
if(spin == 10000000)
{
printf("Port is hung %d\n", EDEADLK);
return EDEADLK;
}
port->ci = 1 << slot; // Issue command
_ahci_engine_start(port); /** mom_4 **/
printf("W1:port->cmd=%08x,ci=%08x,tfd=%08x,sact=%08x ", port->cmd,port->ci, port->tfd, port->sact);
delay_ms(1); //debug only
// Wait for completion
uint32_t cntr = 0;
while (1)
{
// In some longer duration reads, it may be helpful to spin on the DPS bit
// in the PxIS port field as well (1 << 5)
if ((port->ci & (1<<slot)) == 0)
break;
if (port->is & HBA_PxIS_TFES) // Task file error
{
printf("Read disk error\n");
return EIO;
}
if(cntr++ > 1000*1000)
{
printf("WE:port->ci=%08x,is=%08x,tfd=%08x,sact=%08x ", port->ci, port->is, port->tfd, port->sact);
_ahci_engine_stop(port);
while(1);
}
}
printf("W2:port->ci=%08x,tfd=%08x,sact=%08x ", port->ci, port->tfd, port->sact);
// Check again
if (port->is & HBA_PxIS_TFES)
{
printf("Read disk error\n");
_ahci_engine_stop(port);
return EIO;
}
while(port->ci != 0)
{
// print("[%d]", k++);
}
printf("W:ok (ci=%d,sig=%08x)\n", port->ci,port->sig);
_ahci_engine_stop(port); /** mom_4 **/
return 0;
}
Код:
void _ahci_engine_stop(HBA_PORT* port)
{
port->cmd &= ~(1 << 0);
port->cmd &= ~(1 << 4); //FRE
while(1)
{
if(port->cmd & AHCI_PORT_CMD_FIS_ON) //FR
continue;
if(port->cmd & AHCI_PORT_CMD_LIST_ON) //CR
continue;
break;
}
}
//---------------------------------------------
void _ahci_engine_start(HBA_PORT* port)
{
port->cmd |= (1 << 4);
port->cmd |= (1 << 0);
port->is = 0;
}
Несколько замечаний/наблюдений по коду (в коде помечены /** mom.x **/ )...
mom.1, mom.2
(_ahci_start_engine/stop_engine вызываются, между вызовами функции записи пауза = 3 sec)
1) Если "cmdheader->c = 0, cmdheader->p->0", функция зависает на проверке "while(port->ci != 0)" (распечатка где "WE": port->ci = 1, port->is = 0x01000020, port->tfd = 0xD0, port->sact = 0)
2) If "cmdheader->c = 1, cmdheader->p->0", 1й вызов функции завершается успешно (port->ci = 0, port->tfd = 0x150, port->sact = 0), но при 2м вызове функции зависаем в проверке окончания операции "while(port->ci != 0)" (port->ci = 1, port->is = 0, port->tfd = 0xD0).
3) If "cmdheader->c = 1, cmdheader->p->1" все вызовы функции записи завершаются успешно!!! Последующий цикл чтения показывает, что во все 5 секторов данные записались верно, но... если уменьшим паузу между вызовами функции записи (например, до 2х сек), то 2й вызов функции завершается ошибкой (port->ci = 0, port->is=0x41000001, port->tfd = 0x8451; fis_d2h: sts=0x51,err=0x84)... кеш? в списке ATA команд есть FLUSH CACHE, но видел в примерах осей (маленьких), чтобы его использовали.
4) mom.3, "cmdfis->control = 0" or "= 8"не влияет
5) mom4... why call "_ahci_start_engine"/"_ahci_stop_engine", т.е. при каждой операции port_engine запускается, после завершения - останавливается... я увидел этот момент в одной из baremetal реализации... С таким подходом и большой паузой
я получил вариант, который пишет в цикле, причем данные записываются верно... Хотя во многих других реализациях я не видел, чтобы engine так часто дергали.
Если закоментировать вызовы stop/start_engine, 1й вызов функции записи завершается без ошибок, 2й с ошибкой (port->ci = 0, port->is = 0x41000021, port->tfd = 0x8451).