Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / sas / include / sas.h
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: sas.h
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program 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 this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
#ifndef __SAS_H__
#define __SAS_H__
#include <queue>
#include "pcie.h"
#include "pci_dev.h"
#include "module.h"
#include "mpi.h"
#include "mpi_ioc.h"
#include "mpi_init.h"
#include "mpi_cnfg.h"
#include "disk.h"
#include "system.h"
// ioc parameters
#define IOC_REPLY_QUEUE_DEPTH 128
#define IOC_REQ_QUEUE_DEPTH 128
#define IOC_PRI_QUEUE_DEPTH 128
#define IOC_REQ_FRAME_SIZE 128
#define IOC_CHAIN_DEPTH 128
#define IOC_NUM_PORTS 4
#define IOC_BLOCK_SIZE 32
#define IOC_PRODUCTION_ID 101
#define IOC_CAPABILITIES 0xfc1
// port parameters
#define PORT_TYPE 0x30
#define PORT_MAX_DEVICES 1
#define PORT_SCSI_ID 0
#define PORT_PROTOCOL_FLAGS 0xc
#define PORT_MAX_POSTED_CMD_BUFFERS 32
#define PORT_MAX_PERSISTENT_IDS 1
#define PORT_MAX_LAN_BUCKETS 0
// message frame address mask
#define FMA_ADDRESS_MASK 0xfffffff8
// maximal size of message (in byte) received/sent through doorbell handshake
#define HDSHK_MSG_SIZE 256
#define swap_hword(value) \
((uint32_t)(((value) & 0xff) << 8 | (value) >> 8))
#define swap_word(value) \
((uint32_t)((Word)swap_hword((HWord)((value) & 0xffff)) << 16 | \
(Word)swap_hword((HWord)((value) >> 16))))
#define swap_lword(value) \
((uint64_t)((((LWord)swap_word(LO_W(value))) << 32) | \
((LWord)swap_word(HI_W(value)))))
// system interface register set
typedef struct {
uint32_t doorbell;
uint32_t write_seq;
uint32_t diag;
uint32_t test_base_addr;
uint32_t diagrw_data;
uint32_t diagrw_addr;
uint32_t res1[6];
uint32_t intr_status;
uint32_t intr_mask;
uint32_t res2[2];
uint32_t req_q;
uint32_t reply_q;
uint32_t pri_req_q;
} mpt_reg_t;
// ioc configuration
typedef struct {
// assigned by ioc init
uint8_t who_init;
uint8_t max_devices;
uint8_t max_buses;
uint16_t reply_frame_size;
uint32_t host_mfa_high_addr;
uint32_t sense_buffer_high_addr;
sge_simple64_t host_page_buffer_sge;
} ioc_conf_t;
class SAS;
// event queue callback function
extern void sas_io_callback(void *arg1, void *arg2);
/* event table is used to support diskdelay.
It is not a FIFO, because some later request may
finish early than an early one.
allocate an entry: when a request is received.
free an entry: when a reply is read.
*/
#define EVENT_TABLE_SIZE 128
typedef struct {
// valid flag
uint8_t used;
// for diskdelay, parameters used to by send_mpi_rely() in the callback function
uint32_t reply;
uint8_t is_cntx;
uint8_t cntx_type;
// callback function invoke time
int64_t invoke_time;
// done flag
uint8_t done;
} event_table_entry_t;
typedef struct {
event_table_entry_t events[EVENT_TABLE_SIZE];
int next; // next available entry
void init() {
next = 0;
for (int i = 0; i < EVENT_TABLE_SIZE; i++) {
events[i].used = 0;
events[i].reply = 0;
events[i].is_cntx = 0;
events[i].cntx_type = 0;
events[i].invoke_time = 0;
events[i].done = 0;
}
}
void clear() {
init();
}
int alloc() {
int entry = next;
for (int i = 0; i < EVENT_TABLE_SIZE; i++) {
if (events[entry].used == 0) {
events[entry].used = 1;
events[entry].reply = 0;
events[entry].is_cntx = 0;
events[entry].cntx_type = 0;
events[entry].invoke_time = 0;
events[entry].done = 0;
next = entry + 1;
if (next == EVENT_TABLE_SIZE)
next = 0;
return(entry);
}
else {
entry++;
if (entry == EVENT_TABLE_SIZE)
entry = 0;
}
}
fprintf(stderr, "no free entry in the event table\n");
exit(1);
}
void free(int entry) {
events[entry].used = 0;
}
void set_invoke_time(int entry, int64_t time) {
events[entry].invoke_time = time;
}
int64_t get_invoke_time(int entry) {
return(events[entry].invoke_time);
}
void done(int entry, uint32_t reply, uint8_t is_cntx, uint8_t cntx_type) {
events[entry].reply = reply;
events[entry].is_cntx = is_cntx;
events[entry].cntx_type = cntx_type;
events[entry].done = 1;
}
void dump(SAS *sasp, FILE *fp) {
fprintf(fp, "next_entry %d\n", next);
for (int i = 0; i < EVENT_TABLE_SIZE; i++) {
fprintf(fp, "event_table[%d] %hhu %u %hhu %hhu %lld %hhu\n", i,
events[i].used,
events[i].reply,
events[i].is_cntx,
events[i].cntx_type,
events[i].invoke_time,
events[i].done);
}
}
void restore(SAS *sasp, FILE *fp) {
char name[64];
fscanf(fp, "%s %d\n", name, &next);
for (int i = 0; i < EVENT_TABLE_SIZE; i++) {
fscanf(fp, "%s %hhu %u %hhu %hhu %lld %hhu\n", name,
&events[i].used,
&events[i].reply,
&events[i].is_cntx,
&events[i].cntx_type,
&events[i].invoke_time,
&events[i].done);
if (events[i].used == 1) {
if (events[i].done != 1) {
printf("io request for event %d has not been processed yet\n", i);
exit(1);
}
mmi_register_event(events[i].invoke_time, (EventFunc_T*)&sas_io_callback, (void *)sasp, (void *)i);
}
}
}
} event_table_t;
// SAS controller
class SAS : public genericPcieDev, public Module {
const char *fname; // configuration file
uint32_t _io_base; // io base
uint64_t _mem0_base; // mem0 base
uint64_t _mem1_base; // mem1 base
uint8_t _hdshk_msg_req_size; // the size of request msg received through doorbell handshake
uint8_t _hdshk_msg_req_recv; // number of bytes received
uchar_t *_hdshk_msg_req_buf; // buffer for doorbell handshake request msg
uint8_t _hdshk_msg_reply_size; // the size of reply msg sent through doorbell handshake
uint8_t _hdshk_msg_reply_send; // number of bytes sent
uchar_t *_hdshk_msg_reply_buf; // buffer for doorbell handshake reply msg
mpt_reg_t *_mpt_reg; // system interface register set
ioc_conf_t *_ioc_conf; // ioc configuration
uint8_t *_port_enabled; // port enabled
int64_t *_earliest_serving_time; // the earliest time when the controller is ready for new service
uint16_t _request_queue_size; // number of outstanding requests
queue<uint32_t> _reply_post_queue; // reply post queue
queue<uint32_t> _reply_free_queue; // reply free queue
event_table_t _etable; // event table, for recording events
SCSIDisk *_disk[IOC_NUM_PORTS]; // attached disks, one disk per port
pthread_mutex_t _reply_queue_mutex; // mutex for reply queue
void reset_ioc();
void send_handshake_msg();
void process_handshake_msg();
void send_mpi_msg(uint32_t reply, bool is_cntx, uint8_t cntx_type);
void process_mpi_msg(int entry, uint32_t fma);
void doorbell_function_handshake(uint8_t size);
void doorbell_function_reply_frame_removal();
void doorbell_function_ioc_msg_unit_reset();
void compute_disk_delay(int entry, int target, bool wr);
void scsi_io_done(int entry, uint32_t reply, uint8_t is_cntx, uint8_t cntx_type, int target, bool wr);
void scsi_io_error(int entry, msg_scsi_io_request_t *req, uint16_t ioc_status);
void process_scsi_cmd(int entry, msg_scsi_io_request_t *req, uint64_t sge_addr);
void send_page_reply(msg_config_t *req, msg_config_reply_t *reply, config_page_header_t *page_header);
void send_extended_page_reply(msg_config_t *req, msg_config_reply_t *reply,
config_extended_page_header_t *extended_page_header, bool invalid);
void access_page_manufacturing(msg_config_t *req, msg_config_reply_t *reply);
void access_page_ioc(msg_config_t *req, msg_config_reply_t *reply);
void access_page_sas_io_unit(msg_config_t *req, msg_config_reply_t *reply);
void access_page_sas_device(msg_config_t *req, msg_config_reply_t *reply);
void access_page_sas_phy(msg_config_t *req, msg_config_reply_t *reply);
bool memory_space_access(int offset, bool wr, uint64_t *in_buf, uint8_t size);
void access_door_bell(bool wr, uint64_t *in_buf, uint8_t size);
void access_write_sequence(bool wr, uint64_t *in_buf, uint8_t size);
void access_host_diagnostic(bool wr, uint64_t *in_buf, uint8_t size);
void access_test_base_address(bool wr, uint64_t *in_buf, uint8_t size);
void access_diag_rw_data(bool wr, uint64_t *in_buf, uint8_t size);
void access_diag_rw_address(bool wr, uint64_t *in_buf, uint8_t size);
void access_host_interrupt_status(bool wr, uint64_t *in_buf, uint8_t size);
void access_host_interrupt_mask(bool wr, uint64_t *in_buf, uint8_t size);
void access_host_request_queue(bool wr, uint64_t *in_buf, uint8_t size);
void access_reply_queue(bool wr, uint64_t *in_buf, uint8_t size);
void access_hi_pri_request_queue(bool wr, uint64_t *in_buf, uint8_t size);
void memory_access(uint64_t va, addrMd_xactnType mode, void *data, uint32_t size, bool wr);
void memory_transport(uint64_t sge_addr, addrMd_xactnType sge_addr_mode, void *data, uint32_t size, bool wr);
void memory_transport(void *sge, void *data, uint32_t size, bool wr);
void set_doorbell_interrupt();
void set_reply_msg_interrupt();
bool msi_enabled();
addrMd_xactnType mfa_addr_mode() {
return(_ioc_conf->host_mfa_high_addr == 0 ? mem_addr32 : mem_addr64);
}
uint64_t mfa_addr(uint32_t addr) {
return(addr | (_ioc_conf->host_mfa_high_addr << 32));
}
public:
SAS(const char *modname, const char *instance_name);
~SAS();
// callback function for disk io
void io_callback(int entry);
// override virtual functions from Module class
const char *get_help();
const char *get_version();
bool parse_arg(const char *);
bool check_args();
void init_done();
void module_added(mmi_instance_t, const char*);
void module_deleted(mmi_instance_t, const char*);
void modinfo();
void *get_interface(const char *name);
bool dump(FILE *fp);
bool restore(FILE *fp);
// override virtual function from genericPcieDev
pcieCompleter devif_memAccess(bool wr, uint64_t offset, addrMd_xactnType mode, void * data,\
uint16_t length, uint8_t be, uint16_t reqId, tlp_X args,SAM_DeviceId* samId);
pcieCompleter devif_confAccess(bool wr, uint32_t offset, void * data, \
uint8_t be,uint16_t reqId, addrMd_xactnType tType, uint16_t length, tlp_X args,SAM_DeviceId* samId);
pcieCompleter devif_ioAccess(bool wr, uint32_t offset, void * data, uint8_t be, \
uint16_t reqId, uint16_t length, tlp_X args,SAM_DeviceId* samId);
pcieCompleter devif_msgAccess(uint8_t messageCode, msgRouting route, uint64_t tarIdOrAddr, uint16_t reqId, \
void * data, uint16_t length, uint16_t vendorId, uint32_t vendor_data, tlp_X args,SAM_DeviceId* samId);
void devif_confAccessCb(bool wr, uint64_t offset, uint8_t be);
mmi_instance_t devif_getInstance() {return instance;}
const char *devif_getName() {return getName();}
// other functions
void handle_ui_cmd(int argc, char * argv[]);
void initPci();
};
#endif // __SAS_H__