Add the 'ips' driver for the IBM (now Adaptec) ServeRAID controller

series.  This driver was generously developed and released by David
Jeffreys and Adaptec.  I've updated it to work with 5.x and fixed a
few bugs.

MFC After:	1 week
This commit is contained in:
scottl 2003-05-11 06:36:49 +00:00
parent 24ca3b293f
commit 3c8a50d786
9 changed files with 2372 additions and 0 deletions

697
sys/dev/ips/ips.c Normal file
View File

@ -0,0 +1,697 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <dev/ips/ips.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <machine/clock.h>
static d_open_t ips_open;
static d_close_t ips_close;
static d_ioctl_t ips_ioctl;
#define IPS_CDEV_MAJOR 175
static struct cdevsw ips_cdevsw = {
.d_open = ips_open,
.d_close = ips_close,
.d_ioctl = ips_ioctl,
.d_name = "ips",
.d_maj = IPS_CDEV_MAJOR,
};
static int ips_open(dev_t dev, int flags, int fmt, struct thread *td)
{
ips_softc_t *sc = dev->si_drv1;
sc->state |= IPS_DEV_OPEN;
return 0;
}
static int ips_close(dev_t dev, int flags, int fmt, struct thread *td)
{
ips_softc_t *sc = dev->si_drv1;
sc->state &= ~IPS_DEV_OPEN;
return 0;
}
static int ips_ioctl(dev_t dev, u_long command, caddr_t addr, int32_t flags, struct thread *td)
{
ips_softc_t *sc;
sc = dev->si_drv1;
return ips_ioctl_request(sc, command, addr, flags);
}
static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_command_t *command = cmdptr;
PRINTF(10, "ips: in ips_cmd_dmaload\n");
if(!error)
command->command_phys_addr = segments[0].ds_addr;
}
/* is locking needed? what locking guarentees are there on removal? */
static __inline__ int ips_cmdqueue_free(ips_softc_t *sc)
{
int i, error = -1;
intrmask_t mask = splbio();
if(!sc->used_commands){
for(i = 0; i < sc->max_cmds; i++){
if(!(sc->commandarray[i].command_phys_addr))
continue;
bus_dmamap_unload(sc->command_dmatag,
sc->commandarray[i].command_dmamap);
bus_dmamem_free(sc->command_dmatag,
sc->commandarray[i].command_buffer,
sc->commandarray[i].command_dmamap);
}
error = 0;
sc->state |= IPS_OFFLINE;
}
splx(mask);
return error;
}
/* places all ips command structs on the free command queue. No locking as if someone else tries
* to access this during init, we have bigger problems */
static __inline__ int ips_cmdqueue_init(ips_softc_t *sc)
{
int i;
ips_command_t *command;
SLIST_INIT(&sc->free_cmd_list);
STAILQ_INIT(&sc->cmd_wait_list);
for(i = 0; i < sc->max_cmds; i++){
sc->commandarray[i].id = i;
sc->commandarray[i].sc = sc;
SLIST_INSERT_HEAD(&sc->free_cmd_list, &sc->commandarray[i],
next);
}
for(i = 0; i < sc->max_cmds; i++){
command = &sc->commandarray[i];
if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer,
BUS_DMA_NOWAIT, &command->command_dmamap))
goto error;
bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
command->command_buffer,IPS_COMMAND_LEN,
ips_cmd_dmaload, command, BUS_DMA_NOWAIT);
if(!command->command_phys_addr){
bus_dmamem_free(sc->command_dmatag,
command->command_buffer, command->command_dmamap);
goto error;
}
}
sc->state &= ~IPS_OFFLINE;
return 0;
error:
ips_cmdqueue_free(sc);
return ENOMEM;
}
static int ips_add_waiting_command(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags)
{
intrmask_t mask;
ips_command_t *command;
ips_wait_list_t *waiter;
unsigned long memflags = 0;
if(IPS_NOWAIT_FLAG & flags)
memflags = M_NOWAIT;
waiter = malloc(sizeof(ips_wait_list_t), M_DEVBUF, memflags);
if(!waiter)
return ENOMEM;
mask = splbio();
if(sc->state & IPS_OFFLINE){
splx(mask);
return EIO;
}
command = SLIST_FIRST(&sc->free_cmd_list);
if(command && !(sc->state & IPS_TIMEOUT)){
SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
(sc->used_commands)++;
splx(mask);
clear_ips_command(command);
bzero(command->command_buffer, IPS_COMMAND_LEN);
free(waiter, M_DEVBUF);
command->arg = data;
return callback(command);
}
DEVICE_PRINTF(1, sc->dev, "adding command to the wait queue\n");
waiter->callback = callback;
waiter->data = data;
STAILQ_INSERT_TAIL(&sc->cmd_wait_list, waiter, next);
splx(mask);
return 0;
}
static void ips_run_waiting_command(ips_softc_t *sc)
{
ips_wait_list_t *waiter;
ips_command_t *command;
int (*callback)(ips_command_t*);
intrmask_t mask;
mask = splbio();
waiter = STAILQ_FIRST(&sc->cmd_wait_list);
command = SLIST_FIRST(&sc->free_cmd_list);
if(!waiter || !command){
splx(mask);
return;
}
DEVICE_PRINTF(1, sc->dev, "removing command from wait queue\n");
SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
STAILQ_REMOVE_HEAD(&sc->cmd_wait_list, next);
(sc->used_commands)++;
splx(mask);
clear_ips_command(command);
bzero(command->command_buffer, IPS_COMMAND_LEN);
command->arg = waiter->data;
callback = waiter->callback;
free(waiter, M_DEVBUF);
callback(command);
return;
}
/* returns a free command struct if one is available.
* It also blanks out anything that may be a wild pointer/value.
* Also, command buffers are not freed. They are
* small so they are saved and kept dmamapped and loaded.
*/
int ips_get_free_cmd(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags)
{
intrmask_t mask;
ips_command_t *command;
mask = splbio();
if(sc->state & IPS_OFFLINE){
splx(mask);
return EIO;
}
command = SLIST_FIRST(&sc->free_cmd_list);
if(!command || (sc->state & IPS_TIMEOUT)){
splx(mask);
if(flags & IPS_NOWAIT_FLAG)
return EAGAIN;
return ips_add_waiting_command(sc, callback, data, flags);
}
SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
(sc->used_commands)++;
splx(mask);
clear_ips_command(command);
bzero(command->command_buffer, IPS_COMMAND_LEN);
command->arg = data;
return callback(command);
}
/* adds a command back to the free command queue */
void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
{
intrmask_t mask;
mask = splbio();
SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
(sc->used_commands)--;
splx(mask);
if(!(sc->state & IPS_TIMEOUT))
ips_run_waiting_command(sc);
}
static int ips_diskdev_init(ips_softc_t *sc)
{
int i;
for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
if(sc->drives[i].state & IPS_LD_OKAY){
sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
device_set_ivars(sc->diskdev[i],(void *) i);
}
}
if(bus_generic_attach(sc->dev)){
device_printf(sc->dev, "Attaching bus failed\n");
}
return 0;
}
static int ips_diskdev_free(ips_softc_t *sc)
{
int i;
int error = 0;
for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){
if(sc->diskdev[i])
error = device_delete_child(sc->dev, sc->diskdev[i]);
if(error)
return error;
}
bus_generic_detach(sc->dev);
return 0;
}
/* ips_timeout is periodically called to make sure no commands sent
* to the card have become stuck. If it finds a stuck command, it
* sets a flag so the driver won't start any more commands and then
* is periodically called to see if all outstanding commands have
* either finished or timed out. Once timed out, an attempt to
* reinitialize the card is made. If that fails, the driver gives
* up and declares the card dead. */
static void ips_timeout(void *arg)
{
intrmask_t mask;
ips_softc_t *sc = arg;
int i, state = 0;
ips_command_t *command;
command = &sc->commandarray[0];
mask = splbio();
for(i = 0; i < sc->max_cmds; i++){
if(!command[i].timeout){
continue;
}
command[i].timeout--;
if(!command[i].timeout){
if(!(sc->state & IPS_TIMEOUT)){
sc->state |= IPS_TIMEOUT;
device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
}
command[i].status.value = IPS_ERROR_STATUS;
command[i].callback(&command[i]);
/* hmm, this should be enough cleanup */
} else
state = 1;
}
if(!state && (sc->state & IPS_TIMEOUT)){
if(sc->ips_adapter_reinit(sc, 1)){
device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n");
sc->state |= IPS_OFFLINE;
sc->state &= ~IPS_TIMEOUT;
/* Grr, I hate this solution. I run waiting commands
one at a time and error them out just before they
would go to the card. This sucks. */
} else
sc->state &= ~IPS_TIMEOUT;
ips_run_waiting_command(sc);
}
if (sc->state != IPS_OFFLINE)
sc->timer = timeout(ips_timeout, sc, 10*hz);
splx(mask);
}
/* check card and initialize it */
int ips_adapter_init(ips_softc_t *sc)
{
DEVICE_PRINTF(1,sc->dev, "initializing\n");
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ IPS_COMMAND_LEN +
IPS_MAX_SG_LEN,
/* numsegs */ 1,
/* maxsegsize*/ IPS_COMMAND_LEN +
IPS_MAX_SG_LEN,
/* flags */ 0,
&sc->command_dmatag) != 0) {
device_printf(sc->dev, "can't alloc command dma tag\n");
goto error;
}
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ IPS_MAX_IOBUF_SIZE,
/* numsegs */ IPS_MAX_SG_ELEMENTS,
/* maxsegsize*/ IPS_MAX_IOBUF_SIZE,
/* flags */ 0,
&sc->sg_dmatag) != 0) {
device_printf(sc->dev, "can't alloc SG dma tag\n");
goto error;
}
/* create one command buffer until we know how many commands this card
can handle */
sc->max_cmds = 1;
ips_cmdqueue_init(sc);
if(sc->ips_adapter_reinit(sc, 0))
goto error;
mtx_init(&sc->cmd_mtx, "ips command mutex", NULL, MTX_DEF);
if(ips_get_adapter_info(sc) || ips_get_drive_info(sc)){
device_printf(sc->dev, "failed to get configuration data from device\n");
goto error;
}
ips_update_nvram(sc); /* no error check as failure doesn't matter */
ips_cmdqueue_free(sc);
if(sc->adapter_info.max_concurrent_cmds)
sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
else
sc->max_cmds = 32;
if(ips_cmdqueue_init(sc)){
device_printf(sc->dev, "failed to initialize command buffers\n");
goto error;
}
sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR,
S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev));
sc->device_file->si_drv1 = sc;
ips_diskdev_init(sc);
sc->timer = timeout(ips_timeout, sc, 10*hz);
return 0;
error:
ips_adapter_free(sc);
return ENXIO;
}
/* see if we should reinitialize the card and wait for it to timeout or complete initialization */
int ips_morpheus_reinit(ips_softc_t *sc, int force)
{
u_int32_t tmp;
int i;
tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
(ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){
ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
return 0;
}
ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
ips_read_4(sc, MORPHEUS_REG_OIMR);
device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n");
ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
DELAY(5000000);
pci_read_config(sc->dev, 0, 4);
tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){
DELAY(1000000);
DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
}
if(tmp & MORPHEUS_BIT_POST1)
ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){
device_printf(sc->dev,"Adapter error during initialization.\n");
return 1;
}
for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){
DELAY(1000000);
DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
}
if(tmp & MORPHEUS_BIT_POST2)
ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){
device_printf(sc->dev, "adapter failed config check\n");
return 1;
}
ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
if(force && ips_clear_adapter(sc)){
device_printf(sc->dev, "adapter clear failed\n");
return 1;
}
return 0;
}
/* clean up so we can unload the driver. */
int ips_adapter_free(ips_softc_t *sc)
{
int error = 0;
intrmask_t mask;
if(sc->state & IPS_DEV_OPEN)
return EBUSY;
if((error = ips_diskdev_free(sc)))
return error;
if(ips_cmdqueue_free(sc)){
device_printf(sc->dev,
"trying to exit when command queue is not empty!\n");
return EBUSY;
}
DEVICE_PRINTF(1, sc->dev, "free\n");
mask = splbio();
untimeout(ips_timeout, sc, sc->timer);
splx(mask);
if (mtx_initialized(&sc->cmd_mtx))
mtx_destroy(&sc->cmd_mtx);
if(sc->sg_dmatag)
bus_dma_tag_destroy(sc->sg_dmatag);
if(sc->command_dmatag)
bus_dma_tag_destroy(sc->command_dmatag);
if(sc->device_file)
destroy_dev(sc->device_file);
return 0;
}
void ips_morpheus_intr(void *void_sc)
{
ips_softc_t *sc = (ips_softc_t *)void_sc;
u_int32_t oisr, iisr;
int cmdnumber;
ips_cmd_status_t status;
iisr =ips_read_4(sc, MORPHEUS_REG_IISR);
oisr =ips_read_4(sc, MORPHEUS_REG_OISR);
PRINTF(9,"interrupt registers in:%x out:%x\n",iisr, oisr);
if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){
DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n");
return;
}
while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){
cmdnumber = status.fields.command_id;
sc->commandarray[cmdnumber].status.value = status.value;
sc->commandarray[cmdnumber].timeout = 0;
sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
DEVICE_PRINTF(9,sc->dev, "got command %d\n", cmdnumber);
}
return;
}
void ips_issue_morpheus_cmd(ips_command_t *command)
{
intrmask_t mask = splbio();
/* hmmm, is there a cleaner way to do this? */
if(command->sc->state & IPS_OFFLINE){
splx(mask);
command->status.value = IPS_ERROR_STATUS;
command->callback(command);
return;
}
command->timeout = 10;
ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
splx(mask);
}
static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_copper_queue_t *queue = queueptr;
if(error){
return;
}
queue->base_phys_addr = segments[0].ds_addr;
}
static int ips_copperhead_queue_init(ips_softc_t *sc)
{
int error;
bus_dma_tag_t dmatag;
bus_dmamap_t dmamap;
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ sizeof(ips_copper_queue_t),
/* numsegs */ 1,
/* maxsegsize*/ sizeof(ips_copper_queue_t),
/* flags */ 0,
&dmatag) != 0) {
device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
error = ENOMEM;
goto exit;
}
if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
BUS_DMA_NOWAIT, &dmamap)){
error = ENOMEM;
goto exit;
}
bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
sc->copper_queue->dmatag = dmatag;
sc->copper_queue->dmamap = dmamap;
sc->copper_queue->nextstatus = 1;
bus_dmamap_load(dmatag, dmamap,
&(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4,
ips_copperhead_queue_callback, sc->copper_queue,
BUS_DMA_NOWAIT);
if(sc->copper_queue->base_phys_addr == 0){
error = ENOMEM;
goto exit;
}
ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
IPS_MAX_CMD_NUM * 4);
ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
return 0;
exit:
bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
bus_dma_tag_destroy(dmatag);
return error;
}
/* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */
int ips_copperhead_reinit(ips_softc_t *sc, int force)
{
int i, j;
u_int32_t postcode = 0, configstatus = 0;
ips_write_1(sc, COPPER_REG_SCPR, 0x80);
ips_write_1(sc, COPPER_REG_SCPR, 0);
device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n");
for(j = 0; j < 2; j++){
postcode <<= 8;
for(i = 0; i < 45; i++){
if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
postcode |= ips_read_1(sc, COPPER_REG_ISPR);
ips_write_1(sc, COPPER_REG_HISR,
COPPER_GHI_BIT);
break;
} else
DELAY(1000000);
}
if(i == 45)
return 1;
}
for(j = 0; j < 2; j++){
configstatus <<= 8;
for(i = 0; i < 240; i++){
if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
ips_write_1(sc, COPPER_REG_HISR,
COPPER_GHI_BIT);
break;
} else
DELAY(1000000);
}
if(i == 240)
return 1;
}
for(i = 0; i < 240; i++){
if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){
break;
} else
DELAY(1000000);
}
if(i == 240)
return 1;
ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
ips_copperhead_queue_init(sc);
ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
i = ips_read_1(sc, COPPER_REG_SCPR);
ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
if(!configstatus){
device_printf(sc->dev, "adapter initialization failed\n");
return 1;
}
if(force && ips_clear_adapter(sc)){
device_printf(sc->dev, "adapter clear failed\n");
return 1;
}
return 0;
}
static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc)
{
intrmask_t mask;
u_int32_t value;
int statnum = sc->copper_queue->nextstatus++;
if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
sc->copper_queue->nextstatus = 0;
mask = splbio();
value = sc->copper_queue->status[statnum];
ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
4 * statnum);
splx(mask);
return value;
}
void ips_copperhead_intr(void *void_sc)
{
ips_softc_t *sc = (ips_softc_t *)void_sc;
int cmdnumber;
ips_cmd_status_t status;
while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){
status.value = ips_copperhead_cmd_status(sc);
cmdnumber = status.fields.command_id;
sc->commandarray[cmdnumber].status.value = status.value;
sc->commandarray[cmdnumber].timeout = 0;
sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
PRINTF(9, "ips: got command %d\n", cmdnumber);
}
return;
}
void ips_issue_copperhead_cmd(ips_command_t *command)
{
int i;
intrmask_t mask = splbio();
/* hmmm, is there a cleaner way to do this? */
if(command->sc->state & IPS_OFFLINE){
splx(mask);
command->status.value = IPS_ERROR_STATUS;
command->callback(command);
return;
}
command->timeout = 10;
for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
i++ ){
if( i == 20){
printf("sem bit still set, can't send a command\n");
splx(mask);
return;
}
DELAY(500);/* need to do a delay here */
}
ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
splx(mask);
}

403
sys/dev/ips/ips.h Normal file
View File

@ -0,0 +1,403 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <machine/bus_memio.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
/*
* IPS CONSTANTS
*/
#define IPS_VENDOR_ID 0x1014
#define IPS_MORPHEUS_DEVICE_ID 0x01BD
#define IPS_COPPERHEAD_DEVICE_ID 0x002E
#define IPS_CSL 0xff
#define IPS_POCL 0x30
/* amounts of memory to allocate for certain commands */
#define IPS_ADAPTER_INFO_LEN (sizeof(ips_adapter_info_t))
#define IPS_DRIVE_INFO_LEN (sizeof(ips_drive_info_t))
#define IPS_COMMAND_LEN 24
#define IPS_MAX_SG_LEN (sizeof(ips_sg_element_t) * IPS_MAX_SG_ELEMENTS)
#define IPS_NVRAM_PAGE_SIZE 128
/* various flags */
#define IPS_NOWAIT_FLAG 1
/* states for the card to be in */
#define IPS_DEV_OPEN 0x01
#define IPS_TIMEOUT 0x02 /* command time out, need reset */
#define IPS_OFFLINE 0x04 /* can't reset card/card failure */
/* max number of commands set to something low for now */
#define IPS_MAX_CMD_NUM 128
#define IPS_MAX_NUM_DRIVES 8
#define IPS_MAX_SG_ELEMENTS 32
#define IPS_MAX_IOBUF_SIZE (64 * 1024)
#define IPS_BLKSIZE 512
/* logical drive states */
#define IPS_LD_OFFLINE 0x02
#define IPS_LD_OKAY 0x03
#define IPS_LD_FREE 0x00
#define IPS_LD_SYS 0x06
#define IPS_LD_CRS 0x24
/* register offsets */
#define MORPHEUS_REG_OMR0 0x0018 /* Outbound Msg. Reg. 0 */
#define MORPHEUS_REG_OMR1 0x001C /* Outbound Msg. Reg. 1 */
#define MORPHEUS_REG_IDR 0x0020 /* Inbound Doorbell Reg. */
#define MORPHEUS_REG_IISR 0x0024 /* Inbound IRQ Status Reg. */
#define MORPHEUS_REG_IIMR 0x0028 /* Inbound IRQ Mask Reg. */
#define MORPHEUS_REG_OISR 0x0030 /* Outbound IRQ Status Reg. */
#define MORPHEUS_REG_OIMR 0x0034 /* Outbound IRQ Status Reg. */
#define MORPHEUS_REG_IQPR 0x0040 /* Inbound Queue Port Reg. */
#define MORPHEUS_REG_OQPR 0x0044 /* Outbound Queue Port Reg. */
#define COPPER_REG_SCPR 0x05 /* Subsystem Ctrl. Port Reg. */
#define COPPER_REG_ISPR 0x06 /* IRQ Status Port Reg. */
#define COPPER_REG_CBSP 0x07 /* ? Reg. */
#define COPPER_REG_HISR 0x08 /* Host IRQ Status Reg. */
#define COPPER_REG_CCSAR 0x10 /* Cmd. Channel Sys Addr Reg.*/
#define COPPER_REG_CCCR 0x14 /* Cmd. Channel Ctrl. Reg. */
#define COPPER_REG_SQHR 0x20 /* Status Queue Head Reg. */
#define COPPER_REG_SQTR 0x24 /* Status Queue Tail Reg. */
#define COPPER_REG_SQER 0x28 /* Status Queue End Reg. */
#define COPPER_REG_SQSR 0x2C /* Status Queue Start Reg. */
/* bit definitions */
#define MORPHEUS_BIT_POST1 0x01
#define MORPHEUS_BIT_POST2 0x02
#define MORPHEUS_BIT_CMD_IRQ 0x08
#define COPPER_CMD_START 0x101A
#define COPPER_SEM_BIT 0x08
#define COPPER_EI_BIT 0x80
#define COPPER_EBM_BIT 0x02
#define COPPER_RESET_BIT 0x80
#define COPPER_GHI_BIT 0x04
#define COPPER_SCE_BIT 0x01
#define COPPER_OP_BIT 0x01
#define COPPER_ILE_BIT 0x10
/* status defines */
#define IPS_POST1_OK 0x8000
#define IPS_POST2_OK 0x000f
/* command op codes */
#define IPS_READ_CMD 0x02
#define IPS_WRITE_CMD 0x03
#define IPS_ADAPTER_INFO_CMD 0x05
#define IPS_CACHE_FLUSH_CMD 0x0A
#define IPS_REBUILD_STATUS_CMD 0x0C
#define IPS_ERROR_TABLE_CMD 0x17
#define IPS_DRIVE_INFO_CMD 0x19
#define IPS_SUBSYS_PARAM_CMD 0x40
#define IPS_CONFIG_SYNC_CMD 0x58
#define IPS_SG_READ_CMD 0x82
#define IPS_SG_WRITE_CMD 0x83
#define IPS_RW_NVRAM_CMD 0xBC
/* error information returned by the adapter */
#define IPS_MIN_ERROR 0x02
#define IPS_ERROR_STATUS 0x13000200 /* ahh, magic numbers */
#define IPS_OS_FREEBSD 10
#define IPS_VERSION_MAJOR "0.90"
#define IPS_VERSION_MINOR ".00"
/*
* IPS MACROS
*/
#define ips_read_1(sc,offset) bus_space_read_1(sc->bustag, sc->bushandle, offset)
#define ips_read_2(sc,offset) bus_space_read_2(sc->bustag, sc->bushandle, offset)
#define ips_read_4(sc,offset) bus_space_read_4(sc->bustag, sc->bushandle, offset)
#define ips_write_1(sc,offset,value) bus_space_write_1(sc->bustag, sc->bushandle, offset, value)
#define ips_write_2(sc,offset,value) bus_space_write_2(sc->bustag, sc->bushandle, offset, value)
#define ips_write_4(sc,offset,value) bus_space_write_4(sc->bustag, sc->bushandle, offset, value)
/* this is ugly. It zeros the end elements in an ips_command_t struct starting with the status element */
#define clear_ips_command(command) bzero(&((command)->status), (unsigned long)(&(command)[1])-(unsigned long)&((command)->status))
#define ips_read_request(iobuf) ((iobuf)->bio_cmd == BIO_READ)
#define COMMAND_ERROR(status) (((status)->fields.basic_status & 0x0f) >= IPS_MIN_ERROR)
#ifndef IPS_DEBUG
#define DEVICE_PRINTF(x...)
#define PRINTF(x...)
#else
#define DEVICE_PRINTF(level,x...) if(IPS_DEBUG >= level)device_printf(x)
#define PRINTF(level,x...) if(IPS_DEBUG >= level)printf(x)
#endif
/*
* IPS STRUCTS
*/
struct ips_softc;
typedef struct{
u_int8_t command;
u_int8_t id;
u_int8_t drivenum;
u_int8_t reserve2;
u_int32_t lba;
u_int32_t buffaddr;
u_int32_t reserve3;
} __attribute__ ((packed)) ips_generic_cmd;
typedef struct{
u_int8_t command;
u_int8_t id;
u_int8_t drivenum;
u_int8_t segnum;
u_int32_t lba;
u_int32_t buffaddr;
u_int16_t length;
u_int16_t reserve1;
} __attribute__ ((packed)) ips_io_cmd;
typedef struct{
u_int8_t command;
u_int8_t id;
u_int8_t pagenum;
u_int8_t rw;
u_int32_t reserve1;
u_int32_t buffaddr;
u_int32_t reserve3;
} __attribute__ ((packed)) ips_rw_nvram_cmd;
typedef struct{
u_int8_t command;
u_int8_t id;
u_int8_t drivenum;
u_int8_t reserve1;
u_int32_t reserve2;
u_int32_t buffaddr;
u_int32_t reserve3;
} __attribute__ ((packed)) ips_drive_cmd;
typedef struct{
u_int8_t command;
u_int8_t id;
u_int8_t reserve1;
u_int8_t commandtype;
u_int32_t reserve2;
u_int32_t buffaddr;
u_int32_t reserve3;
} __attribute__((packed)) ips_adapter_info_cmd;
typedef union{
ips_generic_cmd generic_cmd;
ips_drive_cmd drive_cmd;
ips_adapter_info_cmd adapter_info_cmd;
} ips_cmd_buff_t;
typedef struct {
u_int32_t signature;
u_int8_t reserved;
u_int8_t adapter_slot;
u_int16_t adapter_type;
u_int8_t bios_high[4];
u_int8_t bios_low[4];
u_int16_t reserve2;
u_int8_t reserve3;
u_int8_t operating_system;
u_int8_t driver_high[4];
u_int8_t driver_low[4];
u_int8_t reserve4[100];
}__attribute__((packed)) ips_nvram_page5;
typedef struct{
u_int32_t addr;
u_int32_t len;
} ips_sg_element_t;
typedef struct{
u_int8_t drivenum;
u_int8_t merge_id;
u_int8_t raid_lvl;
u_int8_t state;
u_int32_t sector_count;
} __attribute__((packed)) ips_drive_t;
typedef struct{
u_int8_t drivecount;
u_int8_t reserve1;
u_int16_t reserve2;
ips_drive_t drives[IPS_MAX_NUM_DRIVES];
}__attribute__((packed)) ips_drive_info_t;
typedef struct{
u_int8_t drivecount;
u_int8_t miscflags;
u_int8_t SLTflags;
u_int8_t BSTflags;
u_int8_t pwr_chg_count;
u_int8_t wrong_addr_count;
u_int8_t unident_count;
u_int8_t nvram_dev_chg_count;
u_int8_t codeblock_version[8];
u_int8_t bootblock_version[8];
u_int32_t drive_sector_count[IPS_MAX_NUM_DRIVES];
u_int8_t max_concurrent_cmds;
u_int8_t max_phys_devices;
u_int16_t flash_prog_count;
u_int8_t defunct_disks;
u_int8_t rebuildflags;
u_int8_t offline_drivecount;
u_int8_t critical_drivecount;
u_int16_t config_update_count;
u_int8_t blockedflags;
u_int8_t psdn_error;
u_int16_t addr_dead_disk[4*16];/* ugly, max # channels * max # scsi devices per channel */
}__attribute__((packed)) ips_adapter_info_t;
typedef struct {
u_int32_t status[IPS_MAX_CMD_NUM];
u_int32_t base_phys_addr;
int nextstatus;
bus_dma_tag_t dmatag;
bus_dmamap_t dmamap;
} ips_copper_queue_t;
typedef union {
struct {
u_int8_t reserved;
u_int8_t command_id;
u_int8_t basic_status;
u_int8_t extended_status;
} fields;
volatile u_int32_t value;
} ips_cmd_status_t;
/* used to keep track of current commands to the card */
typedef struct ips_command{
u_int8_t command_number;
u_int8_t id;
u_int8_t timeout;
struct ips_softc * sc;
bus_dmamap_t command_dmamap;
void * command_buffer;
u_int32_t command_phys_addr;/*WARNING! must be changed if 64bit addressing ever used*/
ips_cmd_status_t status;
SLIST_ENTRY(ips_command) next;
bus_dma_tag_t data_dmatag;
bus_dmamap_t data_dmamap;
void * data_buffer;
void * arg;
void (* callback)(struct ips_command *command);
}ips_command_t;
typedef struct ips_wait_list{
STAILQ_ENTRY(ips_wait_list) next;
void *data;
int (* callback)(ips_command_t *command);
}ips_wait_list_t;
typedef struct ips_softc{
struct resource * iores;
struct resource * irqres;
int state;
int iotype;
int rid;
int irqrid;
void * irqcookie;
bus_space_tag_t bustag;
bus_space_handle_t bushandle;
bus_dma_tag_t adapter_dmatag;
bus_dma_tag_t command_dmatag;
bus_dma_tag_t sg_dmatag;
device_t dev;
dev_t device_file;
struct callout_handle timer;
ips_adapter_info_t adapter_info;
device_t diskdev[IPS_MAX_NUM_DRIVES];
ips_drive_t drives[IPS_MAX_NUM_DRIVES];
u_int8_t drivecount;
u_int8_t next_drive;
u_int8_t max_cmds;
volatile u_int8_t used_commands;
ips_command_t commandarray[IPS_MAX_CMD_NUM];
SLIST_HEAD(command_list, ips_command) free_cmd_list;
STAILQ_HEAD(command_wait_list,ips_wait_list) cmd_wait_list;
int (* ips_adapter_reinit)(struct ips_softc *sc,
int force);
void (* ips_adapter_intr)(void *sc);
void (* ips_issue_cmd)(ips_command_t *command);
ips_copper_queue_t * copper_queue;
struct mtx cmd_mtx;
}ips_softc_t;
/* function defines from ips_ioctl.c */
extern int ips_ioctl_request(ips_softc_t *sc, u_long ioctl_cmd, caddr_t addr,
int32_t flags);
/* function defines from ips_disk.c */
extern void ipsd_finish(struct bio *iobuf);
/* function defines from ips_commands.c */
extern int ips_flush_cache(ips_softc_t *sc);
extern void ips_start_io_request(ips_softc_t *sc, struct bio *iobuf);
extern int ips_get_drive_info(ips_softc_t *sc);
extern int ips_get_adapter_info(ips_softc_t *sc);
extern int ips_update_nvram(ips_softc_t *sc);
extern int ips_clear_adapter(ips_softc_t *sc);
/* function defines from ips.c */
extern int ips_get_free_cmd(ips_softc_t *sc, int (*callback)(ips_command_t *),
void *data, unsigned long flags);
extern void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command);
extern int ips_adapter_init(ips_softc_t *sc);
extern int ips_morpheus_reinit(ips_softc_t *sc, int force);
extern int ips_adapter_free(ips_softc_t *sc);
extern void ips_morpheus_intr(void *sc);
extern void ips_issue_morpheus_cmd(ips_command_t *command);
extern int ips_copperhead_reinit(ips_softc_t *sc, int force);
extern void ips_copperhead_intr(void *sc);
extern void ips_issue_copperhead_cmd(ips_command_t *command);

636
sys/dev/ips/ips_commands.c Normal file
View File

@ -0,0 +1,636 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <dev/ips/ips.h>
/*
* This is an interrupt callback. It is called from
* interrupt context when the adapter has completed the
* command. This very generic callback simply stores
* the command's return value in command->arg and wake's
* up anyone waiting on the command.
*/
static void ips_wakeup_callback(ips_command_t *command)
{
ips_cmd_status_t *status;
status = command->arg;
status->value = command->status.value;
bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_POSTWRITE);
mtx_lock(&command->sc->cmd_mtx);
wakeup(status);
mtx_unlock(&command->sc->cmd_mtx);
}
/* Below are a series of functions for sending an IO request
* to the adapter. The flow order is: start, send, callback, finish.
* The caller must have already assembled an iorequest struct to hold
* the details of the IO request. */
static void ips_io_request_finish(ips_command_t *command)
{
struct bio *iobuf = command->arg;
if(ips_read_request(iobuf)) {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTREAD);
} else {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTWRITE);
}
bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
if(COMMAND_ERROR(&command->status)){
iobuf->bio_flags |=BIO_ERROR;
iobuf->bio_error = EIO;
}
ips_insert_free_cmd(command->sc, command);
ipsd_finish(iobuf);
}
static void ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_softc_t *sc;
ips_command_t *command = cmdptr;
ips_sg_element_t *sg_list;
ips_io_cmd *command_struct;
struct bio *iobuf = command->arg;
int i, length = 0;
u_int8_t cmdtype;
sc = command->sc;
if(error){
printf("ips: error = %d in ips_sg_request_callback\n", error);
bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
iobuf->bio_flags |= BIO_ERROR;
iobuf->bio_error = ENOMEM;
ips_insert_free_cmd(sc, command);
ipsd_finish(iobuf);
return;
}
command_struct = (ips_io_cmd *)command->command_buffer;
command_struct->id = command->id;
command_struct->drivenum = (uint32_t)iobuf->bio_driver1;
if(segnum != 1){
if(ips_read_request(iobuf))
cmdtype = IPS_SG_READ_CMD;
else
cmdtype = IPS_SG_WRITE_CMD;
command_struct->segnum = segnum;
sg_list = (ips_sg_element_t *)((u_int8_t *)
command->command_buffer + IPS_COMMAND_LEN);
for(i = 0; i < segnum; i++){
sg_list[i].addr = segments[i].ds_addr;
sg_list[i].len = segments[i].ds_len;
length += segments[i].ds_len;
}
command_struct->buffaddr =
(u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN;
} else {
if(ips_read_request(iobuf))
cmdtype = IPS_READ_CMD;
else
cmdtype = IPS_WRITE_CMD;
command_struct->buffaddr = segments[0].ds_addr;
length = segments[0].ds_len;
}
command_struct->command = cmdtype;
command_struct->lba = iobuf->bio_pblkno;
length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE;
command_struct->length = length;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
if(ips_read_request(iobuf)) {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_PREREAD);
} else {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_PREWRITE);
}
PRINTF(10, "ips test: command id: %d segments: %d blkno: %lld "
"pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
iobuf->bio_blkno, iobuf->bio_pblkno,
length, segments[0].ds_len);
sc->ips_issue_cmd(command);
return;
}
static int ips_send_io_request(ips_command_t *command)
{
ips_softc_t *sc = command->sc;
struct bio *iobuf = command->arg;
command->data_dmatag = sc->sg_dmatag;
if(bus_dmamap_create(command->data_dmatag, 0, &command->data_dmamap)){
device_printf(sc->dev, "dmamap failed\n");
iobuf->bio_flags |= BIO_ERROR;
iobuf->bio_error = ENOMEM;
ips_insert_free_cmd(sc, command);
ipsd_finish(iobuf);
return 0;
}
command->callback = ips_io_request_finish;
PRINTF(10, "ips test: : bcount %ld\n", iobuf->bio_bcount);
bus_dmamap_load(command->data_dmatag, command->data_dmamap,
iobuf->bio_data, iobuf->bio_bcount,
ips_io_request_callback, command, 0);
return 0;
}
void ips_start_io_request(ips_softc_t *sc, struct bio *iobuf)
{
if(ips_get_free_cmd(sc, ips_send_io_request, iobuf, 0)){
device_printf(sc->dev, "no mem for command slots!\n");
iobuf->bio_flags |= BIO_ERROR;
iobuf->bio_error = ENOMEM;
ipsd_finish(iobuf);
return;
}
return;
}
/* Below are a series of functions for sending an adapter info request
* to the adapter. The flow order is: get, send, callback. It uses
* the generic finish callback at the top of this file.
* This can be used to get configuration/status info from the card */
static void ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_softc_t *sc;
ips_command_t *command = cmdptr;
ips_adapter_info_cmd *command_struct;
sc = command->sc;
if(error){
ips_cmd_status_t * status = command->arg;
status->value = IPS_ERROR_STATUS; /* a lovely error value */
ips_insert_free_cmd(sc, command);
printf("ips: error = %d in ips_get_adapter_info\n", error);
return;
}
command_struct = (ips_adapter_info_cmd *)command->command_buffer;
command_struct->command = IPS_ADAPTER_INFO_CMD;
command_struct->id = command->id;
command_struct->buffaddr = segments[0].ds_addr;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_PREREAD);
sc->ips_issue_cmd(command);
}
static int ips_send_adapter_info_cmd(ips_command_t *command)
{
int error = 0;
ips_softc_t *sc = command->sc;
ips_cmd_status_t *status = command->arg;
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ IPS_ADAPTER_INFO_LEN,
/* numsegs */ 1,
/* maxsegsize*/ IPS_ADAPTER_INFO_LEN,
/* flags */ 0,
&command->data_dmatag) != 0) {
printf("ips: can't alloc dma tag for adapter status\n");
error = ENOMEM;
goto exit;
}
if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
BUS_DMA_NOWAIT, &command->data_dmamap)){
error = ENOMEM;
goto exit;
}
command->callback = ips_wakeup_callback;
mtx_lock(&sc->cmd_mtx);
bus_dmamap_load(command->data_dmatag, command->data_dmamap,
command->data_buffer,IPS_ADAPTER_INFO_LEN,
ips_adapter_info_callback, command, BUS_DMA_NOWAIT);
if ((status->value == IPS_ERROR_STATUS) ||
(msleep(status, &sc->cmd_mtx, 0, "ips", 30*hz) == EWOULDBLOCK))
error = ETIMEDOUT;
mtx_unlock(&sc->cmd_mtx);
if (error == 0) {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTREAD);
memcpy(&(sc->adapter_info), command->data_buffer,
IPS_ADAPTER_INFO_LEN);
}
bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
exit:
/* I suppose I should clean up my memory allocations */
bus_dmamem_free(command->data_dmatag, command->data_buffer,
command->data_dmamap);
bus_dma_tag_destroy(command->data_dmatag);
ips_insert_free_cmd(sc, command);
return error;
}
int ips_get_adapter_info(ips_softc_t *sc)
{
int error = 0;
ips_cmd_status_t *status;
status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT);
if(!status)
return ENOMEM;
if(ips_get_free_cmd(sc, ips_send_adapter_info_cmd, status,
IPS_NOWAIT_FLAG) > 0){
device_printf(sc->dev, "unable to get adapter configuration\n");
free(status, M_DEVBUF);
return ENXIO;
}
if(COMMAND_ERROR(status)){
error = ENXIO;
}
free(status, M_DEVBUF);
return error;
}
/* Below are a series of functions for sending a drive info request
* to the adapter. The flow order is: get, send, callback. It uses
* the generic finish callback at the top of this file.
* This can be used to get drive status info from the card */
static void ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_softc_t *sc;
ips_command_t *command = cmdptr;
ips_drive_cmd *command_struct;
sc = command->sc;
if(error){
ips_cmd_status_t * status = command->arg;
status->value = IPS_ERROR_STATUS;
ips_insert_free_cmd(sc, command);
printf("ips: error = %d in ips_get_drive_info\n", error);
return;
}
command_struct = (ips_drive_cmd *)command->command_buffer;
command_struct->command = IPS_DRIVE_INFO_CMD;
command_struct->id = command->id;
command_struct->buffaddr = segments[0].ds_addr;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_PREREAD);
sc->ips_issue_cmd(command);
}
static int ips_send_drive_info_cmd(ips_command_t *command)
{
int error = 0;
ips_softc_t *sc = command->sc;
ips_cmd_status_t *status = command->arg;
ips_drive_info_t *driveinfo;
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ IPS_DRIVE_INFO_LEN,
/* numsegs */ 1,
/* maxsegsize*/ IPS_DRIVE_INFO_LEN,
/* flags */ 0,
&command->data_dmatag) != 0) {
printf("ips: can't alloc dma tag for drive status\n");
error = ENOMEM;
goto exit;
}
if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
BUS_DMA_NOWAIT, &command->data_dmamap)){
error = ENOMEM;
goto exit;
}
command->callback = ips_wakeup_callback;
mtx_lock(&sc->cmd_mtx);
bus_dmamap_load(command->data_dmatag, command->data_dmamap,
command->data_buffer,IPS_DRIVE_INFO_LEN,
ips_drive_info_callback, command, BUS_DMA_NOWAIT);
if ((status->value == IPS_ERROR_STATUS) ||
(msleep(status, &sc->cmd_mtx, 0, "ips", 10*hz) == EWOULDBLOCK))
error = ETIMEDOUT;
mtx_unlock(&sc->cmd_mtx);
if (error == 0) {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTREAD);
driveinfo = command->data_buffer;
memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
sc->drivecount = driveinfo->drivecount;
device_printf(sc->dev, "logical drives: %d\n",sc->drivecount);
}
bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
exit:
/* I suppose I should clean up my memory allocations */
bus_dmamem_free(command->data_dmatag, command->data_buffer,
command->data_dmamap);
bus_dma_tag_destroy(command->data_dmatag);
ips_insert_free_cmd(sc, command);
return error;
}
int ips_get_drive_info(ips_softc_t *sc)
{
int error = 0;
ips_cmd_status_t *status;
status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT);
if(!status)
return ENOMEM;
if(ips_get_free_cmd(sc, ips_send_drive_info_cmd, status,
IPS_NOWAIT_FLAG) > 0){
free(status, M_DEVBUF);
device_printf(sc->dev, "unable to get drive configuration\n");
return ENXIO;
}
if(COMMAND_ERROR(status)){
error = ENXIO;
}
free(status, M_DEVBUF);
return error;
}
/* Below is a pair of functions for making sure data is safely
* on disk by flushing the adapter's cache. */
static int ips_send_flush_cache_cmd(ips_command_t *command)
{
ips_softc_t *sc = command->sc;
ips_cmd_status_t *status = command->arg;
ips_generic_cmd *command_struct;
PRINTF(10,"ips test: got a command, building flush command\n");
command->callback = ips_wakeup_callback;
command_struct = (ips_generic_cmd *)command->command_buffer;
command_struct->command = IPS_CACHE_FLUSH_CMD;
command_struct->id = command->id;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
mtx_lock(&sc->cmd_mtx);
sc->ips_issue_cmd(command);
if (status->value != IPS_ERROR_STATUS)
msleep(status, &sc->cmd_mtx, 0, "flush2", 0);
mtx_unlock(&sc->cmd_mtx);
ips_insert_free_cmd(sc, command);
return 0;
}
int ips_flush_cache(ips_softc_t *sc)
{
ips_cmd_status_t *status;
status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT);
if(!status)
return ENOMEM;
device_printf(sc->dev, "flushing cache\n");
if(ips_get_free_cmd(sc, ips_send_flush_cache_cmd, status,
IPS_NOWAIT_FLAG)){
free(status, M_DEVBUF);
device_printf(sc->dev, "ERROR: unable to get a command! can't flush cache!\n");
}
if(COMMAND_ERROR(status)){
device_printf(sc->dev, "ERROR: cache flush command failed!\n");
}
free(status, M_DEVBUF);
return 0;
}
static void ips_write_nvram(ips_command_t *command){
ips_softc_t *sc = command->sc;
ips_rw_nvram_cmd *command_struct;
ips_nvram_page5 *nvram;
/*FIXME check for error */
command->callback = ips_wakeup_callback;
command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
command_struct->command = IPS_RW_NVRAM_CMD;
command_struct->id = command->id;
command_struct->pagenum = 5;
command_struct->rw = 1; /*write*/
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTREAD);
nvram = command->data_buffer;
strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
nvram->operating_system = IPS_OS_FREEBSD;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
sc->ips_issue_cmd(command);
}
static void ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_softc_t *sc;
ips_command_t *command = cmdptr;
ips_rw_nvram_cmd *command_struct;
sc = command->sc;
if(error){
ips_cmd_status_t * status = command->arg;
status->value = IPS_ERROR_STATUS;
ips_insert_free_cmd(sc, command);
printf("ips: error = %d in ips_read_nvram_callback\n", error);
return;
}
command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
command_struct->command = IPS_RW_NVRAM_CMD;
command_struct->id = command->id;
command_struct->pagenum = 5;
command_struct->rw = 0;
command_struct->buffaddr = segments[0].ds_addr;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_PREREAD);
sc->ips_issue_cmd(command);
}
static int ips_read_nvram(ips_command_t *command){
int error = 0;
ips_softc_t *sc = command->sc;
ips_cmd_status_t *status = command->arg;
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ IPS_NVRAM_PAGE_SIZE,
/* numsegs */ 1,
/* maxsegsize*/ IPS_NVRAM_PAGE_SIZE,
/* flags */ 0,
&command->data_dmatag) != 0) {
printf("ips: can't alloc dma tag for nvram\n");
error = ENOMEM;
goto exit;
}
if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
BUS_DMA_NOWAIT, &command->data_dmamap)){
error = ENOMEM;
goto exit;
}
command->callback = ips_write_nvram;
mtx_lock(&sc->cmd_mtx);
bus_dmamap_load(command->data_dmatag, command->data_dmamap,
command->data_buffer,IPS_NVRAM_PAGE_SIZE,
ips_read_nvram_callback, command, BUS_DMA_NOWAIT);
if ((status->value == IPS_ERROR_STATUS) ||
(msleep(status, &sc->cmd_mtx, 0, "ips", 0) == EWOULDBLOCK))
error = ETIMEDOUT;
mtx_unlock(&sc->cmd_mtx);
if (error == 0) {
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTWRITE);
}
bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
exit:
bus_dmamem_free(command->data_dmatag, command->data_buffer,
command->data_dmamap);
bus_dma_tag_destroy(command->data_dmatag);
ips_insert_free_cmd(sc, command);
return error;
}
int ips_update_nvram(ips_softc_t *sc)
{
ips_cmd_status_t *status;
status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT);
if(!status)
return ENOMEM;
if(ips_get_free_cmd(sc, ips_read_nvram, status, IPS_NOWAIT_FLAG)){
free(status, M_DEVBUF);
device_printf(sc->dev, "ERROR: unable to get a command! can't update nvram\n");
return 1;
}
if(COMMAND_ERROR(status)){
device_printf(sc->dev, "ERROR: nvram update command failed!\n");
}
free(status, M_DEVBUF);
return 0;
}
static int ips_send_config_sync_cmd(ips_command_t *command)
{
ips_softc_t *sc = command->sc;
ips_cmd_status_t *status = command->arg;
ips_generic_cmd *command_struct;
PRINTF(10,"ips test: got a command, building flush command\n");
command->callback = ips_wakeup_callback;
command_struct = (ips_generic_cmd *)command->command_buffer;
command_struct->command = IPS_CONFIG_SYNC_CMD;
command_struct->id = command->id;
command_struct->reserve2 = IPS_POCL;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
mtx_lock(&sc->cmd_mtx);
sc->ips_issue_cmd(command);
if (status->value != IPS_ERROR_STATUS)
msleep(status, &sc->cmd_mtx, 0, "ipssyn", 0);
mtx_unlock(&sc->cmd_mtx);
ips_insert_free_cmd(sc, command);
return 0;
}
static int ips_send_error_table_cmd(ips_command_t *command)
{
ips_softc_t *sc = command->sc;
ips_cmd_status_t *status = command->arg;
ips_generic_cmd *command_struct;
PRINTF(10,"ips test: got a command, building errortable command\n");
command->callback = ips_wakeup_callback;
command_struct = (ips_generic_cmd *)command->command_buffer;
command_struct->command = IPS_ERROR_TABLE_CMD;
command_struct->id = command->id;
command_struct->reserve2 = IPS_CSL;
bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
mtx_lock(&sc->cmd_mtx);
sc->ips_issue_cmd(command);
if (status->value != IPS_ERROR_STATUS)
msleep(status, &sc->cmd_mtx, 0, "ipsetc", 0);
mtx_unlock(&sc->cmd_mtx);
ips_insert_free_cmd(sc, command);
return 0;
}
int ips_clear_adapter(ips_softc_t *sc)
{
ips_cmd_status_t *status;
status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT);
if(!status)
return ENOMEM;
device_printf(sc->dev, "syncing config\n");
if(ips_get_free_cmd(sc, ips_send_config_sync_cmd, status,
IPS_NOWAIT_FLAG)){
free(status, M_DEVBUF);
device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n");
return 1;
}
if(COMMAND_ERROR(status)){
free(status, M_DEVBUF);
device_printf(sc->dev, "ERROR: cache sync command failed!\n");
return 1;
}
device_printf(sc->dev, "clearing error table\n");
if(ips_get_free_cmd(sc, ips_send_error_table_cmd, status,
IPS_NOWAIT_FLAG)){
free(status, M_DEVBUF);
device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n");
return 1;
}
if(COMMAND_ERROR(status)){
device_printf(sc->dev, "ERROR: etable command failed!\n");
free(status, M_DEVBUF);
return 1;
}
free(status, M_DEVBUF);
return 0;
}

162
sys/dev/ips/ips_disk.c Normal file
View File

@ -0,0 +1,162 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <dev/ips/ips.h>
#include <dev/ips/ips_disk.h>
#include <sys/stat.h>
static int ipsd_probe(device_t dev);
static int ipsd_attach(device_t dev);
static int ipsd_detach(device_t dev);
static disk_open_t ipsd_open;
static disk_close_t ipsd_close;
static disk_strategy_t ipsd_strategy;
static device_method_t ipsd_methods[] = {
DEVMETHOD(device_probe, ipsd_probe),
DEVMETHOD(device_attach, ipsd_attach),
DEVMETHOD(device_detach, ipsd_detach),
{ 0, 0 }
};
static driver_t ipsd_driver = {
"ipsd",
ipsd_methods,
sizeof(ipsdisk_softc_t)
};
static devclass_t ipsd_devclass;
DRIVER_MODULE(ipsd, ips, ipsd_driver, ipsd_devclass, 0, 0);
/* handle opening of disk device. It must set up all
information about the geometry and size of the disk */
static int ipsd_open(struct disk *dp)
{
ipsdisk_softc_t *dsc = dp->d_drv1;
dsc->state |= IPS_DEV_OPEN;
DEVICE_PRINTF(2, dsc->dev, "I'm open\n");
return 0;
}
static int ipsd_close(struct disk *dp)
{
ipsdisk_softc_t *dsc = dp->d_drv1;
dsc->state &= ~IPS_DEV_OPEN;
DEVICE_PRINTF(2, dsc->dev, "I'm closed for the day\n");
return 0;
}
/* ipsd_finish is called to clean up and return a completed IO request */
void ipsd_finish(struct bio *iobuf)
{
if (iobuf->bio_flags & BIO_ERROR) {
ipsdisk_softc_t *dsc;
dsc = iobuf->bio_disk->d_drv1;
device_printf(dsc->dev, "iobuf error %d\n", iobuf->bio_error);
} else
iobuf->bio_resid = 0;
biodone(iobuf);
}
static void ipsd_strategy(struct bio *iobuf)
{
ipsdisk_softc_t *dsc;
dsc = iobuf->bio_disk->d_drv1;
DEVICE_PRINTF(8,dsc->dev,"in strategy\n");
(uint32_t)iobuf->bio_driver1 = dsc->sc->drives[dsc->disk_number].drivenum;
ips_start_io_request(dsc->sc, iobuf);
}
static int ipsd_probe(device_t dev)
{
DEVICE_PRINTF(2,dev, "in probe\n");
device_set_desc(dev, "Logical Drive");
return 0;
}
static int ipsd_attach(device_t dev)
{
device_t adapter;
ipsdisk_softc_t *dsc;
u_int totalsectors;
DEVICE_PRINTF(2,dev, "in attach\n");
dsc = (ipsdisk_softc_t *)device_get_softc(dev);
bzero(dsc, sizeof(ipsdisk_softc_t));
adapter = device_get_parent(dev);
dsc->dev = dev;
dsc->sc = device_get_softc(adapter);
dsc->unit = device_get_unit(dev);
dsc->disk_number = (int) device_get_ivars(dev);
dsc->ipsd_disk.d_drv1 = dsc;
dsc->ipsd_disk.d_name = "ipsd";
dsc->ipsd_disk.d_maxsize = IPS_MAX_IO_SIZE;
dsc->ipsd_disk.d_open = ipsd_open;
dsc->ipsd_disk.d_close = ipsd_close;
dsc->ipsd_disk.d_strategy = ipsd_strategy;
totalsectors = dsc->sc->drives[dsc->disk_number].sector_count;
if ((totalsectors > 0x400000) &&
((dsc->sc->adapter_info.miscflags & 0x8) == 0)) {
dsc->ipsd_disk.d_fwheads = IPS_NORM_HEADS;
dsc->ipsd_disk.d_fwsectors = IPS_NORM_SECTORS;
} else {
dsc->ipsd_disk.d_fwheads = IPS_COMP_HEADS;
dsc->ipsd_disk.d_fwsectors = IPS_COMP_SECTORS;
}
dsc->ipsd_disk.d_sectorsize = IPS_BLKSIZE;
dsc->ipsd_disk.d_mediasize = totalsectors * IPS_BLKSIZE;
disk_create(dsc->unit, &dsc->ipsd_disk, 0, NULL, NULL);
device_printf(dev, "Logical Drive (%dMB)\n",
dsc->sc->drives[dsc->disk_number].sector_count >> 11);
return 0;
}
static int ipsd_detach(device_t dev)
{
ipsdisk_softc_t *dsc;
DEVICE_PRINTF(2, dev,"in detach\n");
dsc = (ipsdisk_softc_t *)device_get_softc(dev);
if(dsc->state & IPS_DEV_OPEN)
return (EBUSY);
disk_destroy(&dsc->ipsd_disk);
return 0;
}

66
sys/dev/ips/ips_disk.h Normal file
View File

@ -0,0 +1,66 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/disk.h>
#include <sys/bio.h>
#include <sys/disk.h>
#include <geom/geom_disk.h>
#include <machine/bus_memio.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#define IPS_MAX_IO_SIZE 0x10000
#define IPS_COMP_HEADS 128
#define IPS_COMP_SECTORS 32
#define IPS_NORM_HEADS 254
#define IPS_NORM_SECTORS 63
typedef struct ipsdisk_softc {
device_t dev;
int unit;
int disk_number;
u_int32_t state;
struct disk ipsd_disk;
ips_softc_t *sc;
}ipsdisk_softc_t;

157
sys/dev/ips/ips_ioctl.c Normal file
View File

@ -0,0 +1,157 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <dev/ips/ips.h>
#include <dev/ips/ips_ioctl.h>
static void ips_ioctl_finish(ips_command_t *command)
{
ips_ioctl_t *ioctl_cmd = command->arg;
if(ioctl_cmd->readwrite & IPS_IOCTL_READ){
bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
BUS_DMASYNC_POSTREAD);
} else if(ioctl_cmd->readwrite & IPS_IOCTL_WRITE){
bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
BUS_DMASYNC_POSTWRITE);
}
bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(ioctl_cmd->dmatag, ioctl_cmd->dmamap);
ioctl_cmd->status.value = command->status.value;
ips_insert_free_cmd(command->sc, command);
}
static void ips_ioctl_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
{
ips_command_t *command = cmdptr;
ips_ioctl_t *ioctl_cmd = command->arg;
ips_generic_cmd *command_buffer = command->command_buffer;
if(error){
ioctl_cmd->status.value = IPS_ERROR_STATUS;
ips_insert_free_cmd(command->sc, command);
return;
}
command_buffer->id = command->id;
command_buffer->buffaddr = segments[0].ds_addr;
if(ioctl_cmd->readwrite & IPS_IOCTL_WRITE){
bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
BUS_DMASYNC_PREWRITE);
} else if(ioctl_cmd->readwrite & IPS_IOCTL_READ){
bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
BUS_DMASYNC_PREREAD);
}
bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
BUS_DMASYNC_PREWRITE);
command->sc->ips_issue_cmd(command);
}
static int ips_ioctl_start(ips_command_t *command)
{
ips_ioctl_t *ioctl_cmd = command->arg;
memcpy(command->command_buffer, ioctl_cmd->command_buffer,
sizeof(ips_generic_cmd));
command->callback = ips_ioctl_finish;
bus_dmamap_load(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
ioctl_cmd->data_buffer,ioctl_cmd->datasize,
ips_ioctl_callback, command, 0);
return 0;
}
static int ips_ioctl_cmd(ips_softc_t *sc, ips_ioctl_t *ioctl_cmd, ips_user_request *user_request)
{
int error = EINVAL;
if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag,
/* alignment */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ ioctl_cmd->datasize,
/* numsegs */ 1,
/* maxsegsize*/ ioctl_cmd->datasize,
/* flags */ 0,
&ioctl_cmd->dmatag) != 0) {
return ENOMEM;
}
if(bus_dmamem_alloc(ioctl_cmd->dmatag, &ioctl_cmd->data_buffer,
0, &ioctl_cmd->dmamap)){
error = ENOMEM;
goto exit;
}
if(copyin(user_request->data_buffer,ioctl_cmd->data_buffer,
ioctl_cmd->datasize))
goto exit;
ioctl_cmd->status.value = 0xffffffff;
if((error = ips_get_free_cmd(sc, ips_ioctl_start, ioctl_cmd,0)) > 0){
error = ENOMEM;
goto exit;
}
while( ioctl_cmd->status.value == 0xffffffff)
tsleep(ioctl_cmd, 0, "ips", hz/10);
if(COMMAND_ERROR(&ioctl_cmd->status))
error = EIO;
else
error = 0;
if(copyout(ioctl_cmd->data_buffer, user_request->data_buffer,
ioctl_cmd->datasize))
error = EINVAL;
exit: bus_dmamem_free(ioctl_cmd->dmatag, ioctl_cmd->data_buffer,
ioctl_cmd->dmamap);
bus_dma_tag_destroy(ioctl_cmd->dmatag);
return error;
}
int ips_ioctl_request(ips_softc_t *sc, u_long ioctl_request, caddr_t addr, int32_t flags){
int error = EINVAL;
ips_ioctl_t *ioctl_cmd;
ips_user_request *user_request;
switch(ioctl_request){
case IPS_USER_CMD:
user_request = (ips_user_request *)addr;
ioctl_cmd = malloc(sizeof(ips_ioctl_t), M_DEVBUF, M_WAITOK);
ioctl_cmd->command_buffer = malloc(sizeof(ips_generic_cmd),
M_DEVBUF, M_WAITOK);
if(copyin(user_request->command_buffer,
ioctl_cmd->command_buffer, sizeof(ips_generic_cmd))){
free(ioctl_cmd->command_buffer, M_DEVBUF);
free(ioctl_cmd, M_DEVBUF);
break;
}
ioctl_cmd->readwrite = IPS_IOCTL_READ | IPS_IOCTL_WRITE;
ioctl_cmd->datasize = IPS_IOCTL_BUFFER_SIZE;
error = ips_ioctl_cmd(sc, ioctl_cmd, user_request);
free(ioctl_cmd->command_buffer, M_DEVBUF);
free(ioctl_cmd, M_DEVBUF);
break;
}
return error;
}

61
sys/dev/ips/ips_ioctl.h Normal file
View File

@ -0,0 +1,61 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <sys/ioccom.h>
#define IPS_USER_CMD _IOWR(0x81,0x21,ips_user_request)
#define IPS_IOCTL_READ 1
#define IPS_IOCTL_WRITE 2
#define IPS_REBUILD_STAT_SIZE 116
#define IPS_SUBSYS_PARAM_SIZE 128
#define IPS_RW_NVRAM_SIZE 128
#define IPS_IOCTL_BUFFER_SIZE 4096
typedef struct ips_ioctl{
ips_generic_cmd * command_buffer;
void * data_buffer;
ips_cmd_status_t status;
int datasize;
int readwrite;
bus_dma_tag_t dmatag;
bus_dmamap_t dmamap;
}ips_ioctl_t;
typedef struct ips_user_request{
void * command_buffer;
void * data_buffer;
u_int32_t status;
}ips_user_request;

182
sys/dev/ips/ips_pci.c Normal file
View File

@ -0,0 +1,182 @@
/*-
* Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Written by: David Jeffery
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <dev/ips/ips.h>
static int ips_pci_free(ips_softc_t *sc);
static int ips_pci_probe(device_t dev)
{
if ((pci_get_vendor(dev) == IPS_VENDOR_ID) &&
(pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID)) {
device_set_desc(dev, "IBM ServeRAID Adapter");
return 0;
} else if ((pci_get_vendor(dev) == IPS_VENDOR_ID) &&
(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID)) {
device_set_desc(dev, "IBM ServeRAID Adapter");
return (0);
}
return(ENXIO);
}
static int ips_pci_attach(device_t dev)
{
u_int32_t command;
ips_softc_t *sc;
DEVICE_PRINTF(1, dev, "in attach.\n");
sc = (ips_softc_t *)device_get_softc(dev);
if(!sc){
printf("how is sc NULL?!\n");
return (ENXIO);
}
bzero(sc, sizeof(ips_softc_t));
sc->dev = dev;
if(pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID){
sc->ips_adapter_reinit = ips_morpheus_reinit;
sc->ips_adapter_intr = ips_morpheus_intr;
sc->ips_issue_cmd = ips_issue_morpheus_cmd;
} else if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID){
sc->ips_adapter_reinit = ips_copperhead_reinit;
sc->ips_adapter_intr = ips_copperhead_intr;
sc->ips_issue_cmd = ips_issue_copperhead_cmd;
} else
goto error;
/* make sure busmastering is on */
command = pci_read_config(dev, PCIR_COMMAND, 1);
command |= PCIM_CMD_BUSMASTEREN;
pci_write_config(dev, PCIR_COMMAND, command, 1);
/* seting up io space */
sc->iores = NULL;
if(command & PCIM_CMD_MEMEN){
PRINTF(10, "trying MEMIO\n");
if(pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID)
sc->rid = PCIR_MAPS;
else
sc->rid = PCIR_MAPS + 4;
sc->iotype = SYS_RES_MEMORY;
sc->iores = bus_alloc_resource(dev, sc->iotype, &sc->rid, 0, ~0, 1, RF_ACTIVE);
}
if(!sc->iores && command & PCIM_CMD_PORTEN){
PRINTF(10, "trying PORTIO\n");
sc->rid = PCIR_MAPS;
sc->iotype = SYS_RES_IOPORT;
sc->iores = bus_alloc_resource(dev, sc->iotype, &sc->rid, 0, ~0, 1, RF_ACTIVE);
}
if(sc->iores == NULL){
device_printf(dev, "resource allocation failed\n");
return (ENXIO);
}
sc->bustag = rman_get_bustag(sc->iores);
sc->bushandle = rman_get_bushandle(sc->iores);
/*allocate an interrupt. when does the irq become active? after leaving attach? */
sc->irqrid = 0;
if(!(sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqrid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE))){
device_printf(dev, "irq allocation failed\n");
goto error;
}
if(bus_setup_intr(dev, sc->irqres, INTR_TYPE_BIO, sc->ips_adapter_intr, sc, &sc->irqcookie)){
device_printf(dev, "irq setup failed\n");
goto error;
}
if (bus_dma_tag_create( /* parent */ NULL,
/* alignemnt */ 1,
/* boundary */ 0,
/* lowaddr */ BUS_SPACE_MAXADDR_32BIT,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
/* filterarg */ NULL,
/* maxsize */ BUS_SPACE_MAXSIZE_32BIT,
/* numsegs */ IPS_MAX_SG_ELEMENTS,
/* maxsegsize*/ BUS_SPACE_MAXSIZE_32BIT,
/* flags */ 0,
&sc->adapter_dmatag) != 0) {
printf("IPS can't alloc dma tag\n");
goto error;
}
if(ips_adapter_init(sc))
goto error;
return 0;
error:
ips_pci_free(sc);
return (ENXIO);
}
static int ips_pci_free(ips_softc_t *sc)
{
if(sc->adapter_dmatag)
bus_dma_tag_destroy(sc->adapter_dmatag);
if(sc->irqcookie)
bus_teardown_intr(sc->dev, sc->irqres, sc->irqcookie);
if(sc->irqres)
bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqrid, sc->irqres);
if(sc->iores)
bus_release_resource(sc->dev, sc->iotype, sc->rid, sc->iores);
return 0;
}
static int ips_pci_detach(device_t dev)
{
ips_softc_t *sc;
DEVICE_PRINTF(1, dev, "detaching ServeRaid\n");
sc = (ips_softc_t *) device_get_softc(dev);
ips_flush_cache(sc);
if(ips_adapter_free(sc))
return EBUSY;
ips_pci_free(sc);
mtx_destroy(&sc->cmd_mtx);
return 0;
}
static int ips_pci_shutdown(device_t dev)
{
ips_softc_t *sc = (ips_softc_t *) device_get_softc(dev);
ips_flush_cache(sc);
return 0;
}
static device_method_t ips_driver_methods[] = {
DEVMETHOD(device_probe, ips_pci_probe),
DEVMETHOD(device_attach, ips_pci_attach),
DEVMETHOD(device_detach, ips_pci_detach),
DEVMETHOD(device_shutdown, ips_pci_shutdown),
{0,0}
};
static driver_t ips_pci_driver = {
"ips",
ips_driver_methods,
sizeof(ips_softc_t),
};
static devclass_t ips_devclass;
DRIVER_MODULE(ips, pci, ips_pci_driver, ips_devclass, 0, 0);

8
sys/modules/ips/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/ips
KMOD = ips
SRCS = ips.c ips_pci.c ips.h ips_disk.c ips_disk.h ips_commands.c \
ips_ioctl.h ips_ioctl.c device_if.h bus_if.h pci_if.h
.include <bsd.kmod.mk>