/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This 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 program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Module Name:
* commsup.c
*
* Abstract: Contain all routines that are required for FSA host/adapter
* communication.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/blkdev.h>
#include <scsi/scsi_host.h>
#include <asm/semaphore.h>
#include "aacraid.h"
/**
* fib_map_alloc - allocate the fib objects
* @dev: Adapter to allocate for
*
* Allocate and map the shared PCI space for the FIB blocks used to
* talk to the Adaptec firmware.
*/
static int fib_map_alloc(struct aac_dev *dev)
{
dprintk((KERN_INFO
"allocate hardware fibs pci_alloc_consistent(%p, %d * (%d + %d), %p)\n",
dev->pdev, dev->max_fib_size, dev->scsi_host_ptr->can_queue,
AAC_NUM_MGT_FIB, &dev->hw_fib_pa));
if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, dev->max_fib_size
* (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB),
&dev->hw_fib_pa))==NULL)
return -ENOMEM;
return 0;
}
/**
* fib_map_free - free the fib objects
* @dev: Adapter to free
*
* Free the PCI mappings and the memory allocated for FIB blocks
* on this adapter.
*/
void fib_map_free(struct aac_dev *dev)
{
pci_free_consistent(dev->pdev, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB), dev->hw_fib_va, dev->hw_fib_pa);
}
/**
* fib_setup - setup the fibs
* @dev: Adapter to set up
*
* Allocate the PCI space for the fibs, map it and then intialise the
* fib area, the unmapped fib data and also the free list
*/
int fib_setup(struct aac_dev * dev)
{
struct fib *fibptr;
struct hw_fib *hw_fib_va;
dma_addr_t hw_fib_pa;
int i;
while (((i = fib_map_alloc(dev)) == -ENOMEM)
&& (dev->scsi_host_ptr->can_queue > (64 - AAC_NUM_MGT_FIB))) {
dev->init->MaxIoCommands = cpu_to_le32((dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) >> 1);
dev->scsi_host_ptr->can_queue = le32_to_cpu(dev->init->MaxIoCommands) - AAC_NUM_MGT_FIB;
}
if (i<0)
return -ENOMEM;
hw_fib_va = dev->hw_fib_va;
hw_fib_pa = dev->hw_fib_pa;
memset(hw_fib_va, 0, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB));
/*
* Initialise the fibs
*/
for (i = 0, fibptr = &dev->fibs[i]; i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); i++, fibptr++)
{
fibptr->dev = dev;
fibptr->hw_fib = hw_fib_va;
fibptr->data = (void *) fibptr->hw_fib->data;
fibptr->next = fibptr+1; /* Forward chain the fibs */
init_MUTEX_LOCKED(&fibptr->event_wait);
spin_lock_init(&fibptr->event_lock);
hw_fib_va->header.XferState = cpu_to_le32(0xffffffff);
hw_fib_va->header.SenderSize = cpu_to_le16(dev->max_fib_size);
fibptr->hw_fib_pa = hw_fib_pa;
hw_fib_va = (struct hw_fib *)((unsigned char *)hw_fib_va + dev->max_fib_size);
hw_fib_pa = hw_fib_pa + dev->max_fib_size;
}
/*
* Add the fib chain to the free list
*/
dev->fibs[dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB - 1].next = NULL;
/*
* Enable this to debug out of queue space
*/
dev->free_fib = &dev->fibs[0];
return 0;
}
/**
* fib_alloc - allocate a fib
* @dev: Adapter to allocate the fib for
*
* Allocate a fib from the adapter fib pool. If the pool is empty we
* return NULL.
*/
struct fib * fib_alloc(struct aac_dev *dev)
{
struct fib * fibptr;
unsigned long flags;
spin_lock_irqsave(&dev->fib_lock, flags);
fibptr = dev->free_fib;
if(!fibptr){
spin_unlock_irqrestore(&dev->fib_lock, flags);
return fibptr;
}
dev->free_fib = fibptr->next;
spin_unlock_irqrestore(&dev->fib_lock, flags);
/*
* Set the proper node type code and node byte size
*/
fibptr->type = FSAFS_NTC_FIB_CONTEXT;
fibptr->size = sizeof(struct fib);
/*
* Null out fields that depend on being zero at the start of
* each I/O
*/
fibptr->hw_fib->header