| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852 |
- /*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-08-04 JasonHu first version
- */
- #include <rtconfig.h>
- #ifdef BSP_DRV_AHCI
- #include <rtthread.h>
- #include <rtdef.h>
- #include <rtdbg.h>
- #include <rthw.h>
- #include <ioremap.h>
- #include <dma.h>
- #include <mmu.h>
- #include <board.h>
- #include "drv_ahci.h"
- #include "pci.h"
- #define DEV_NAME "sd"
- // #define RT_DRV_AHCI_DEBUG
- #ifdef RT_DRV_AHCI_DEBUG
- #define dbgprint rt_kprintf
- #else
- #define dbgprint(...)
- #endif
- /* memio info on the pci bar 5 */
- #define PCI_AHCI_MEMIO_BAR 5
- #define LOWER32(a) (rt_uint32_t)((a) & 0xffffffff)
- #define LOWER8(a) (rt_uint8_t)((a) & 0xff)
- #define HIGHER8(a) (rt_uint8_t)(((a) >> 8) & 0xff)
- /* maxim ports we support */
- #define DRV_AHCI_PORT_NR 32
- struct device_extension
- {
- rt_uint64_t sector_count; /* sectors in this disk. */
- rt_uint8_t type; /* AHCI device type */
- rt_uint8_t port; /* port for each device. */
- rt_uint32_t slots; /* solts for device read/write transfer bits */
- struct rt_mutex lock; /* lock for disk read/write */
- void *fis_vaddr;
- void *clb_vaddr;
- rt_hw_dma_t clb_dma;
- rt_hw_dma_t fis_dma;
- void *cmd_hdrs[HBA_COMMAND_HEADER_NUM]; /* command header */
- rt_hw_dma_t cmd_hdrs_dmas[HBA_COMMAND_HEADER_NUM]; /* command header dma */
- };
- typedef struct device_extension rt_device_extension_t;
- static struct hba_memory *g_hba_base; /* hba memory io base addr */
- static rt_err_t ahci_create_device(rt_device_extension_t *extension);
- static rt_uint32_t ahci_flush_commands(struct hba_port *port)
- {
- /* the commands may not take effect until the command
- * register is read again by software, because reasons.
- */
- rt_hw_dsb();
- volatile rt_uint32_t c = port->command;
- rt_hw_dmb();
- return c;
- }
- static void ahci_stop_port_command_engine(struct hba_port *port)
- {
- rt_hw_dsb();
- port->command &= ~HBA_PxCMD_ST;
- rt_hw_dsb();
- port->command &= ~HBA_PxCMD_FRE;
- rt_hw_dmb();
- while((port->command & HBA_PxCMD_CR) || (port->command & HBA_PxCMD_FR))
- {
- rt_hw_cpu_pause();
- }
- }
- static void ahci_start_port_command_engine(struct hba_port *port)
- {
- rt_hw_dmb();
- while(port->command & HBA_PxCMD_CR)
- {
- rt_hw_cpu_pause();
- }
- rt_hw_dsb();
- port->command |= HBA_PxCMD_FRE;
- rt_hw_dsb();
- port->command |= HBA_PxCMD_ST;
- ahci_flush_commands((struct hba_port *)port);
- }
- static struct hba_command_header *ahci_initialize_command_header(rt_device_extension_t *dev, struct hba_memory *abar,
- struct hba_port *port, int slot, int write,
- int atapi, int prd_entries, int fis_len)
- {
- struct hba_command_header *hdr = (struct hba_command_header *)dev->clb_vaddr;
- hdr += slot;
- hdr->write = write ? 1 : 0;
- hdr->prdb_count = 0;
- hdr->atapi=atapi ? 1 : 0;
- hdr->fis_length = fis_len;
- hdr->prdt_len = prd_entries;
- hdr->prefetchable = 0;
- hdr->bist = 0;
- hdr->pmport = 0;
- hdr->reset = 0;
- return hdr;
- }
- static struct fis_reg_host_to_device *ahci_initialize_fis_host_to_device(rt_device_extension_t *dev, struct hba_memory *abar,
- struct hba_port *port, int slot, int cmdctl, int ata_command)
- {
- struct hba_command_table *tbl = (struct hba_command_table *)(dev->cmd_hdrs[slot]);
- struct fis_reg_host_to_device *fis = (struct fis_reg_host_to_device *)(tbl->command_fis);
- rt_memset(fis, 0, sizeof(*fis));
- fis->fis_type = FIS_TYPE_REG_H2D;
- fis->command = ata_command;
- fis->c = cmdctl ? 1 : 0;
- return fis;
- }
- static void ahci_send_command(struct hba_port *port, int slot)
- {
- port->interrupt_status = ~0;
- port->command_issue = (1 << slot);
- ahci_flush_commands(port);
- }
- static int ahci_write_prdt(rt_device_extension_t *dev, struct hba_memory *abar, struct hba_port *port,
- int slot, int offset, int length, rt_ubase_t virt_buffer)
- {
- int num_entries = ((length - 1) / PRDT_MAX_COUNT) + 1;
- struct hba_command_table *tbl = (struct hba_command_table *)(dev->cmd_hdrs[slot]);
- int i;
- struct hba_prdt_entry *prd;
- for(i = 0; i < num_entries - 1; i++)
- {
- rt_ubase_t phys_buffer;
- phys_buffer = rt_hw_vir2phy(virt_buffer);
- prd = &tbl->prdt_entries[i + offset];
- prd->byte_count = PRDT_MAX_COUNT - 1;
- prd->data_base_l = LOWER32(phys_buffer);
- prd->data_base_h = 0;
- prd->interrupt_on_complete = 0;
- length -= PRDT_MAX_COUNT;
- virt_buffer += PRDT_MAX_COUNT;
- }
- rt_ubase_t phys_buffer;
- phys_buffer = rt_hw_vir2phy(virt_buffer);
- prd = &tbl->prdt_entries[i + offset];
- prd->byte_count = length - 1;
- prd->data_base_l = LOWER32(phys_buffer);
- prd->data_base_h = 0;
- prd->interrupt_on_complete = 0;
- return num_entries;
- }
- static void ahci_reset_device(struct hba_memory *abar, struct hba_port *port, rt_device_extension_t *dev)
- {
- dbgprint("[ahci] device port %d: sending COMRESET and reinitializing\n", dev->port);
- ahci_stop_port_command_engine(port);
- port->sata_error = ~0;
- /* power on, spin up */
- port->command |= 2;
- port->command |= 4;
- ahci_flush_commands(port);
- rt_thread_mdelay(1);
- /* initialize state */
- port->interrupt_status = ~0; /* clear pending interrupts */
- port->interrupt_enable = AHCI_DEFAULT_INT; /* we want some interrupts */
- port->command &= ~((1 << 27) | (1 << 26)); /* clear some bits */
- port->sata_control |= 1;
- rt_thread_mdelay(10);
- port->sata_control |= (~1);
- rt_thread_mdelay(10);
- port->interrupt_status = ~0; /* clear pending interrupts */
- port->interrupt_enable = AHCI_DEFAULT_INT; /* we want some interrupts */
- ahci_start_port_command_engine(port);
- dev->slots = 0;
- port->sata_error = ~0;
- }
- static rt_err_t ahci_port_dma_data_transfer(rt_device_extension_t *dev, struct hba_memory *abar, struct hba_port *port,
- int slot, int write, rt_ubase_t virt_buffer, int sectors, rt_uint64_t lba)
- {
- struct fis_reg_host_to_device *fis;
- int timeout;
- int fis_len = sizeof(struct fis_reg_host_to_device) / 4;
- int ne = ahci_write_prdt(dev, abar, port, slot, 0, ATA_SECTOR_SIZE * sectors, virt_buffer);
- ahci_initialize_command_header(dev, abar, port, slot, write, 0, ne, fis_len);
- fis = ahci_initialize_fis_host_to_device(dev, abar, port, slot, 1, write ? ATA_CMD_WRITE_DMA_EX : ATA_CMD_READ_DMA_EX);
- fis->device = 1 << 6;
- fis->count_l = LOWER8(sectors);
- fis->count_h = HIGHER8(sectors);
- fis->lba0 = (unsigned char)( lba & 0xFF);
- fis->lba1 = (unsigned char)((lba >> 8) & 0xFF);
- fis->lba2 = (unsigned char)((lba >> 16) & 0xFF);
- fis->lba3 = (unsigned char)((lba >> 24) & 0xFF);
- fis->lba4 = (unsigned char)((lba >> 32) & 0xFF);
- fis->lba5 = (unsigned char)((lba >> 40) & 0xFF);
- port->sata_error = ~0;
- timeout = ATA_TFD_TIMEOUT;
- while ((port->task_file_data & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && --timeout)
- {
- rt_thread_yield();
- }
- if(!timeout)
- {
- goto port_hung;
- }
- port->sata_error = ~0;
- ahci_send_command(port, slot);
- timeout = ATA_TFD_TIMEOUT;
- while ((port->task_file_data & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && --timeout)
- {
- rt_thread_yield();
- }
- if(!timeout)
- {
- goto port_hung;
- }
- timeout = AHCI_CMD_TIMEOUT;
- while(--timeout)
- {
- if(!((port->sata_active | port->command_issue) & (1 << slot)))
- break;
- rt_thread_yield();
- }
- if(!timeout)
- {
- goto port_hung;
- }
- if(port->sata_error)
- {
- dbg_log(DBG_ERROR, "[ahci] device %d: ahci error\n", dev->port);
- goto error;
- }
- if(port->task_file_data & ATA_DEV_ERR)
- {
- dbg_log(DBG_ERROR, "[ahci] device %d: task file data error\n", dev->port);
- goto error;
- }
- return RT_EOK;
- port_hung:
- dbg_log(DBG_ERROR, "[ahci] device %d: port hung\n", dev->port);
- error:
- dbg_log(DBG_ERROR, "[ahci] device %d: tfd=%x, serr=%x\n",
- dev->port, port->task_file_data, port->sata_error);
- ahci_reset_device(abar, port, dev);
- return RT_ERROR;
- }
- static rt_err_t ahci_device_identify(rt_device_extension_t *dev, struct hba_memory *abar, struct hba_port *port)
- {
- int fis_len = sizeof(struct fis_reg_host_to_device) / 4;
- rt_hw_dma_t dma;
- dma.size = 0x1000;
- dma.alignment = 0x1000;
- RT_ASSERT(rt_hw_dma_alloc(&dma) == RT_EOK);
- ahci_write_prdt(dev, abar, port, 0, 0, 512, (rt_ubase_t)dma.vaddr);
- ahci_initialize_command_header(dev, abar, port, 0, 0, 0, 1, fis_len);
- ahci_initialize_fis_host_to_device(dev, abar, port, 0, 1, ATA_CMD_IDENTIFY);
- int timeout = ATA_TFD_TIMEOUT;
- port->sata_error = ~0;
- while ((port->task_file_data & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && --timeout)
- {
- rt_hw_cpu_pause();
- }
- if(!timeout )
- {
- dbg_log(DBG_ERROR, "[ahci] device %d: identify 1: port hung\n", dev->port);
- dbg_log(DBG_ERROR, "[ahci] device %d: identify 1: tfd=%x, serr=%x\n",
- dev->port, port->task_file_data, port->sata_error);
- rt_hw_dma_free(&dma);
- return RT_ETIMEOUT;
- }
- ahci_send_command(port, 0);
- timeout = AHCI_CMD_TIMEOUT;
- while(--timeout)
- {
- if(!((port->sata_active | port->command_issue) & 1))
- break;
- }
- if(!timeout)
- {
- dbg_log(DBG_ERROR, "[ahci] device %d: identify 2: port hung\n", dev->port);
- dbg_log(DBG_ERROR, "[ahci] device %d: identify 2: tfd=%x, serr=%x\n",
- dev->port, port->task_file_data, port->sata_error);
- rt_hw_dma_free(&dma);
- return RT_ETIMEOUT;
- }
- struct ata_identify *identify = (struct ata_identify *) dma.vaddr;
- if (identify->lba48_addressable_sectors)
- {
- dev->sector_count = identify->lba48_addressable_sectors;
- }
- else
- {
- dev->sector_count = 0;
- }
- dbgprint("[ahci] device %d: num sectors=%d\n", dev->port, dev->sector_count);
- rt_hw_dma_free(&dma);
- if (!dev->sector_count)
- {
- dbg_log(DBG_ERROR, "[ahci] device %d invalid sectors ZERO.\n", dev->port);
- return RT_EINVAL;
- }
- return RT_EOK;
- }
- static rt_uint32_t ahci_check_type(volatile struct hba_port *port)
- {
- port->command &= ~1;
- while(port->command & (1 << 15))
- {
- rt_hw_cpu_pause();
- }
- port->command &= ~(1 << 4);
- while(port->command & (1 << 14))
- {
- rt_hw_cpu_pause();
- }
- rt_hw_dsb();
- port->command |= 2;
- rt_hw_dsb();
- rt_thread_mdelay(10);
- rt_uint32_t s = port->sata_status;
- uint8_t ipm, det;
- ipm = (s >> 8) & 0x0F;
- det = s & 0x0F;
- if(ipm != HBA_PORT_IPM_ACTIVE || det != HBA_PORT_DET_PRESENT)
- {
- return AHCI_DEV_NULL;
- }
- switch (port->signature)
- {
- case SATA_SIG_ATAPI:
- return AHCI_DEV_SATAPI;
- case SATA_SIG_SEMB:
- return AHCI_DEV_SEMB;
- case SATA_SIG_PM:
- return AHCI_DEV_PM;
- default:
- return AHCI_DEV_SATA;
- }
- return AHCI_DEV_SATA;
- }
- int ahci_initialize_device(rt_device_extension_t *dev, struct hba_memory *abar)
- {
- struct hba_port *port = (struct hba_port *)&abar->ports[dev->port];
- ahci_stop_port_command_engine(port);
- port->sata_error = ~0;
- /* power on, spin up */
- port->command |= (2 | 4);
- ahci_flush_commands(port);
- rt_thread_mdelay(2);
- /* initialize state */
- port->interrupt_status = ~0; /* clear pending interrupts */
- port->interrupt_enable = AHCI_DEFAULT_INT; /* we want some interrupts */
- port->command &= ~1;
- while(port->command & (1 << 15))
- {
- rt_hw_cpu_pause();
- }
- port->command &= ~((1 << 27) | (1 << 26) | 1); /* clear some bits */
- ahci_flush_commands(port);
- /* start reset sata */
- port->sata_control |= 1;
- rt_thread_mdelay(20);
- /* close DET, after init sata device done. */
- port->sata_control &= (~1);
- rt_thread_mdelay(10);
- while(!(port->sata_status & 1))
- {
- rt_hw_cpu_pause();
- }
- port->sata_error = ~0;
- port->command |= (1 << 28); /* set interface to active */
- while((port->sata_status >> 8) != 1)
- {
- rt_hw_cpu_pause();
- }
- port->interrupt_status = ~0; /* clear pending interrupts */
- port->interrupt_enable = AHCI_DEFAULT_INT; /* we want some interrupts */
- rt_ubase_t clb_phys, fis_phys;
- dev->clb_dma.size = 0x2000;
- dev->clb_dma.alignment = 0x1000;
- dev->fis_dma.size = 0x1000;
- dev->fis_dma.alignment = 0x1000;
- RT_ASSERT(rt_hw_dma_alloc(&dev->clb_dma) == RT_EOK);
- RT_ASSERT(rt_hw_dma_alloc(&dev->fis_dma) == RT_EOK);
- dev->clb_vaddr = (void *)dev->clb_dma.vaddr;
- dev->fis_vaddr = (void *)dev->fis_dma.vaddr;
- clb_phys = dev->clb_dma.paddr;
- fis_phys = dev->fis_dma.paddr;
- dev->slots=0;
- struct hba_command_header *hdr = (struct hba_command_header *)dev->clb_vaddr;
- int i;
- for(i = 0; i < HBA_COMMAND_HEADER_NUM; i++)
- {
- dev->cmd_hdrs_dmas[i].size = 0x1000;
- dev->cmd_hdrs_dmas[i].alignment = 0x1000;
- RT_ASSERT(rt_hw_dma_alloc(&dev->cmd_hdrs_dmas[i]) == RT_EOK);
- dev->cmd_hdrs[i] = (void *)dev->cmd_hdrs_dmas[i].vaddr;
- rt_memset(hdr, 0, sizeof(*hdr));
- hdr->command_table_base_l = LOWER32(dev->cmd_hdrs_dmas[i].paddr);
- hdr->command_table_base_h = 0;
- hdr++;
- }
- port->command_list_base_l = LOWER32(clb_phys);
- port->command_list_base_h = 0;
- port->fis_base_l = LOWER32(fis_phys);
- port->fis_base_h = 0;
- ahci_start_port_command_engine(port);
- port->sata_error = ~0;
- return ahci_device_identify(dev, abar, port);
- }
- static rt_uint32_t ahci_probe_ports(struct hba_memory *abar)
- {
- rt_uint32_t pi = abar->port_implemented;
- dbgprint("[ahci] ports implemented: %x\n", pi);
- int counts = 0; /* exist device count */
- int i = 0;
- rt_device_extension_t *extension;
- while (i < DRV_AHCI_PORT_NR)
- {
- if (pi & 1)
- {
- rt_uint32_t type = ahci_check_type(&abar->ports[i]);
- if (type == AHCI_DEV_SATA) { /* SATA device */
- dbgprint("[ahci] detected SATA device on port %d\n", i);
- extension = rt_malloc(sizeof(rt_device_extension_t));
- if (extension == RT_NULL)
- {
- dbg_log(DBG_ERROR, "[ahci] port %d alloc memory for extension failed!\n", i);
- return counts;
- }
- extension->type = type;
- extension->port = i;
- rt_mutex_init(&extension->lock, "ahci", RT_IPC_FLAG_PRIO);
- if (ahci_initialize_device(extension, abar) == RT_EOK)\
- {
- if (ahci_create_device(extension) == RT_EOK) {
- counts++;
- }
- else
- {
- dbg_log(DBG_ERROR, "[ahci] failed to create device %d, disabling port!\n", i);
- rt_free(extension);
- }
- } else {
- dbg_log(DBG_ERROR, "[ahci] failed to initialize device %d, disabling port.\n", i);
- }
- } else if(type == AHCI_DEV_SATAPI) { /* SATAPI device */
- dbg_log(DBG_WARNING, "[ahci] not support SATAPI device on port %d now!\n", i);
- } else if(type == AHCI_DEV_PM) { /* PM device */
- dbg_log(DBG_WARNING, "[ahci] not support Port multiplier on port %d now!\n", i);
- } else if(type == AHCI_DEV_SEMB) { /* SEMB device */
- dbg_log(DBG_WARNING, "[ahci] not support Enclosure management bridge on port %d now!\n", i);
- }
- /* do not deal other type now. */
- }
- i++;
- pi >>= 1;
- }
- return counts;
- }
- static int ahci_port_get_slot(rt_device_extension_t *dev)
- {
- for(;;)
- {
- int i;
- rt_mutex_take(&dev->lock, RT_WAITING_FOREVER);
- for(i = 0; i < DRV_AHCI_PORT_NR; i++)
- {
- if(!(dev->slots & (1 << i)))
- {
- dev->slots |= (1 << i);
- rt_mutex_release(&dev->lock);
- return i;
- }
- }
- rt_mutex_release(&dev->lock);
- rt_thread_yield();
- }
- }
- void ahci_port_put_slot(rt_device_extension_t *dev, int slot)
- {
- rt_mutex_take(&dev->lock, RT_WAITING_FOREVER);
- dev->slots &= ~(1 << slot);
- rt_mutex_release(&dev->lock);
- }
- /* since a DMA transfer must write to contiguous physical RAM, we need to allocate
- * buffers that allow us to create PRDT entries that do not cross a page boundary.
- * That means that each PRDT entry can transfer a maximum of PAGE_SIZE bytes (for
- * 0x1000 page size, that's 8 sectors). Thus, we allocate a buffer that is page aligned,
- * in a multiple of PAGE_SIZE, so that the PRDT will write to contiguous physical ram
- * (the key here is that the buffer need not be contiguous across multiple PRDT entries).
- */
- static rt_size_t ahci_rw_multiple_do(rt_device_extension_t *dev, int rw, rt_uint64_t blk, unsigned char *out_buffer, int count)
- {
- rt_uint32_t length = count * ATA_SECTOR_SIZE;
- rt_uint64_t end_blk = dev->sector_count;
- if (blk >= end_blk)
- {
- dbg_log(DBG_ERROR, "ahci: lba %d out of range %d\n", blk, end_blk);
- return 0;
- }
- if((blk + count) > end_blk)
- {
- count = end_blk - blk;
- }
- if(!count)
- {
- return 0;
- }
- int num_pages = ((ATA_SECTOR_SIZE * (count - 1)) / PAGE_SIZE) + 1;
- RT_ASSERT((length <= (unsigned)num_pages * 0x1000));
- rt_hw_dma_t dma;
- dma.size = 0x1000 * num_pages;
- dma.alignment = 0x1000;
- RT_ASSERT(rt_hw_dma_alloc(&dma) == RT_EOK);
- rt_size_t num_read_blocks = count;
- struct hba_port *port = (struct hba_port *)&g_hba_base->ports[dev->port];
- if(rw == 1)
- {
- rt_memcpy((void *)dma.vaddr, out_buffer, length);
- }
- int slot = ahci_port_get_slot(dev);
- if(ahci_port_dma_data_transfer(dev, g_hba_base, port, slot, rw == 1 ? 1 : 0, (rt_ubase_t)dma.vaddr, count, blk) != RT_EOK)
- {
- num_read_blocks = 0;
- }
- ahci_port_put_slot(dev, slot);
- if(rw == 0 && num_read_blocks)
- {
- rt_memcpy(out_buffer, (void *)dma.vaddr, length);
- }
- rt_hw_dma_free(&dma);
- return num_read_blocks;
- }
- /* and then since there is a maximum transfer amount because of the page size
- * limit, wrap the transfer function to allow for bigger transfers than that even.
- */
- static rt_size_t ahci_rw_multiple(rt_device_extension_t *dev, int rw, rt_uint64_t blk, unsigned char *out_buffer, int count)
- {
- int i = 0;
- rt_size_t ret = 0;
- int c = count;
- for(i = 0; i < count; i += (PRDT_MAX_ENTRIES * PRDT_MAX_COUNT) / ATA_SECTOR_SIZE)
- {
- int n = (PRDT_MAX_ENTRIES * PRDT_MAX_COUNT) / ATA_SECTOR_SIZE;
- if(n > c)
- {
- n = c;
- }
- ret += ahci_rw_multiple_do(dev, rw, blk+i, out_buffer + ret, n);
- c -= n;
- }
- return ret;
- }
- static rt_pci_device_t *ahci_get_pci_info(void)
- {
- rt_pci_device_t *ahci = rt_pci_device_get(0x1, 0x6);
- if(ahci == RT_NULL)
- {
- ahci = rt_pci_device_get(0x8086, 0x8c03);
- }
- if(ahci == RT_NULL)
- {
- ahci = rt_pci_device_get(0x8086, 0x2922);
- }
- if(ahci == RT_NULL)
- {
- return RT_NULL;
- }
- dbgprint("[ahci] device vendorID %x deviceID %x class code %x\n", ahci->vendor_id, ahci->device_id, ahci->class_code);
- rt_pci_enable_bus_mastering(ahci);
- g_hba_base = rt_ioremap((void *) ahci->bars[PCI_AHCI_MEMIO_BAR].base_addr, ahci->bars[PCI_AHCI_MEMIO_BAR].length);
- if (g_hba_base == RT_NULL) {
- dbgprint("[ahci] device memio_remap on %x length %x failed!\n", ahci->bars[PCI_AHCI_MEMIO_BAR].base_addr, ahci->bars[PCI_AHCI_MEMIO_BAR].length);
- return RT_NULL;
- }
- mmu_flush_tlb();
- dbgprint("[ahci] mapping memory iobase from paddr %x to vaddr %x\n", ahci->bars[PCI_AHCI_MEMIO_BAR].base_addr, g_hba_base);
- dbgprint("[ahci] using interrupt %d\n", ahci->irq_line);
- return ahci;
- }
- static rt_err_t rt_ahci_init(rt_device_t dev)
- {
- return RT_EOK;
- }
- static rt_err_t rt_ahci_open(rt_device_t dev, rt_uint16_t oflag)
- {
- return RT_EOK;
- }
- static rt_err_t rt_ahci_close(rt_device_t dev)
- {
- return RT_EOK;
- }
- /*
- * position: block page address, not bytes address
- * buffer: read buffer addr
- * size : how many blocks
- */
- static rt_size_t rt_ahci_read(rt_device_t device, rt_off_t position, void *buffer, rt_size_t size)
- {
- return ahci_rw_multiple((rt_device_extension_t *)device->user_data, 0, position, (unsigned char *)buffer, size);
- }
- /*
- * position: block page address, not bytes address
- * buffer: write buffer addr
- * size : how many blocks
- */
- static rt_size_t rt_ahci_write(rt_device_t device, rt_off_t position, const void *buffer, rt_size_t size)
- {
- return ahci_rw_multiple((rt_device_extension_t *)device->user_data, 1, position, (unsigned char *)buffer, size);
- }
- static rt_err_t rt_ahci_control(rt_device_t dev, int cmd, void *args)
- {
- RT_ASSERT(dev != RT_NULL);
- RT_ASSERT(dev->user_data != NULL);
- RT_ASSERT(args != RT_NULL);
- rt_device_extension_t *extension = (rt_device_extension_t *)dev->user_data;
- rt_err_t err = RT_EOK;
- switch(cmd)
- {
- case RT_DEVICE_CTRL_BLK_GETGEOME:
- {
- struct rt_device_blk_geometry *geometry;
- geometry = (struct rt_device_blk_geometry *)args;
- if (geometry == RT_NULL)
- {
- return -RT_ERROR;
- }
- geometry->bytes_per_sector = ATA_SECTOR_SIZE;
- geometry->block_size = ATA_SECTOR_SIZE;
- geometry->sector_count = extension->sector_count;
- dbgprint("[ahci] getgeome: bytes_per_sector:%d, block_size:%d, sector_count:%d\n",
- geometry->bytes_per_sector, geometry->block_size, geometry->sector_count);
- break;
- }
- default:
- err = RT_ERROR;
- break;
- }
- return err;
- }
- static void rt_hw_ahci_isr(int vector, void *param)
- {
- int i;
- for (i = 0; i < 32; i++)
- {
- if (g_hba_base->interrupt_status & (1 << i))
- {
- dbgprint("[ahci] interrupt on port %d occured!\n", i);
- g_hba_base->ports[i].interrupt_status = ~0;
- g_hba_base->interrupt_status = (1 << i);
- ahci_flush_commands((struct hba_port *)&g_hba_base->ports[i]);
- }
- }
- }
- static void ahci_init_hba(struct hba_memory *abar)
- {
- if(abar->ext_capabilities & 1)
- {
- /* request BIOS/OS ownership handoff */
- abar->bohc |= (1 << 1);
- while((abar->bohc & 1) || !(abar->bohc & (1<<1)))
- {
- rt_hw_cpu_pause();
- }
- }
- /* enable the AHCI and reset it */
- abar->global_host_control |= HBA_GHC_AHCI_ENABLE;
- abar->global_host_control |= HBA_GHC_RESET;
- /* wait for reset to complete */
- while(abar->global_host_control & HBA_GHC_RESET)
- {
- rt_hw_cpu_pause();
- }
- /* enable the AHCI and interrupts */
- abar->global_host_control |= HBA_GHC_AHCI_ENABLE;
- abar->global_host_control |= HBA_GHC_INTERRUPT_ENABLE;
- rt_thread_mdelay(20);
- dbgprint("[ahci] caps: %x %x ver:%x ctl: %x\n", abar->capability, abar->ext_capabilities, abar->version, abar->global_host_control);
- }
- #ifdef RT_USING_DEVICE_OPS
- const static struct rt_device_ops ahci_ops =
- {
- rt_ahci_init,
- rt_ahci_open,
- rt_ahci_close,
- rt_ahci_read,
- rt_ahci_write,
- rt_ahci_control
- };
- #endif
- static rt_err_t ahci_create_device(rt_device_extension_t *extension)
- {
- static int ahci_next_device = 0; /* first is sd0 */
- rt_device_t device = rt_device_create(RT_Device_Class_Block, 0);
- if (device == RT_NULL)
- {
- dbg_log(DBG_ERROR, "[ahci] create device failed!\n");
- return RT_ENOMEM;
- }
- device->user_data = (void *)extension;
- #ifdef RT_USING_DEVICE_OPS
- device->ops = &ahci_ops;
- #else
- device->init = rt_ahci_init;
- device->open = rt_ahci_open;
- device->close = rt_ahci_close;
- device->read = rt_ahci_read;
- device->write = rt_ahci_write;
- device->control = rt_ahci_control;
- #endif
- char devname[8] = {0};
- rt_sprintf(devname, "%s%c", DEV_NAME, '0' + ahci_next_device);
- ahci_next_device++;
- if (rt_device_register(device, devname, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE) != RT_EOK)
- {
- dbg_log(DBG_ERROR, "[ahci] register device failed!\n");
- rt_device_destroy(device);
- return RT_ENOMEM;
- }
- return RT_EOK;
- }
- static int rt_hw_ahci_init(void)
- {
- /* 1. get pci info */
- rt_pci_device_t *ahci_pci = ahci_get_pci_info();
- if(ahci_pci == RT_NULL)
- {
- dbg_log(DBG_ERROR, "[ahci] no AHCI controllers present!\n");
- return RT_ERROR;
- }
- /* 2. install intr */
- if (rt_hw_interrupt_install(ahci_pci->irq_line, rt_hw_ahci_isr, RT_NULL, "ahci") < 0)
- {
- dbg_log(DBG_ERROR, "[ahci] install IRQ failed!\n");
- rt_iounmap(g_hba_base);
- return RT_ERROR;
- }
- rt_hw_interrupt_umask(ahci_pci->irq_line);
- /* 3. init ahci device */
- ahci_init_hba(g_hba_base);
- if (!ahci_probe_ports(g_hba_base))
- {
- dbg_log(DBG_ERROR, "[ahci] initializing ahci driver failed!.\n");
- rt_hw_interrupt_mask(ahci_pci->irq_line);
- rt_iounmap(g_hba_base);
- return RT_ERROR;
- }
- rt_kprintf("[ahci] disk driver init done!\n");
- return RT_EOK;
- }
- #ifdef RT_USING_COMPONENTS_INIT
- INIT_DEVICE_EXPORT(rt_hw_ahci_init);
- #endif
- #endif /* BSP_DRV_AHCI */
|