/*	$OpenBSD: gdtvar.h,v 1.12 2006/05/07 23:18:59 marco Exp $	*/

/*
 * Copyright (c) 1999, 2000 Niklas Hallqvist.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This driver would not have written if it was not for the hardware donations
 * from both ICP-Vortex and Öko.neT.  I want to thank them for their support.
 */

#define DEVNAME(s)  ((s)->sc_dev.dv_xname)
#define GDT_CMD_RESERVE	4	/* Internal driver cmd reserve. */

#define GDT_IOCTL_DUMMY _IOWR('B', 32, struct gdt_dummy)
struct gdt_dummy {
	void *cookie;
	int x;
};

/* XXX Is this pragma necessary?  */
#pragma pack(1)

#define GDT_SCRATCH_SZ 4096

#define GDT_IOCTL_GENERAL _IOWR('B', 33, gdt_ucmd_t)	/* general IOCTL */
typedef struct gdt_ucmd {
	void *cookie;
	u_int16_t io_node;
	u_int16_t service;
	u_int32_t timeout;
	u_int16_t status;
	u_int32_t info;

	u_int32_t BoardNode;			/* board node (always 0) */
	u_int32_t CommandIndex;			/* command number */
	u_int16_t OpCode;			/* the command (READ,..) */
	union {
		struct {
			u_int16_t DeviceNo;	/* number of cache drive */
			u_int32_t BlockNo;	/* block number */
			u_int32_t BlockCnt;	/* block count */
			void	*DestAddr;	/* data */
		} cache;			/* cache service cmd. str. */
		struct {
			u_int16_t param_size;	/* size of p_param buffer */
			u_int32_t subfunc;	/* IOCTL function */
			u_int32_t channel;	/* device */
			void	*p_param;	/* data */
		} ioctl;			/* IOCTL command structure */
		struct {
			u_int16_t reserved;
			u_int32_t direction;	/* data direction */
			u_int32_t mdisc_time;	/* disc. time (0: no timeout)*/
			u_int32_t mcon_time;	/* connect time(0: no to.) */
			void	*sdata;		/* dest. addr. (if s/g: -1) */
			u_int32_t sdlen;	/* data length (bytes) */
			u_int32_t clen;		/* SCSI cmd. length(6,10,12) */
			u_int8_t cmd[12];	/* SCSI command */
			u_int8_t target;	/* target ID */
			u_int8_t lun;		/* LUN */
			u_int8_t bus;		/* SCSI bus number */
			u_int8_t priority;	/* only 0 used */
			u_int32_t sense_len;	/* sense data length */
			void	*sense_data;	/* sense data addr. */
			u_int32_t link_p;	/* linked cmds (not supp.) */
		} raw;				/* raw service cmd. struct. */
	} u;
	u_int8_t data[GDT_SCRATCH_SZ];
	int	complete_flag;
	TAILQ_ENTRY(gdt_ucmd) links;
} gdt_ucmd_t;

#define GDT_IOCTL_DRVERS _IOWR('B', 34, int)	/* get driver version */
typedef struct gdt_drvers {
	void *cookie;
	int vers;
} gdt_drvers_t;

#define GDT_IOCTL_CTRTYPE _IOR('B', 35, gdt_ctrt_t)	/* get ctr. type */
typedef struct gdt_ctrt {
	void *cookie;
	u_int16_t io_node;
	u_int16_t oem_id;
	u_int16_t type;
	u_int32_t info;
	u_int8_t access;
	u_int8_t remote;
	u_int16_t ext_type;
	u_int16_t device_id;
	u_int16_t sub_device_id;
} gdt_ctrt_t;

#define GDT_IOCTL_OSVERS _IOR('B', 36, gdt_osv_t)	/* get OS version */
typedef struct gdt_osv {
	void *cookie;
	u_int8_t oscode;
	u_int8_t version;
	u_int8_t subversion;
	u_int16_t revision;
	char	name[64];
} gdt_osv_t;

#define GDT_IOCTL_CTRCNT _IOR('B', 37, int)	/* get ctr. count */
typedef struct gdt_ctrcnt {
	void *cookie;
	int cnt;
} gdt_ctrcnt_t;

#define GDT_IOCTL_EVENT _IOWR('B', 38, gdt_event_t)	/* get event */

typedef struct {
	u_int16_t size;		/* size of structure */
	union {
		char	stream[16];
		struct {
			u_int16_t ionode;
			u_int16_t service;
			u_int32_t index;
		} driver;
		struct {
			u_int16_t ionode;
			u_int16_t service;
			u_int16_t status;
			u_int32_t info;
			u_int8_t scsi_coord[3];
		} async;
		struct {
			u_int16_t ionode;
			u_int16_t service;
			u_int16_t status;
			u_int32_t info;
			u_int16_t hostdrive;
			u_int8_t scsi_coord[3];
			u_int8_t sense_key;
		} sync;
		struct {
			u_int32_t l1, l2, l3, l4;
		} test;
	} eu;
	u_int32_t severity;
	u_int8_t event_string[256];          
} gdt_evt_data;
#define GDT_ES_ASYNC	1
#define GDT_ES_DRIVER	2
#define GDT_ES_TEST	3
#define GDT_ES_SYNC	4

/* dvrevt structure */
typedef struct {
	u_int32_t first_stamp;
	u_int32_t last_stamp;
	u_int16_t same_count;
	u_int16_t event_source;
	u_int16_t event_idx;
	u_int8_t application;
	u_int8_t reserved;
	gdt_evt_data event_data;
} gdt_evt_str;

typedef struct gdt_event {
	void *cookie;
	int erase;
	int handle;
	gdt_evt_str dvr;
} gdt_event_t;

#define GDT_IOCTL_STATIST _IOR('B', 39, gdt_statist_t) /* get statistics */
typedef struct gdt_statist {
	void *cookie;
	u_int16_t io_count_act;
	u_int16_t io_count_max;
	u_int16_t req_queue_act;
	u_int16_t req_queue_max;
	u_int16_t cmd_index_act;
	u_int16_t cmd_index_max;
	u_int16_t sg_count_act;
	u_int16_t sg_count_max;
} gdt_statist_t;

#pragma pack()

#ifdef _KERNEL

/* Debugging */
/* #define GDT_DEBUG	GDT_D_IOCTL | GDT_D_INFO */
#ifdef GDT_DEBUG
#define GDT_DPRINTF(mask, args) if (gdt_debug & (mask)) printf args
#define GDT_D_INTR	0x01
#define GDT_D_MISC	0x02
#define GDT_D_CMD	0x04
#define GDT_D_QUEUE	0x08
#define GDT_D_IO	0x10
#define GDT_D_IOCTL	0x20
#define GDT_D_INFO	0x40
extern int gdt_debug;
#else
#define GDT_DPRINTF(mask, args)
#endif

/* Miscellaneous constants */
#define GDT_RETRIES		100000000	/* 100000 * 1us = 100s */
#define GDT_TIMEOUT		100000000	/* 100000 * 1us = 100s */
#define GDT_POLL_TIMEOUT	10000000	/* 10000 * 1us = 10s */
#define GDT_WATCH_TIMEOUT	10000		/* 10000 * 1ms = 10s */

/* Context structure for interrupt services */
struct gdt_intr_ctx {
	u_int32_t info, info2;
	u_int16_t cmd_status, service;
	u_int8_t istatus;
};

/*
 * A command contol block, one for each corresponding command index of the
 * controller.
 */
struct gdt_ccb {
	TAILQ_ENTRY(gdt_ccb) gc_chain;
	struct scsi_xfer *gc_xs;
	struct gdt_ucmd *gc_ucmd;
	bus_dmamap_t gc_dmamap_xfer;
	int gc_timeout;
	u_int32_t gc_info;
	u_int32_t gc_blockno;
	u_int32_t gc_blockcnt;
	u_int16_t gc_status;
	u_int8_t gc_service;
	u_int8_t gc_cmd_index;
	u_int8_t gc_flags;
#define GDT_GCF_CMD_MASK	0x3
#define GDT_GCF_UNUSED		0	
#define GDT_GCF_INTERNAL	1
#define GDT_GCF_SCREEN 		2
#define GDT_GCF_SCSI 		3
#define GDT_GCF_WATCHDOG 	0x4
};

static __inline__ int gdt_ccb_set_cmd(struct gdt_ccb *, int);
static __inline__ int
gdt_ccb_set_cmd(ccb, flag)
	struct gdt_ccb *ccb;
	int flag;
{
	int rv = ccb->gc_flags & GDT_GCF_CMD_MASK;

	ccb->gc_flags &= ~GDT_GCF_CMD_MASK;
	ccb->gc_flags |= flag;
	return (rv);
}

struct gdt_softc {
	struct	device sc_dev;
	void   *sc_ih;
	struct	scsi_link sc_link;	/* Virtual SCSI bus for cache devs */
	struct	scsi_link *sc_raw_link;	/* Raw SCSI busses */

	int	sc_class;		/* Controller class */
#define GDT_ISA		0x01
#define GDT_EISA	0x02
#define GDT_PCI		0x03
#define GDT_PCINEW	0x04
#define GDT_MPR		0x05
#define GDT_CLASS_MASK	0x07
#define GDT_FC		0x10
#define GDT_CLASS(gdt)	((gdt)->sc_class & GDT_CLASS_MASK)

	bus_space_tag_t sc_dpmemt;
	bus_space_handle_t sc_dpmemh;
	bus_addr_t sc_dpmembase;
	bus_dma_tag_t sc_dmat;

	/* XXX These could go into a class-dependent opaque struct instead */
	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_ioh;
	bus_addr_t sc_iobase;

	u_int16_t sc_ic_all_size;

	struct gdt_ccb sc_ccbs[GDT_MAXCMDS];
	TAILQ_HEAD(, gdt_ccb) sc_free_ccb, sc_ccbq;
	TAILQ_HEAD(, gdt_ucmd) sc_ucmdq;
	LIST_HEAD(, scsi_xfer) sc_queue;
	struct scsi_xfer *sc_queuelast;

	int	sc_ndevs;

	u_int16_t sc_cmd_len;
	u_int16_t sc_cmd_off;
	u_int16_t sc_cmd_cnt;
	u_int8_t sc_cmd[GDT_CMD_SZ];

	u_int32_t sc_info;
	u_int32_t sc_info2;
	bus_dma_segment_t sc_scratch_seg;
	caddr_t sc_scratch;
	u_int16_t sc_status;

	u_int8_t sc_bus_cnt;
	u_int8_t sc_bus_id[GDT_MAXBUS];
	u_int8_t sc_more_proc;

	u_int32_t sc_total_disks;

	struct {
		u_int8_t hd_present;
		u_int8_t hd_is_logdrv;
		u_int8_t hd_is_arraydrv;
		u_int8_t hd_is_master;
		u_int8_t hd_is_parity;
		u_int8_t hd_is_hotfix;
		u_int8_t hd_master_no;
		u_int8_t hd_lock;
		u_int8_t hd_heads;
		u_int8_t hd_secs;
		u_int16_t hd_devtype;
		u_int32_t hd_size;
		u_int8_t hd_ldr_no;
		u_int8_t hd_rw_attribs;
		u_int32_t hd_start_sec;
	} sc_hdr[GDT_MAX_HDRIVES];

	struct {
		u_int8_t	ra_lock;	/* chan locked? (hot plug */
		u_int8_t	ra_phys_cnt;	/* physical disk count */
		u_int8_t	ra_local_no;	/* local channel number */
		u_int8_t	ra_io_cnt[GDT_MAXID];	/* current IO count */
		u_int32_t	ra_address;	/* channel address */
		u_int32_t	ra_id_list[GDT_MAXID];	/* IDs of phys disks */
	} sc_raw[GDT_MAXBUS];			/* SCSI channels */

	struct {
		u_int32_t cp_version;
		u_int16_t cp_state;
		u_int16_t cp_strategy;
		u_int16_t cp_write_back;
		u_int16_t cp_block_size;
	} sc_cpar;

	struct {
		u_int32_t bi_ser_no;		/* serial number */
		u_int8_t bi_oem_id[2];		/* u_int8_t OEM ID */
		u_int16_t bi_ep_flags;		/* eprom flags */
		u_int32_t bi_proc_id;		/* processor ID */
		u_int32_t bi_memsize;		/* memory size (bytes) */
		u_int8_t bi_mem_banks;		/* memory banks */
		u_int8_t bi_chan_type;		/* channel type */
		u_int8_t bi_chan_count;		/* channel count */
		u_int8_t bi_rdongle_pres;	/* dongle present */
		u_int32_t bi_epr_fw_ver;	/* (eprom) firmware ver */
		u_int32_t bi_upd_fw_ver;	/* (update) firmware ver */
		u_int32_t bi_upd_revision;	/* update revision */
		char bi_type_string[16];	/* char controller name */
		char bi_raid_string[16];	/* char RAID firmware name */
		u_int8_t bi_update_pres;	/* update present? */
		u_int8_t bi_xor_pres;		/* XOR engine present */
		u_int8_t bi_prom_type;		/* ROM type (eprom/flash) */
		u_int8_t bi_prom_count;		/* number of ROM devices */
		u_int32_t bi_dup_pres;		/* duplexing module pres? */
		u_int32_t bi_chan_pres;		/* # of exp. channels */
		u_int32_t bi_mem_pres;		/* memory expansion inst? */
		u_int8_t bi_ft_bus_system;	/* fault bus supported? */
		u_int8_t bi_subtype_valid;	/* board_subtype valid */
		u_int8_t bi_board_subtype;	/* subtype/hardware level */
		u_int8_t bi_rampar_pres;	/* RAM parity check hw? */
	} sc_binfo;

	struct {
		u_int8_t bf_chaining;	/* chaining supported */
		u_int8_t bf_striping;	/* striping (RAID-0) supported */
		u_int8_t bf_mirroring;	/* mirroring (RAID-1) supported */
		u_int8_t bf_raid;	/* RAID-4/5/10 supported */
	} sc_bfeat;

	u_int16_t sc_raw_feat;
	u_int16_t sc_cache_feat;

	void (*sc_copy_cmd)(struct gdt_softc *, struct gdt_ccb *);
	u_int8_t (*sc_get_status)(struct gdt_softc *);
	void (*sc_intr)(struct gdt_softc *, struct gdt_intr_ctx *);
	void (*sc_release_event)(struct gdt_softc *, struct gdt_ccb *);
	void (*sc_set_sema0)(struct gdt_softc *);
	int (*sc_test_busy)(struct gdt_softc *);
};

/* XXX These have to become spinlocks in case of SMP */
#define GDT_LOCK_GDT(gdt) splbio()
#define GDT_UNLOCK_GDT(gdt, lock) splx(lock)
typedef int gdt_lock_t;

void	gdtminphys(struct buf *);
int	gdt_attach(struct gdt_softc *);
int	gdt_intr(void *);

#ifdef __GNUC__
/* These all require correctly aligned buffers */
static __inline__ void gdt_enc16(u_int8_t *, u_int16_t);
static __inline__ void gdt_enc32(u_int8_t *, u_int32_t);
static __inline__ u_int8_t gdt_dec8(u_int8_t *);
static __inline__ u_int16_t gdt_dec16(u_int8_t *);
static __inline__ u_int32_t gdt_dec32(u_int8_t *);

static __inline__ void
gdt_enc16(addr, value)
	u_int8_t *addr;
	u_int16_t value;
{
	*(u_int16_t *)addr = htole16(value);
}

static __inline__ void
gdt_enc32(addr, value)
	u_int8_t *addr;
	u_int32_t value;
{
	*(u_int32_t *)addr = htole32(value);
}

static __inline__ u_int8_t
gdt_dec8(addr)
	u_int8_t *addr;
{
	return *(u_int8_t *)addr;
}

static __inline__ u_int16_t
gdt_dec16(addr)
	u_int8_t *addr;
{
	return letoh16(*(u_int16_t *)addr);
}

static __inline__ u_int32_t
gdt_dec32(addr)
	u_int8_t *addr;
{
	return letoh32(*(u_int32_t *)addr);
}
#endif

extern u_int8_t gdt_polling;

#endif
