/*
 * soclib_hba.h - soclib AHCI block device driver definition.
 *
 * Author        Alain Greiner (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _SOCLIB_HBA_H_
#define _SOCLIB_HBA_H_

#include <chdev.h>
#include <hal_types.h>

/*****************************************************************************************
 * This driver supports the SocLib VciMultiAhci component, that is a multi-channels,
 * block oriented, external storage controler, respecting the AHCI standard.
 *
 * 1. Each HBA channel define an independant physical disk, but this driver
 *    supports only channel 0, because ALMOS-MKH uses only one physical disk.
 *
 * 2. The SOCLIB HBA component support split memory buffers (several physical
 *    buffers for one single command), but this driver supports only one
 *    single buffer I/O operation.
 *
 * 3. The "command list" can register up to 32 independant commands, posted
 *    by different user threads. The command list is filled by the server
 *    thread associated to the device, using one free slot per command, until the
 *    device waiting queue of client threads is empty.
 *
 * 4. As there is only one interrupt, and the registered I/O operation can complete
 *    in any order, this interrupt is not linked to a specific I/O operation.
 *    The associated HBA_ISR unblock all client threads that have a completed command,
 *    using the "hba_active_slots" and "hba_owner_thread[32]" global variables.
 ****************************************************************************************/

/*****************************************************************************************
 * HBA component registers offsets
 ****************************************************************************************/

enum SoclibMultiAhciRegisters 
{
  HBA_PXCLB_REG        = 0,         // command list base address 32 LSB bits
  HBA_PXCLBU_REG       = 1,         // command list base address 32 MSB bits
  HBA_PXIS_REG         = 4,         // interrupt status
  HBA_PXIE_REG         = 5,         // interrupt enable
  HBA_PXCMD_REG        = 6,         // run
  HBA_BLOCK_SIZE_REG   = 7,         // number of bytes per block
  HBA_BLOCK_COUNT_REG  = 8,         // number of blocks
  HBA_PXCI_REG         = 14,        // command bit-vector     

};

/*****************************************************************************************
 * This structure defines the command header  (16 bytes)
 ****************************************************************************************/

typedef struct hba_cmd_header_s  
{
    unsigned int        res0;       // reserved	
    unsigned char	    lba0;	    // LBA 7:0
    unsigned char	    lba1;	    // LBA 15:8
    unsigned char	    lba2;	    // LBA 23:16
    unsigned char	    res1;	    // reserved
    unsigned char	    lba3;	    // LBA 31:24
    unsigned char	    lba4;	    // LBA 39:32
    unsigned char	    lba5;	    // LBA 47:40
    unsigned char	    res2;	    // reserved
    unsigned int        res3;       // reserved	

} 
hba_cmd_header_t;

/*****************************************************************************************
 * This structure defines the command buffer  (16 bytes)
 ****************************************************************************************/

typedef struct hba_cmd_buffer_s 
{
    unsigned int        dba;	    // Buffer base address 32 LSB bits
    unsigned int        dbau;	    // Buffer base address 32 MSB bits
    unsigned int        res0;	    // reserved
    unsigned int        dbc;	    // Buffer byte count
} 
hba_cmd_buffer_t;

/*****************************************************************************************
 * This structure defines a command table  (32 bytes, because we support only one buffer)
 ****************************************************************************************/

typedef struct hba_cmd_table_s
{
    hba_cmd_header_t   header;      // contains LBA
    hba_cmd_buffer_t   buffer;      // only one physical buffer
}
hba_cmd_table_t;

/*****************************************************************************************
 * This structure defines a command descriptor  (16 bytes)
 ****************************************************************************************/

typedef struct hba_cmd_desc_s
{
    unsigned char       flag[2];    // W in bit 6 of flag[0]
    unsigned char       prdtl[2];	// Number of buffers
    unsigned int        prdbc;		// Number of bytes actually transfered
    unsigned int        ctba;		// Command Table base address 32 LSB bits
    unsigned int        ctbau;		// Command Table base address 32 MSB bits
}
hba_cmd_desc_t;

/********************************************************************************************
 * This function access the SOCLIB_HBA hardware registers to get the block size and the
 * number of blocks, and initialises the "extension" field of the IOC device descriptor.
 ********************************************************************************************
 * @ chdev     : local pointer on the generic IOC chdev descriptor.
 *******************************************************************************************/
extern void soclib_hba_init( chdev_t * chdev );

/********************************************************************************************
 * This function is called by the server thread associated to the IOC device.
 * It decodes the IOC device command embedded in the calling thread descriptor,
 * (format defined in the dev_ioc.h file) and starts execution of this generic command, 
 * accessing the relevant SOCLIB_HBA hardware registers.
 * It registers the command in the SOCLIB_HBA device and returns without blocking
 * because this AHCI component supports up to 32 concurrent I/O operations. 
 * It simply deschedules without blocking if the number of registered I/O operations
 * becomes larger than 32, which should be a rare event.
 ********************************************************************************************
 * @ xp_thread  : extended pointer on the client thread.
 *******************************************************************************************/
extern void soclib_hba_command( xptr_t  thread_xp );

/********************************************************************************************
 * This Interrupt Service Routine is executed when the IRQ signaling the completion of 
 * one or several I/O operations registered in the SOCLIB_AHCI device is received.
 * It acknowledges the IRQ by accessing the proper SOCLIB_HBA register, and unblock
 * all client thread that have an I/O operation completed.
 ********************************************************************************************
 * @ chdev     : local pointer on the generic IOC device descriptor.
 *******************************************************************************************/
extern void soclib_hba_isr( chdev_t * chdev );


#endif /* _SOCLIB_HBA_H_ */
