| [1] | 1 | /* | 
|---|
|  | 2 | * soclib_hba.c - soclib AHCI block device driver implementation. | 
|---|
|  | 3 | * | 
|---|
|  | 4 | * Author     Alain Greiner (2016) | 
|---|
|  | 5 | * | 
|---|
|  | 6 | * Copyright (c) UPMC Sorbonne Universites | 
|---|
|  | 7 | * | 
|---|
|  | 8 | * This file is part of ALMOS-MKH.. | 
|---|
|  | 9 | * | 
|---|
|  | 10 | * ALMOS-MKH. is free software; you can redistribute it and/or modify it | 
|---|
|  | 11 | * under the terms of the GNU General Public License as published by | 
|---|
|  | 12 | * the Free Software Foundation; version 2.0 of the License. | 
|---|
|  | 13 | * | 
|---|
|  | 14 | * ALMOS-MKH. is distributed in the hope that it will be useful, but | 
|---|
|  | 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
|  | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|---|
|  | 17 | * General Public License for more details. | 
|---|
|  | 18 | * | 
|---|
|  | 19 | * You should have received a copy of the GNU General Public License | 
|---|
|  | 20 | * along with ALMOS-MKH.; if not, write to the Free Software Foundation, | 
|---|
|  | 21 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | 
|---|
|  | 22 | */ | 
|---|
|  | 23 |  | 
|---|
|  | 24 | #include <printk.h> | 
|---|
|  | 25 | #include <hal_special.h> | 
|---|
| [4] | 26 | #include <chdev.h> | 
|---|
| [1] | 27 | #include <dev_ioc.h> | 
|---|
|  | 28 | #include <soclib_hba.h> | 
|---|
|  | 29 | #include <spinlock.h> | 
|---|
|  | 30 | #include <thread.h> | 
|---|
|  | 31 |  | 
|---|
|  | 32 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 33 | //   SOCLIB_HBA specific global variables | 
|---|
|  | 34 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 35 |  | 
|---|
|  | 36 | // command list : up to 32 commands | 
|---|
|  | 37 | __attribute__((section(".kdata"))) | 
|---|
|  | 38 | hba_cmd_desc_t     hba_cmd_list[32] __attribute__((aligned(0x40))); | 
|---|
|  | 39 |  | 
|---|
|  | 40 | // command tables array : one command table per entry in command list | 
|---|
|  | 41 | __attribute__((section(".kdata"))) | 
|---|
|  | 42 | hba_cmd_table_t    hba_cmd_table[32] __attribute__((aligned(0x40))); | 
|---|
|  | 43 |  | 
|---|
|  | 44 | // extended pointer on the owner thread, for each slot | 
|---|
|  | 45 | __attribute__((section(".kdata"))) | 
|---|
|  | 46 | xptr_t             hba_owner_thread[32]; | 
|---|
|  | 47 |  | 
|---|
|  | 48 | // bit vector of active slots | 
|---|
|  | 49 | __attribute__((section(".kdata"))) | 
|---|
|  | 50 | uint32_t           hba_active_slots; | 
|---|
|  | 51 |  | 
|---|
|  | 52 | // spinlock protecting the command slot allocator | 
|---|
|  | 53 | __attribute__((section(".kdata"))) | 
|---|
|  | 54 | spinlock_t         hba_lock; | 
|---|
|  | 55 |  | 
|---|
| [4] | 56 | /////////////////////////////////////// | 
|---|
|  | 57 | void soclib_hba_init( chdev_t * chdev ) | 
|---|
| [1] | 58 | { | 
|---|
|  | 59 | // get hardware device base address | 
|---|
| [4] | 60 | xptr_t  hba_xp = chdev->base; | 
|---|
| [1] | 61 |  | 
|---|
|  | 62 | // get hardware device cluster and local pointer | 
|---|
|  | 63 | cxy_t      hba_cxy  = GET_CXY( hba_xp ); | 
|---|
|  | 64 | uint32_t * hba_ptr  = (uint32_t *)GET_PTR( hba_xp ); | 
|---|
|  | 65 |  | 
|---|
|  | 66 | // get block_size and block_count | 
|---|
|  | 67 | uint32_t block_size  = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_BLOCK_SIZE_REG ) ); | 
|---|
|  | 68 | uint32_t block_count = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_BLOCK_COUNT_REG ) ); | 
|---|
|  | 69 |  | 
|---|
|  | 70 | // set device descriptor extension | 
|---|
| [4] | 71 | chdev->ext.ioc.size  = block_size; | 
|---|
|  | 72 | chdev->ext.ioc.count = block_count; | 
|---|
| [1] | 73 |  | 
|---|
|  | 74 | // reset SOCLIB_HBA driver global variables | 
|---|
|  | 75 | hba_active_slots = 0; | 
|---|
|  | 76 |  | 
|---|
|  | 77 | } // end soclib_hba_init() | 
|---|
|  | 78 |  | 
|---|
|  | 79 |  | 
|---|
|  | 80 | ////////////////////////////////////////////////////////////////// | 
|---|
|  | 81 | void __attribute__ ((noinline)) soclib_hba_command( xptr_t th_xp ) | 
|---|
|  | 82 | { | 
|---|
|  | 83 |  | 
|---|
|  | 84 | uint32_t           to_mem;       // to_mem : command argument | 
|---|
|  | 85 | uint32_t           lba;          // lba    : command argument | 
|---|
|  | 86 | uint32_t           count;        // count  : command argument | 
|---|
|  | 87 | xptr_t             buf_xp;       // buffer : command argument | 
|---|
|  | 88 | xptr_t             dev_xp;       // device : command argument | 
|---|
|  | 89 | uint32_t           cmd_id;       // current slot index in command bit_vector | 
|---|
|  | 90 | hba_cmd_desc_t   * cmd_desc;     // command descriptor pointer | 
|---|
|  | 91 | hba_cmd_table_t  * cmd_table;    // command table pointer | 
|---|
|  | 92 | bool_t             found; | 
|---|
|  | 93 | uint32_t           iter; | 
|---|
|  | 94 |  | 
|---|
|  | 95 | // get client thread cluster and local pointer | 
|---|
|  | 96 | cxy_t      th_cxy = GET_CXY( th_xp ); | 
|---|
|  | 97 | thread_t * th_ptr = (thread_t *)GET_PTR( th_xp ); | 
|---|
|  | 98 |  | 
|---|
|  | 99 | // get command arguments and extended pointer on IOC device | 
|---|
| [4] | 100 | to_mem    =         hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.to_mem ) ); | 
|---|
|  | 101 | lba       =         hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.lba    ) ); | 
|---|
|  | 102 | count     =         hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.count  ) ); | 
|---|
|  | 103 | buf_xp    = (xptr_t)hal_remote_lwd( XPTR( th_cxy , &th_ptr->command.ioc.buf_xp ) ); | 
|---|
|  | 104 | dev_xp    = (xptr_t)hal_remote_lwd( XPTR( th_cxy , &th_ptr->command.ioc.dev_xp ) ); | 
|---|
| [1] | 105 |  | 
|---|
|  | 106 | // get IOC device cluster and local pointer | 
|---|
| [4] | 107 | cxy_t     dev_cxy = GET_CXY( dev_xp ); | 
|---|
|  | 108 | chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp ); | 
|---|
| [1] | 109 |  | 
|---|
|  | 110 | // get extended pointer on SOCLIB-HBA peripheral | 
|---|
|  | 111 | xptr_t     hba_xp = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->base ) ); | 
|---|
|  | 112 |  | 
|---|
|  | 113 | // get SOCLIB_HBA device cluster and local pointer | 
|---|
|  | 114 | cxy_t      hba_cxy = GET_CXY( hba_xp ); | 
|---|
|  | 115 | uint32_t * hba_ptr = (uint32_t *)GET_PTR( hba_xp ); | 
|---|
|  | 116 |  | 
|---|
|  | 117 | // try to register the I/O operation in a free slot | 
|---|
|  | 118 | // returns if success, deschedule if no slot available | 
|---|
|  | 119 | // we do not need a lock to access the slot allocator, | 
|---|
|  | 120 | // because the driver is only called by the server thread. | 
|---|
|  | 121 | while( 1 ) | 
|---|
|  | 122 | { | 
|---|
|  | 123 | // try to find a free slot in the 32 slots command list | 
|---|
|  | 124 | cmd_id = 0; | 
|---|
|  | 125 | found  = false; | 
|---|
|  | 126 | for ( iter = 0 ; iter < 32 ; iter++ ) | 
|---|
|  | 127 | { | 
|---|
|  | 128 | if( (hba_active_slots & (1<<iter) ) == 0 ) | 
|---|
|  | 129 | { | 
|---|
|  | 130 | found  = true; | 
|---|
|  | 131 | cmd_id = iter; | 
|---|
|  | 132 | hba_active_slots |= (1<<iter); | 
|---|
|  | 133 | break; | 
|---|
|  | 134 | } | 
|---|
|  | 135 | } | 
|---|
|  | 136 |  | 
|---|
|  | 137 | if( found )  // register and starts the I/O operation | 
|---|
|  | 138 | { | 
|---|
|  | 139 | // compute pointers on command descriptor and command table | 
|---|
|  | 140 | cmd_desc  = &hba_cmd_list[cmd_id]; | 
|---|
|  | 141 | cmd_table = &hba_cmd_table[cmd_id]; | 
|---|
|  | 142 |  | 
|---|
|  | 143 | // set  buffer descriptor in command table | 
|---|
|  | 144 | cmd_table->buffer.dba  = (uint32_t)(buf_xp); | 
|---|
|  | 145 | cmd_table->buffer.dbau = (uint32_t)(buf_xp >> 32); | 
|---|
|  | 146 | cmd_table->buffer.dbc  = count * 512; | 
|---|
|  | 147 |  | 
|---|
|  | 148 | // initialize command table header | 
|---|
|  | 149 | cmd_table->header.lba0 = (char)lba; | 
|---|
|  | 150 | cmd_table->header.lba1 = (char)(lba>>8); | 
|---|
|  | 151 | cmd_table->header.lba2 = (char)(lba>>16); | 
|---|
|  | 152 | cmd_table->header.lba3 = (char)(lba>>24); | 
|---|
|  | 153 | cmd_table->header.lba4 = 0; | 
|---|
|  | 154 | cmd_table->header.lba5 = 0; | 
|---|
|  | 155 |  | 
|---|
|  | 156 | // initialise command descriptor | 
|---|
|  | 157 | cmd_desc->prdtl[0] = 1; | 
|---|
|  | 158 | cmd_desc->prdtl[1] = 0; | 
|---|
|  | 159 | if( to_mem ) cmd_desc->flag[0] = 0x00; | 
|---|
|  | 160 | else         cmd_desc->flag[0] = 0x40; | 
|---|
|  | 161 |  | 
|---|
|  | 162 | #if USE_IOB // software L2/L3 cache coherence | 
|---|
|  | 163 |  | 
|---|
|  | 164 | // compute physical addresses | 
|---|
|  | 165 | paddr_t   cmd_desc_paddr;    // command descriptor physical address | 
|---|
|  | 166 | paddr_t   cmd_table_paddr;   // command table header physical address | 
|---|
|  | 167 | bool_t    ident = CONFIG_KERNEL_IDENTITY; | 
|---|
|  | 168 |  | 
|---|
|  | 169 | error = vmm_v2p_translate( ident , cmd_table , &cmd_table_paddr ); | 
|---|
|  | 170 | if( error ) | 
|---|
|  | 171 | { | 
|---|
|  | 172 | printk("\n[PANIC] in %s : cannot get paddr fo cmd_table\n", __FUNCTION__ ); | 
|---|
|  | 173 | hal_core_sleep() | 
|---|
|  | 174 | } | 
|---|
|  | 175 |  | 
|---|
|  | 176 | error = vmm_v2p_translate( ident , cmd_desc , &cmd_desc_paddr ); | 
|---|
|  | 177 | if( error ) | 
|---|
|  | 178 | { | 
|---|
|  | 179 | printk("\n[PANIC] in %s : cannot get paddr fo cmd_desc\n", __FUNCTION__ ); | 
|---|
|  | 180 | hal_core_sleep() | 
|---|
|  | 181 | } | 
|---|
|  | 182 |  | 
|---|
|  | 183 | // update L3 for command table | 
|---|
|  | 184 | dev_mmc_sync( cmd_table_paddr , sizeof(hba_cmd_table_t) ); | 
|---|
|  | 185 |  | 
|---|
|  | 186 | // update L3 for command descriptor | 
|---|
|  | 187 | dev_mmc_sync( cmd_desc_paddr , sizeof(hba_cmd_desc_t) ); | 
|---|
|  | 188 |  | 
|---|
|  | 189 | #endif // end software L2/L3 cache coherence | 
|---|
|  | 190 |  | 
|---|
|  | 191 | // activates HBA interrupts | 
|---|
|  | 192 | hal_remote_sw( XPTR( hba_cxy , hba_ptr + HBA_PXIE_REG ) , 0x1 ); | 
|---|
|  | 193 |  | 
|---|
|  | 194 | // set hba_owner_thread[slot] | 
|---|
|  | 195 | hba_owner_thread[cmd_id] = th_xp; | 
|---|
|  | 196 |  | 
|---|
|  | 197 | // set HBA_PXCI_REG to start transfer | 
|---|
|  | 198 | hal_remote_sw( XPTR( hba_cxy , hba_ptr + HBA_PXCI_REG ) , 1<<cmd_id ); | 
|---|
|  | 199 |  | 
|---|
|  | 200 | ioc_dmsg("INFO in %s : thread %x / buffer = %llx at cycle %d\n", | 
|---|
|  | 201 | __FUNCTION__ , hal_remote_lw( XPTR( th_cxy , &th_ptr->trdid ) ) , | 
|---|
|  | 202 | buf_xp , hal_time_stamp() ); | 
|---|
|  | 203 |  | 
|---|
|  | 204 | // exit the while | 
|---|
|  | 205 | break; | 
|---|
|  | 206 | } | 
|---|
|  | 207 | else   // deschedule if no slot available in SOCLIB_HBA | 
|---|
|  | 208 | { | 
|---|
|  | 209 | sched_yield(); | 
|---|
|  | 210 | } | 
|---|
|  | 211 | }  // end while | 
|---|
|  | 212 |  | 
|---|
|  | 213 | } // end soclib_hba_cmd() | 
|---|
|  | 214 |  | 
|---|
|  | 215 |  | 
|---|
| [4] | 216 | ///////////////////////////////////////////////////////////////// | 
|---|
|  | 217 | void __attribute__ ((noinline)) soclib_hba_isr( chdev_t * chdev ) | 
|---|
| [1] | 218 | { | 
|---|
|  | 219 | // get extended pointer on client thread | 
|---|
| [4] | 220 | xptr_t root      = XPTR( local_cxy , &chdev->wait_root ); | 
|---|
| [1] | 221 | xptr_t client_xp = XLIST_FIRST_ELEMENT( root , thread_t , wait_list ); | 
|---|
|  | 222 |  | 
|---|
|  | 223 | // get client thread cluster and local pointer | 
|---|
|  | 224 | cxy_t      client_cxy = GET_CXY( client_xp ); | 
|---|
|  | 225 | thread_t * client_ptr = (thread_t *)GET_PTR( client_xp ); | 
|---|
|  | 226 |  | 
|---|
|  | 227 | // get SOCLIB_HBA device cluster and local pointer | 
|---|
| [4] | 228 | cxy_t      hba_cxy  = GET_CXY( chdev->base ); | 
|---|
|  | 229 | uint32_t * hba_ptr  = (uint32_t *)GET_PTR( chdev->base ); | 
|---|
| [1] | 230 |  | 
|---|
|  | 231 | // get HBA_PXIS_REG and HBA_PXIS_REG current values | 
|---|
|  | 232 | uint32_t current_pxis = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_PXIS_REG ) ); | 
|---|
|  | 233 | uint32_t current_pxci = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_PXCI_REG ) ); | 
|---|
|  | 234 |  | 
|---|
|  | 235 | uint32_t  error    = (current_pxis & 0x40000000) >> 30; | 
|---|
|  | 236 | uint32_t  fault_id = (current_pxis & 0x1F000000) >> 24; | 
|---|
|  | 237 | uint32_t  iter; | 
|---|
|  | 238 |  | 
|---|
|  | 239 | // loop on active commands to signal one or several completed I/O operations | 
|---|
|  | 240 | for( iter = 0 ; iter < 32 ; iter++ ) | 
|---|
|  | 241 | { | 
|---|
|  | 242 | if ( ( (hba_active_slots & (1<<iter)) != 0 ) &&  // active command | 
|---|
|  | 243 | ( (current_pxci     & (1<<iter)) == 0 ) )   // completed command | 
|---|
|  | 244 | { | 
|---|
|  | 245 | // release the slot | 
|---|
|  | 246 | hba_active_slots &= ~(1<<iter); | 
|---|
|  | 247 |  | 
|---|
|  | 248 | // set operation status in client thread command | 
|---|
|  | 249 | if( error && (iter == fault_id ) ) | 
|---|
|  | 250 | { | 
|---|
| [4] | 251 | hal_remote_sw( XPTR( client_cxy , &client_ptr->command.ioc.error ) , 1 ); | 
|---|
| [1] | 252 | } | 
|---|
|  | 253 | else | 
|---|
|  | 254 | { | 
|---|
| [4] | 255 | hal_remote_sw( XPTR( client_cxy , &client_ptr->command.ioc.error ) , 0 ); | 
|---|
| [1] | 256 | } | 
|---|
|  | 257 |  | 
|---|
|  | 258 | // unblock client thread | 
|---|
|  | 259 | thread_unblock( client_xp , THREAD_BLOCKED_IO ); | 
|---|
|  | 260 |  | 
|---|
|  | 261 | ioc_dmsg("INFO in %s : thread %x at cycle %d\n", | 
|---|
|  | 262 | __FUNCTION__ , hal_remote_lw( XPTR( client_cxy , &client_ptr->trdid ) ) , | 
|---|
|  | 263 | hal_time_stamp() ); | 
|---|
|  | 264 | } | 
|---|
|  | 265 | } | 
|---|
|  | 266 |  | 
|---|
|  | 267 | // reset HBA_PXIS_REG | 
|---|
|  | 268 | hal_remote_sw( XPTR( hba_cxy , hba_ptr + HBA_PXIS_REG ) , 0 ); | 
|---|
|  | 269 |  | 
|---|
|  | 270 | } // end soclib_hba_isr() | 
|---|
|  | 271 |  | 
|---|
|  | 272 |  | 
|---|
|  | 273 |  | 
|---|