/////////////////////////////////////////////////////////////////////////////////// // File : boot_hba_driver.c // Date : 18/01/2017 // Author : Alain Greiner / Vu Son // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #ifndef SEG_IOC_BASE # error "The SEG_IOC_BASE value should be defined in the 'hard_config.h' file" #endif #ifndef Y_IO # error "The Y_IO value should be defined in the 'hard_config.h' file" #endif #ifndef X_IO # error "The X_IO value should be defined in the 'hard_config.h' file" #endif #ifndef Y_WIDTH # error "The Y_WIDTH value should be defined in the 'hard_config.h' file" #endif /**************************************************************************** * Global variables. * ****************************************************************************/ uint32_t hba_allocated_cmd[32]; /* State of each command slot (0 if slot is available for use). */ hba_cmd_desc_t hba_cmd_list[32] /* Command List (up to 32 commands). */ __attribute__((aligned(0x40))); hba_cmd_table_t hba_cmd_table[32] /* Command Tables array (one Command Table for each Command List entry). */ __attribute__((aligned(0x40))); /**************************************************************************** * Internal functions. * ****************************************************************************/ /**************************************************************************** * This function returns the value of an HBA register. * * @ reg : HBA register to be read. * * @ returns the value stored in reg. * ****************************************************************************/ static uint32_t boot_hba_get_register(uint32_t reg) { cxy_t cxy = (X_IO << Y_WIDTH) + Y_IO; uint32_t * ptr = (uint32_t *)SEG_IOC_BASE + reg; return boot_remote_lw( XPTR( cxy , ptr ) ); } // boot_hba_get_register() /**************************************************************************** * This function sets a new value to an HBA register. * * @ reg : HBA register to be configured. * * @ val : new value to be written to 'reg'. * ****************************************************************************/ void boot_hba_set_register(uint32_t reg, uint32_t val) { cxy_t cxy = (X_IO << Y_WIDTH) + Y_IO; uint32_t * ptr = (uint32_t *)SEG_IOC_BASE + reg; boot_remote_sw( XPTR( cxy , ptr ) , val ); } // boot_hba_set_register() /**************************************************************************** * This function allocates an unused command index. * * @ returns command index (0 to 31) / returns -1 if not found * ****************************************************************************/ static int boot_hba_cmd_alloc() { uint32_t i; uint32_t cmd_id; // loop on the state array to find an unused slot cmd_id = -1; for (i = 0; i < 32; i++) { if (hba_allocated_cmd[i] == 0) { cmd_id = i; break; } } return cmd_id; } // boot_hba_cmd_alloc() /**************************************************************************** * This function releases the 'cmd_id' command index by resetting its * * corresponding entry in the state array. * * @ returns 0 on success, -1 on error. * ****************************************************************************/ static int boot_hba_cmd_release(uint32_t cmd_id) { // check slot allocated if (hba_allocated_cmd[cmd_id] == 0) { boot_printf("\n[BOOT ERROR] boot_hba_cmd_release(): " "Command %d to be released is not allocated\n", cmd_id ); return -1; } // Reset entry in state array hba_allocated_cmd[cmd_id] = 0; return 0; } /**************************************************************************** * Driver API functions. * ****************************************************************************/ /////////////////// int boot_hba_init() { uint64_t cmd_table_addr; // Command Table physical base address uint64_t cmd_list_addr; // Command List physical base address uint64_t paddr; // Command Table physical address uint32_t i; // Iterator for the initialization loop /* Getting the Command List and Command Table base addresses. */ cmd_table_addr = (uint64_t)(intptr_t)&hba_cmd_table[0]; cmd_list_addr = (uint64_t)(intptr_t)&hba_cmd_list[0]; /* Initializing the Command List. */ for (i = 0; i < 32; i++) { paddr = cmd_table_addr + i * sizeof(hba_cmd_table_t); hba_cmd_list[i].ctba = (uint32_t)paddr; hba_cmd_list[i].ctbau = (uint32_t)(paddr >> 32); hba_allocated_cmd[i] = 0; } /* Initializing the HBA registers. */ boot_hba_set_register( HBA_PXCLB , (uint32_t)cmd_list_addr ); boot_hba_set_register( HBA_PXCLBU, (uint32_t)(cmd_list_addr >> 32) ); boot_hba_set_register( HBA_PXIS , 0 ); boot_hba_set_register( HBA_PXIE , 0 ); boot_hba_set_register( HBA_PXCI , 0 ); boot_hba_set_register( HBA_PXCMD , 1 ); return 0; } // boot_hba_init() /////////////////////////////////// int boot_hba_access( uint32_t lba, xptr_t buf_paddr, uint32_t count ) { uint32_t cmd_id; uint32_t pxci; uint32_t pxis; hba_cmd_desc_t * cmd_desc; hba_cmd_table_t * cmd_table; // get target buffer cluster and pointer cxy_t buf_cxy = GET_CXY( buf_paddr ); uint32_t buf_ptr = (uint32_t)GET_PTR( buf_paddr ); // Check buffer address alignment if (buf_ptr & 0x3F) { boot_puts("\n[BOOT ERROR] boot_hba_access(): " "Buffer address is not cache-line-size-aligned\n"); return -1; } // Get a free slot in the Command List cmd_id = boot_hba_cmd_alloc(); // Initialize pointers cmd_desc = &hba_cmd_list[cmd_id]; cmd_table = &hba_cmd_table[cmd_id]; // Set the buffer descriptor of the Command Table cmd_table->buffer.dba = buf_ptr; cmd_table->buffer.dbau = buf_cxy; cmd_table->buffer.dbc = count * 512; // Initialize the Command Table header cmd_table->header.lba0 = (char)lba; cmd_table->header.lba1 = (char)(lba >> 8); cmd_table->header.lba2 = (char)(lba >> 16); cmd_table->header.lba3 = (char)(lba >> 24); cmd_table->header.lba4 = 0; cmd_table->header.lba5 = 0; // Initialize the Command Descriptor cmd_desc->prdtl[0] = 1; cmd_desc->prdtl[1] = 0; cmd_desc->flag[0] = 0x00; #if USE_IOB // software L2/L3 cache coherence if( boot_mmc_inval( buf_paddr, count<<9 ) ) return -1; #endif // Launch data transfer boot_hba_set_register(HBA_PXCI, (1 << cmd_id)); #if DEBUG_BOOT_IOC boot_printf("\n[BOOT] boot_hba_access(): Transfer launched at cycle %d\n" " lba = %d / buf = %l / nblocks = %d\n", boot_get_proctime() , lba , buf_paddr , count ); #endif // Wait transfer completion do { pxci = boot_hba_get_register(HBA_PXCI); } while (pxci & (1 << cmd_id)); // Get error status then reset it pxis = boot_hba_get_register(HBA_PXIS); boot_hba_set_register(HBA_PXIS, 0); #if DEBUG_BOOT_IOC boot_printf("\n[BOOT] boot_hba_access(): Transfer terminated at cycle %d\n", boot_get_proctime()); #endif if (boot_hba_cmd_release(cmd_id)) return -1; else if (pxis & 0x40000000) return pxis; else return 0; } // boot_hba_access()