linux/sound/pci/asihpi/hpi6000.c
Jesper Juhl fd0977d0f4 ALSA: asihpi - Unsafe memory management when allocating control cache
I noticed that sound/pci/asihpi/hpicmn.c::hpi_alloc_control_cache() does
not check the return value from kmalloc(), which may fail.
If kmalloc() fails we'll dereference a null pointer and things will go bad
fast.
There are two memory allocations in that function and there's also the
problem that the first may succeed and the second may fail and nothing is
done about that either which will also go wrong down the line.

Signed-off-by: Jesper Juhl <jj@chaosbits.net>
Acked-by: Eliot Blennerhassett <linux@audioscience.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2010-11-02 07:38:21 +01:00

1848 lines
50 KiB
C

/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience ASI6200 series adapters.
These PCI bus adapters are based on the TI C6711 DSP.
Exported functions:
void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
#defines
HIDE_PCI_ASSERTS to show the PCI asserts
PROFILE_DSP2 get profile data from DSP2 if present (instead of DSP 1)
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
#define SOURCEFILE_NAME "hpi6000.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpi6000.h"
#include "hpidspcd.h"
#include "hpicmn.h"
#define HPI_HIF_BASE (0x00000200) /* start of C67xx internal RAM */
#define HPI_HIF_ADDR(member) \
(HPI_HIF_BASE + offsetof(struct hpi_hif_6000, member))
#define HPI_HIF_ERROR_MASK 0x4000
/* HPI6000 specific error codes */
#define HPI6000_ERROR_BASE 900
#define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901
#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK 902
#define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903
#define HPI6000_ERROR_MSG_GET_ADR 904
#define HPI6000_ERROR_RESP_GET_ADR 905
#define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32 906
#define HPI6000_ERROR_MSG_RESP_BLOCKREAD32 907
#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX 908
#define HPI6000_ERROR_CONTROL_CACHE_PARAMS 909
#define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT 911
#define HPI6000_ERROR_SEND_DATA_ACK 912
#define HPI6000_ERROR_SEND_DATA_ADR 913
#define HPI6000_ERROR_SEND_DATA_TIMEOUT 914
#define HPI6000_ERROR_SEND_DATA_CMD 915
#define HPI6000_ERROR_SEND_DATA_WRITE 916
#define HPI6000_ERROR_SEND_DATA_IDLECMD 917
#define HPI6000_ERROR_SEND_DATA_VERIFY 918
#define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT 921
#define HPI6000_ERROR_GET_DATA_ACK 922
#define HPI6000_ERROR_GET_DATA_CMD 923
#define HPI6000_ERROR_GET_DATA_READ 924
#define HPI6000_ERROR_GET_DATA_IDLECMD 925
#define HPI6000_ERROR_CONTROL_CACHE_ADDRLEN 951
#define HPI6000_ERROR_CONTROL_CACHE_READ 952
#define HPI6000_ERROR_CONTROL_CACHE_FLUSH 953
#define HPI6000_ERROR_MSG_RESP_GETRESPCMD 961
#define HPI6000_ERROR_MSG_RESP_IDLECMD 962
#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32 963
/* adapter init errors */
#define HPI6000_ERROR_UNHANDLED_SUBSYS_ID 930
/* can't access PCI2040 */
#define HPI6000_ERROR_INIT_PCI2040 931
/* can't access DSP HPI i/f */
#define HPI6000_ERROR_INIT_DSPHPI 932
/* can't access internal DSP memory */
#define HPI6000_ERROR_INIT_DSPINTMEM 933
/* can't access SDRAM - test#1 */
#define HPI6000_ERROR_INIT_SDRAM1 934
/* can't access SDRAM - test#2 */
#define HPI6000_ERROR_INIT_SDRAM2 935
#define HPI6000_ERROR_INIT_VERIFY 938
#define HPI6000_ERROR_INIT_NOACK 939
#define HPI6000_ERROR_INIT_PLDTEST1 941
#define HPI6000_ERROR_INIT_PLDTEST2 942
/* local defines */
#define HIDE_PCI_ASSERTS
#define PROFILE_DSP2
/* for PCI2040 i/f chip */
/* HPI CSR registers */
/* word offsets from CSR base */
/* use when io addresses defined as u32 * */
#define INTERRUPT_EVENT_SET 0
#define INTERRUPT_EVENT_CLEAR 1
#define INTERRUPT_MASK_SET 2
#define INTERRUPT_MASK_CLEAR 3
#define HPI_ERROR_REPORT 4
#define HPI_RESET 5
#define HPI_DATA_WIDTH 6
#define MAX_DSPS 2
/* HPI registers, spaced 8K bytes = 2K words apart */
#define DSP_SPACING 0x800
#define CONTROL 0x0000
#define ADDRESS 0x0200
#define DATA_AUTOINC 0x0400
#define DATA 0x0600
#define TIMEOUT 500000
struct dsp_obj {
__iomem u32 *prHPI_control;
__iomem u32 *prHPI_address;
__iomem u32 *prHPI_data;
__iomem u32 *prHPI_data_auto_inc;
char c_dsp_rev; /*A, B */
u32 control_cache_address_on_dsp;
u32 control_cache_length_on_dsp;
struct hpi_adapter_obj *pa_parent_adapter;
};
struct hpi_hw_obj {
__iomem u32 *dw2040_HPICSR;
__iomem u32 *dw2040_HPIDSP;
u16 num_dsp;
struct dsp_obj ado[MAX_DSPS];
u32 message_buffer_address_on_dsp;
u32 response_buffer_address_on_dsp;
u32 pCI2040HPI_error_count;
struct hpi_control_cache_single control_cache[HPI_NMIXER_CONTROLS];
struct hpi_control_cache *p_cache;
};
static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *source, u32 count);
static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *dest, u32 count);
static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
u16 read_or_write);
#define H6READ 1
#define H6WRITE 0
static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao,
struct hpi_message *phm);
static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr);
static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr);
static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index,
u32 ack_value);
static short hpi6000_send_host_command(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 host_cmd);
static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo);
static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr);
static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr);
static void hpi_write_word(struct dsp_obj *pdo, u32 address, u32 data);
static u32 hpi_read_word(struct dsp_obj *pdo, u32 address);
static void hpi_write_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length);
static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length);
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
static void subsys_delete_adapter(struct hpi_message *phm,
struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
/* local globals */
static u16 gw_pci_read_asserts; /* used to count PCI2040 errors */
static u16 gw_pci_write_asserts; /* used to count PCI2040 errors */
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_GET_INFO:
case HPI_SUBSYS_DRIVER_UNLOAD:
case HPI_SUBSYS_DRIVER_LOAD:
case HPI_SUBSYS_FIND_ADAPTERS:
/* messages that should not get here */
phr->error = HPI_ERROR_UNIMPLEMENTED;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
case HPI_SUBSYS_DELETE_ADAPTER:
subsys_delete_adapter(phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void control_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
if (pao->has_control_cache) {
u16 err;
err = hpi6000_update_control_cache(pao, phm);
if (err) {
phr->error = err;
break;
}
if (hpi_check_control_cache(((struct hpi_hw_obj *)
pao->priv)->p_cache, phm,
phr))
break;
}
hw_message(pao, phm, phr);
break;
case HPI_CONTROL_GET_INFO:
hw_message(pao, phm, phr);
break;
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)->
p_cache, phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_ADAPTER_GET_INFO:
hw_message(pao, phm, phr);
break;
case HPI_ADAPTER_GET_ASSERT:
adapter_get_asserts(pao, phm, phr);
break;
case HPI_ADAPTER_OPEN:
case HPI_ADAPTER_CLOSE:
case HPI_ADAPTER_TEST_ASSERT:
case HPI_ADAPTER_SELFTEST:
case HPI_ADAPTER_GET_MODE:
case HPI_ADAPTER_SET_MODE:
case HPI_ADAPTER_FIND_OBJECT:
case HPI_ADAPTER_GET_PROPERTY:
case HPI_ADAPTER_SET_PROPERTY:
case HPI_ADAPTER_ENUM_PROPERTY:
hw_message(pao, phm, phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
static void outstream_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_OSTREAM_HOSTBUFFER_ALLOC:
case HPI_OSTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
* they're called without allocating the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
phr->error = HPI_ERROR_INVALID_FUNC;
break;
default:
hw_message(pao, phm, phr);
return;
}
}
static void instream_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_ISTREAM_HOSTBUFFER_ALLOC:
case HPI_ISTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
* they're called without allocating the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
phr->error = HPI_ERROR_INVALID_FUNC;
break;
default:
hw_message(pao, phm, phr);
return;
}
}
/************************************************************************/
/** HPI_6000()
* Entry point from HPIMAN
* All calls to the HPI start here
*/
void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
/* subsytem messages get executed by every HPI. */
/* All other messages are ignored unless the adapter index matches */
/* an adapter in the HPI */
HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->object, phm->function);
/* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
HPI_DEBUG_LOG(DEBUG,
" %d,%d refused, for another HPI?\n",
phm->object, phm->function);
return;
}
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
phm->object, phm->function);
return;
}
}
/* Init default response including the size field */
if (phm->function != HPI_SUBSYS_CREATE_ADAPTER)
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_PROCESSING_MESSAGE);
switch (phm->type) {
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
break;
case HPI_OBJ_ADAPTER:
phr->size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_adapter_res);
adapter_message(pao, phm, phr);
break;
case HPI_OBJ_CONTROL:
control_message(pao, phm, phr);
break;
case HPI_OBJ_OSTREAM:
outstream_message(pao, phm, phr);
break;
case HPI_OBJ_ISTREAM:
instream_message(pao, phm, phr);
break;
default:
hw_message(pao, phm, phr);
break;
}
break;
default:
phr->error = HPI_ERROR_INVALID_TYPE;
break;
}
}
/************************************************************************/
/* SUBSYSTEM */
/* create an adapter object and initialise it based on resource information
* passed in in the message
* NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters
*/
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
/* create temp adapter obj, because we don't know what index yet */
struct hpi_adapter_obj ao;
struct hpi_adapter_obj *pao;
u32 os_error_code;
short error = 0;
u32 dsp_index = 0;
HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n");
memset(&ao, 0, sizeof(ao));
/* this HPI only creates adapters for TI/PCI2040 based devices */
if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
return;
if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
return;
if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040)
return;
ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
/* create the adapter object based on the resource information */
/*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */
ao.pci = *phm->u.s.resource.r.pci;
error = create_adapter_obj(&ao, &os_error_code);
if (!error)
error = hpi_add_adapter(&ao);
if (error) {
phr->u.s.data = os_error_code;
kfree(ao.priv);
phr->error = error;
return;
}
/* need to update paParentAdapter */
pao = hpi_find_adapter(ao.index);
if (!pao) {
/* We just added this adapter, why can't we find it!? */
HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n");
phr->error = 950;
return;
}
for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) {
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
phw->ado[dsp_index].pa_parent_adapter = pao;
}
phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
phr->u.s.adapter_index = ao.index;
phr->u.s.num_adapters++;
phr->error = 0;
}
static void subsys_delete_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
struct hpi_hw_obj *phw;
pao = hpi_find_adapter(phm->adapter_index);
if (!pao)
return;
phw = (struct hpi_hw_obj *)pao->priv;
if (pao->has_control_cache)
hpi_free_control_cache(phw->p_cache);
hpi_delete_adapter(pao);
kfree(phw);
phr->error = 0;
}
/* this routine is called from SubSysFindAdapter and SubSysCreateAdapter */
static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
short boot_error = 0;
u32 dsp_index = 0;
u32 control_cache_size = 0;
u32 control_cache_count = 0;
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
/* init error reporting */
pao->dsp_crashed = 0;
/* The PCI2040 has the following address map */
/* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */
/* BAR1 - 32K = HPI registers on DSP */
phw->dw2040_HPICSR = pao->pci.ap_mem_base[0];
phw->dw2040_HPIDSP = pao->pci.ap_mem_base[1];
HPI_DEBUG_LOG(VERBOSE, "csr %p, dsp %p\n", phw->dw2040_HPICSR,
phw->dw2040_HPIDSP);
/* set addresses for the possible DSP HPI interfaces */
for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) {
phw->ado[dsp_index].prHPI_control =
phw->dw2040_HPIDSP + (CONTROL +
DSP_SPACING * dsp_index);
phw->ado[dsp_index].prHPI_address =
phw->dw2040_HPIDSP + (ADDRESS +
DSP_SPACING * dsp_index);
phw->ado[dsp_index].prHPI_data =
phw->dw2040_HPIDSP + (DATA + DSP_SPACING * dsp_index);
phw->ado[dsp_index].prHPI_data_auto_inc =
phw->dw2040_HPIDSP + (DATA_AUTOINC +
DSP_SPACING * dsp_index);
HPI_DEBUG_LOG(VERBOSE, "ctl %p, adr %p, dat %p, dat++ %p\n",
phw->ado[dsp_index].prHPI_control,
phw->ado[dsp_index].prHPI_address,
phw->ado[dsp_index].prHPI_data,
phw->ado[dsp_index].prHPI_data_auto_inc);
phw->ado[dsp_index].pa_parent_adapter = pao;
}
phw->pCI2040HPI_error_count = 0;
pao->has_control_cache = 0;
/* Set the default number of DSPs on this card */
/* This is (conditionally) adjusted after bootloading */
/* of the first DSP in the bootload section. */
phw->num_dsp = 1;
boot_error = hpi6000_adapter_boot_load_dsp(pao, pos_error_code);
if (boot_error)
return boot_error;
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
phw->message_buffer_address_on_dsp = 0L;
phw->response_buffer_address_on_dsp = 0L;
/* get info about the adapter by asking the adapter */
/* send a HPI_ADAPTER_GET_INFO message */
{
struct hpi_message hM;
struct hpi_response hR0; /* response from DSP 0 */
struct hpi_response hR1; /* response from DSP 1 */
u16 error = 0;
HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n");
memset(&hM, 0, sizeof(hM));
hM.type = HPI_TYPE_MESSAGE;
hM.size = sizeof(struct hpi_message);
hM.object = HPI_OBJ_ADAPTER;
hM.function = HPI_ADAPTER_GET_INFO;
hM.adapter_index = 0;
memset(&hR0, 0, sizeof(hR0));
memset(&hR1, 0, sizeof(hR1));
hR0.size = sizeof(hR0);
hR1.size = sizeof(hR1);
error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0);
if (hR0.error) {
HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error);
return hR0.error;
}
if (phw->num_dsp == 2) {
error = hpi6000_message_response_sequence(pao, 1, &hM,
&hR1);
if (error)
return error;
}
pao->adapter_type = hR0.u.a.adapter_type;
pao->index = hR0.u.a.adapter_index;
}
memset(&phw->control_cache[0], 0,
sizeof(struct hpi_control_cache_single) *
HPI_NMIXER_CONTROLS);
/* Read the control cache length to figure out if it is turned on */
control_cache_size =
hpi_read_word(&phw->ado[0],
HPI_HIF_ADDR(control_cache_size_in_bytes));
if (control_cache_size) {
control_cache_count =
hpi_read_word(&phw->ado[0],
HPI_HIF_ADDR(control_cache_count));
pao->has_control_cache = 1;
phw->p_cache =
hpi_alloc_control_cache(control_cache_count,
control_cache_size, (struct hpi_control_cache_info *)
&phw->control_cache[0]
);
if (!phw->p_cache)
pao->has_control_cache = 0;
} else
pao->has_control_cache = 0;
HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n",
pao->adapter_type, pao->index);
pao->open = 0; /* upon creation the adapter is closed */
return 0;
}
/************************************************************************/
/* ADAPTER */
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
#ifndef HIDE_PCI_ASSERTS
/* if we have PCI2040 asserts then collect them */
if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) {
phr->u.a.serial_number =
gw_pci_read_asserts * 100 + gw_pci_write_asserts;
phr->u.a.adapter_index = 1; /* assert count */
phr->u.a.adapter_type = -1; /* "dsp index" */
strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error");
gw_pci_read_asserts = 0;
gw_pci_write_asserts = 0;
phr->error = 0;
} else
#endif
hw_message(pao, phm, phr); /*get DSP asserts */
return;
}
/************************************************************************/
/* LOW-LEVEL */
static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
short error;
u32 timeout;
u32 read = 0;
u32 i = 0;
u32 data = 0;
u32 j = 0;
u32 test_addr = 0x80000000;
u32 test_data = 0x00000001;
u32 dw2040_reset = 0;
u32 dsp_index = 0;
u32 endian = 0;
u32 adapter_info = 0;
u32 delay = 0;
struct dsp_code dsp_code;
u16 boot_load_family = 0;
/* NOTE don't use wAdapterType in this routine. It is not setup yet */
switch (pao->pci.subsys_device_id) {
case 0x5100:
case 0x5110: /* ASI5100 revB or higher with C6711D */
case 0x5200: /* ASI5200 PC_ie version of ASI5100 */
case 0x6100:
case 0x6200:
boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
break;
default:
return HPI6000_ERROR_UNHANDLED_SUBSYS_ID;
}
/* reset all DSPs, indicate two DSPs are present
* set RST3-=1 to disconnect HAD8 to set DSP in little endian mode
*/
endian = 0;
dw2040_reset = 0x0003000F;
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/* read back register to make sure PCI2040 chip is functioning
* note that bits 4..15 are read-only and so should always return zero,
* even though we wrote 1 to them
*/
for (i = 0; i < 1000; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
if (delay != dw2040_reset) {
HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset,
delay);
return HPI6000_ERROR_INIT_PCI2040;
}
/* Indicate that DSP#0,1 is a C6X */
iowrite32(0x00000003, phw->dw2040_HPICSR + HPI_DATA_WIDTH);
/* set Bit30 and 29 - which will prevent Target aborts from being
* issued upon HPI or GP error
*/
iowrite32(0x60000000, phw->dw2040_HPICSR + INTERRUPT_MASK_SET);
/* isolate DSP HAD8 line from PCI2040 so that
* Little endian can be set by pullup
*/
dw2040_reset = dw2040_reset & (~(endian << 3));
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
phw->ado[0].c_dsp_rev = 'B'; /* revB */
phw->ado[1].c_dsp_rev = 'B'; /* revB */
/*Take both DSPs out of reset, setting HAD8 to the correct Endian */
dw2040_reset = dw2040_reset & (~0x00000001); /* start DSP 0 */
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
dw2040_reset = dw2040_reset & (~0x00000002); /* start DSP 1 */
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/* set HAD8 back to PCI2040, now that DSP set to little endian mode */
dw2040_reset = dw2040_reset & (~0x00000008);
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/*delay to allow DSP to get going */
for (i = 0; i < 100; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
/* loop through all DSPs, downloading DSP code */
for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) {
struct dsp_obj *pdo = &phw->ado[dsp_index];
/* configure DSP so that we download code into the SRAM */
/* set control reg for little endian, HWOB=1 */
iowrite32(0x00010001, pdo->prHPI_control);
/* test access to the HPI address register (HPIA) */
test_data = 0x00000001;
for (j = 0; j < 32; j++) {
iowrite32(test_data, pdo->prHPI_address);
data = ioread32(pdo->prHPI_address);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR, "INIT_DSPHPI %x %x %x\n",
test_data, data, dsp_index);
return HPI6000_ERROR_INIT_DSPHPI;
}
test_data = test_data << 1;
}
/* if C6713 the setup PLL to generate 225MHz from 25MHz.
* Since the PLLDIV1 read is sometimes wrong, even on a C6713,
* we're going to do this unconditionally
*/
/* PLLDIV1 should have a value of 8000 after reset */
/*
if (HpiReadWord(pdo,0x01B7C118) == 0x8000)
*/
{
/* C6713 datasheet says we cannot program PLL from HPI,
* and indeed if we try to set the PLL multiply from the
* HPI, the PLL does not seem to lock,
* so we enable the PLL and use the default of x 7
*/
/* bypass PLL */
hpi_write_word(pdo, 0x01B7C100, 0x0000);
for (i = 0; i < 100; i++)
delay = ioread32(phw->dw2040_HPICSR +
HPI_RESET);
/* ** use default of PLL x7 ** */
/* EMIF = 225/3=75MHz */
hpi_write_word(pdo, 0x01B7C120, 0x8002);
/* peri = 225/2 */
hpi_write_word(pdo, 0x01B7C11C, 0x8001);
/* cpu = 225/1 */
hpi_write_word(pdo, 0x01B7C118, 0x8000);
/* ~200us delay */
for (i = 0; i < 2000; i++)
delay = ioread32(phw->dw2040_HPICSR +
HPI_RESET);
/* PLL not bypassed */
hpi_write_word(pdo, 0x01B7C100, 0x0001);
/* ~200us delay */
for (i = 0; i < 2000; i++)
delay = ioread32(phw->dw2040_HPICSR +
HPI_RESET);
}
/* test r/w to internal DSP memory
* C6711 has L2 cache mapped to 0x0 when reset
*
* revB - because of bug 3.0.1 last HPI read
* (before HPI address issued) must be non-autoinc
*/
/* test each bit in the 32bit word */
for (i = 0; i < 100; i++) {
test_addr = 0x00000000;
test_data = 0x00000001;
for (j = 0; j < 32; j++) {
hpi_write_word(pdo, test_addr + i, test_data);
data = hpi_read_word(pdo, test_addr + i);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR,
"DSP mem %x %x %x %x\n",
test_addr + i, test_data,
data, dsp_index);
return HPI6000_ERROR_INIT_DSPINTMEM;
}
test_data = test_data << 1;
}
}
/* memory map of ASI6200
00000000-0000FFFF 16Kx32 internal program
01800000-019FFFFF Internal peripheral
80000000-807FFFFF CE0 2Mx32 SDRAM running @ 100MHz
90000000-9000FFFF CE1 Async peripherals:
EMIF config
------------
Global EMIF control
0 -
1 -
2 -
3 CLK2EN = 1 CLKOUT2 enabled
4 CLK1EN = 0 CLKOUT1 disabled
5 EKEN = 1 <--!! C6713 specific, enables ECLKOUT
6 -
7 NOHOLD = 1 external HOLD disabled
8 HOLDA = 0 HOLDA output is low
9 HOLD = 0 HOLD input is low
10 ARDY = 1 ARDY input is high
11 BUSREQ = 0 BUSREQ output is low
12,13 Reserved = 1
*/
hpi_write_word(pdo, 0x01800000, 0x34A8);
/* EMIF CE0 setup - 2Mx32 Sync DRAM
31..28 Wr setup
27..22 Wr strobe
21..20 Wr hold
19..16 Rd setup
15..14 -
13..8 Rd strobe
7..4 MTYPE 0011 Sync DRAM 32bits
3 Wr hold MSB
2..0 Rd hold
*/
hpi_write_word(pdo, 0x01800008, 0x00000030);
/* EMIF SDRAM Extension
31-21 0
20 WR2RD = 0
19-18 WR2DEAC = 1
17 WR2WR = 0
16-15 R2WDQM = 2
14-12 RD2WR = 4
11-10 RD2DEAC = 1
9 RD2RD = 1
8-7 THZP = 10b
6-5 TWR = 2-1 = 01b (tWR = 10ns)
4 TRRD = 0b = 2 ECLK (tRRD = 14ns)
3-1 TRAS = 5-1 = 100b (Tras=42ns = 5 ECLK)
1 CAS latency = 3 ECLK
(for Micron 2M32-7 operating at 100Mhz)
*/
/* need to use this else DSP code crashes */
hpi_write_word(pdo, 0x01800020, 0x001BDF29);
/* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank)
31 - -
30 SDBSZ 1 4 bank
29..28 SDRSZ 00 11 row address pins
27..26 SDCSZ 01 8 column address pins
25 RFEN 1 refersh enabled
24 INIT 1 init SDRAM
23..20 TRCD 0001
19..16 TRP 0001
15..12 TRC 0110
11..0 - -
*/
/* need to use this else DSP code crashes */
hpi_write_word(pdo, 0x01800018, 0x47117000);
/* EMIF SDRAM Refresh Timing */
hpi_write_word(pdo, 0x0180001C, 0x00000410);
/*MIF CE1 setup - Async peripherals
@100MHz bus speed, each cycle is 10ns,
31..28 Wr setup = 1
27..22 Wr strobe = 3 30ns
21..20 Wr hold = 1
19..16 Rd setup =1
15..14 Ta = 2
13..8 Rd strobe = 3 30ns
7..4 MTYPE 0010 Async 32bits
3 Wr hold MSB =0
2..0 Rd hold = 1
*/
{
u32 cE1 =
(1L << 28) | (3L << 22) | (1L << 20) | (1L <<
16) | (2L << 14) | (3L << 8) | (2L << 4) | 1L;
hpi_write_word(pdo, 0x01800004, cE1);
}
/* delay a little to allow SDRAM and DSP to "get going" */
for (i = 0; i < 1000; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
/* test access to SDRAM */
{
test_addr = 0x80000000;
test_data = 0x00000001;
/* test each bit in the 32bit word */
for (j = 0; j < 32; j++) {
hpi_write_word(pdo, test_addr, test_data);
data = hpi_read_word(pdo, test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR,
"DSP dram %x %x %x %x\n",
test_addr, test_data, data,
dsp_index);
return HPI6000_ERROR_INIT_SDRAM1;
}
test_data = test_data << 1;
}
/* test every Nth address in the DRAM */
#define DRAM_SIZE_WORDS 0x200000 /*2_mx32 */
#define DRAM_INC 1024
test_addr = 0x80000000;
test_data = 0x0;
for (i = 0; i < DRAM_SIZE_WORDS; i = i + DRAM_INC) {
hpi_write_word(pdo, test_addr + i, test_data);
test_data++;
}
test_addr = 0x80000000;
test_data = 0x0;
for (i = 0; i < DRAM_SIZE_WORDS; i = i + DRAM_INC) {
data = hpi_read_word(pdo, test_addr + i);
if (data != test_data) {
HPI_DEBUG_LOG(ERROR,
"DSP dram %x %x %x %x\n",
test_addr + i, test_data,
data, dsp_index);
return HPI6000_ERROR_INIT_SDRAM2;
}
test_data++;
}
}
/* write the DSP code down into the DSPs memory */
/*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */
dsp_code.ps_dev = pao->pci.p_os_data;
error = hpi_dsp_code_open(boot_load_family, &dsp_code,
pos_error_code);
if (error)
return error;
while (1) {
u32 length;
u32 address;
u32 type;
u32 *pcode;
error = hpi_dsp_code_read_word(&dsp_code, &length);
if (error)
break;
if (length == 0xFFFFFFFF)
break; /* end of code */
error = hpi_dsp_code_read_word(&dsp_code, &address);
if (error)
break;
error = hpi_dsp_code_read_word(&dsp_code, &type);
if (error)
break;
error = hpi_dsp_code_read_block(length, &dsp_code,
&pcode);
if (error)
break;
error = hpi6000_dsp_block_write32(pao, (u16)dsp_index,
address, pcode, length);
if (error)
break;
}
if (error) {
hpi_dsp_code_close(&dsp_code);
return error;
}
/* verify that code was written correctly */
/* this time through, assume no errors in DSP code file/array */
hpi_dsp_code_rewind(&dsp_code);
while (1) {
u32 length;
u32 address;
u32 type;
u32 *pcode;
hpi_dsp_code_read_word(&dsp_code, &length);
if (length == 0xFFFFFFFF)
break; /* end of code */
hpi_dsp_code_read_word(&dsp_code, &address);
hpi_dsp_code_read_word(&dsp_code, &type);
hpi_dsp_code_read_block(length, &dsp_code, &pcode);
for (i = 0; i < length; i++) {
data = hpi_read_word(pdo, address);
if (data != *pcode) {
error = HPI6000_ERROR_INIT_VERIFY;
HPI_DEBUG_LOG(ERROR,
"DSP verify %x %x %x %x\n",
address, *pcode, data,
dsp_index);
break;
}
pcode++;
address += 4;
}
if (error)
break;
}
hpi_dsp_code_close(&dsp_code);
if (error)
return error;
/* zero out the hostmailbox */
{
u32 address = HPI_HIF_ADDR(host_cmd);
for (i = 0; i < 4; i++) {
hpi_write_word(pdo, address, 0);
address += 4;
}
}
/* write the DSP number into the hostmailbox */
/* structure before starting the DSP */
hpi_write_word(pdo, HPI_HIF_ADDR(dsp_number), dsp_index);
/* write the DSP adapter Info into the */
/* hostmailbox before starting the DSP */
if (dsp_index > 0)
hpi_write_word(pdo, HPI_HIF_ADDR(adapter_info),
adapter_info);
/* step 3. Start code by sending interrupt */
iowrite32(0x00030003, pdo->prHPI_control);
for (i = 0; i < 10000; i++)
delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
/* wait for a non-zero value in hostcmd -
* indicating initialization is complete
*
* Init could take a while if DSP checks SDRAM memory
* Was 200000. Increased to 2000000 for ASI8801 so we
* don't get 938 errors.
*/
timeout = 2000000;
while (timeout) {
do {
read = hpi_read_word(pdo,
HPI_HIF_ADDR(host_cmd));
} while (--timeout
&& hpi6000_check_PCI2040_error_flag(pao,
H6READ));
if (read)
break;
/* The following is a workaround for bug #94:
* Bluescreen on install and subsequent boots on a
* DELL PowerEdge 600SC PC with 1.8GHz P4 and
* ServerWorks chipset. Without this delay the system
* locks up with a bluescreen (NOT GPF or pagefault).
*/
else
hpios_delay_micro_seconds(1000);
}
if (timeout == 0)
return HPI6000_ERROR_INIT_NOACK;
/* read the DSP adapter Info from the */
/* hostmailbox structure after starting the DSP */
if (dsp_index == 0) {
/*u32 dwTestData=0; */
u32 mask = 0;
adapter_info =
hpi_read_word(pdo,
HPI_HIF_ADDR(adapter_info));
if (HPI_ADAPTER_FAMILY_ASI
(HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER
(adapter_info)) ==
HPI_ADAPTER_FAMILY_ASI(0x6200))
/* all 6200 cards have this many DSPs */
phw->num_dsp = 2;
/* test that the PLD is programmed */
/* and we can read/write 24bits */
#define PLD_BASE_ADDRESS 0x90000000L /*for ASI6100/6200/8800 */
switch (boot_load_family) {
case HPI_ADAPTER_FAMILY_ASI(0x6200):
/* ASI6100/6200 has 24bit path to FPGA */
mask = 0xFFFFFF00L;
/* ASI5100 uses AX6 code, */
/* but has no PLD r/w register to test */
if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
subsys_device_id) ==
HPI_ADAPTER_FAMILY_ASI(0x5100))
mask = 0x00000000L;
/* ASI5200 uses AX6 code, */
/* but has no PLD r/w register to test */
if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
subsys_device_id) ==
HPI_ADAPTER_FAMILY_ASI(0x5200))
mask = 0x00000000L;
break;
case HPI_ADAPTER_FAMILY_ASI(0x8800):
/* ASI8800 has 16bit path to FPGA */
mask = 0xFFFF0000L;
break;
}
test_data = 0xAAAAAA00L & mask;
/* write to 24 bit Debug register (D31-D8) */
hpi_write_word(pdo, PLD_BASE_ADDRESS + 4L, test_data);
read = hpi_read_word(pdo,
PLD_BASE_ADDRESS + 4L) & mask;
if (read != test_data) {
HPI_DEBUG_LOG(ERROR, "PLD %x %x\n", test_data,
read);
return HPI6000_ERROR_INIT_PLDTEST1;
}
test_data = 0x55555500L & mask;
hpi_write_word(pdo, PLD_BASE_ADDRESS + 4L, test_data);
read = hpi_read_word(pdo,
PLD_BASE_ADDRESS + 4L) & mask;
if (read != test_data) {
HPI_DEBUG_LOG(ERROR, "PLD %x %x\n", test_data,
read);
return HPI6000_ERROR_INIT_PLDTEST2;
}
}
} /* for numDSP */
return 0;
}
#define PCI_TIMEOUT 100
static int hpi_set_address(struct dsp_obj *pdo, u32 address)
{
u32 timeout = PCI_TIMEOUT;
do {
iowrite32(address, pdo->prHPI_address);
} while (hpi6000_check_PCI2040_error_flag(pdo->pa_parent_adapter,
H6WRITE)
&& --timeout);
if (timeout)
return 0;
return 1;
}
/* write one word to the HPI port */
static void hpi_write_word(struct dsp_obj *pdo, u32 address, u32 data)
{
if (hpi_set_address(pdo, address))
return;
iowrite32(data, pdo->prHPI_data);
}
/* read one word from the HPI port */
static u32 hpi_read_word(struct dsp_obj *pdo, u32 address)
{
u32 data = 0;
if (hpi_set_address(pdo, address))
return 0; /*? no way to return error */
/* take care of errata in revB DSP (2.0.1) */
data = ioread32(pdo->prHPI_data);
return data;
}
/* write a block of 32bit words to the DSP HPI port using auto-inc mode */
static void hpi_write_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length)
{
u16 length16 = length - 1;
if (length == 0)
return;
if (hpi_set_address(pdo, address))
return;
iowrite32_rep(pdo->prHPI_data_auto_inc, pdata, length16);
/* take care of errata in revB DSP (2.0.1) */
/* must end with non auto-inc */
iowrite32(*(pdata + length - 1), pdo->prHPI_data);
}
/** read a block of 32bit words from the DSP HPI port using auto-inc mode
*/
static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
u32 length)
{
u16 length16 = length - 1;
if (length == 0)
return;
if (hpi_set_address(pdo, address))
return;
ioread32_rep(pdo->prHPI_data_auto_inc, pdata, length16);
/* take care of errata in revB DSP (2.0.1) */
/* must end with non auto-inc */
*(pdata + length - 1) = ioread32(pdo->prHPI_data);
}
static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *source, u32 count)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 128;
u32 local_hpi_address = hpi_address;
int local_count = count;
int xfer_size;
u32 *pdata = source;
while (local_count) {
if (local_count > c6711_burst_size)
xfer_size = c6711_burst_size;
else
xfer_size = local_count;
time_out = PCI_TIMEOUT;
do {
hpi_write_block(pdo, local_hpi_address, pdata,
xfer_size);
} while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE)
&& --time_out);
if (!time_out)
break;
pdata += xfer_size;
local_hpi_address += sizeof(u32) * xfer_size;
local_count -= xfer_size;
}
if (time_out)
return 0;
else
return 1;
}
static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *dest, u32 count)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 16;
u32 local_hpi_address = hpi_address;
int local_count = count;
int xfer_size;
u32 *pdata = dest;
u32 loop_count = 0;
while (local_count) {
if (local_count > c6711_burst_size)
xfer_size = c6711_burst_size;
else
xfer_size = local_count;
time_out = PCI_TIMEOUT;
do {
hpi_read_block(pdo, local_hpi_address, pdata,
xfer_size);
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --time_out);
if (!time_out)
break;
pdata += xfer_size;
local_hpi_address += sizeof(u32) * xfer_size;
local_count -= xfer_size;
loop_count++;
}
if (time_out)
return 0;
else
return 1;
}
static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u16 ack;
u32 address;
u32 length;
u32 *p_data;
u16 error = 0;
/* does the DSP we are referencing exist? */
if (dsp_index >= phw->num_dsp)
return HPI6000_ERROR_MSG_INVALID_DSP_INDEX;
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK) {
pao->dsp_crashed++;
return HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT;
}
pao->dsp_crashed = 0;
/* send the message */
/* get the address and size */
if (phw->message_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
address =
hpi_read_word(pdo,
HPI_HIF_ADDR(message_buffer_address));
phw->message_buffer_address_on_dsp = address;
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --timeout);
if (!timeout)
return HPI6000_ERROR_MSG_GET_ADR;
} else
address = phw->message_buffer_address_on_dsp;
/* dwLength = sizeof(struct hpi_message); */
length = phm->size;
/* send it */
p_data = (u32 *)phm;
if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data,
(u16)length / 4))
return HPI6000_ERROR_MSG_RESP_BLOCKWRITE32;
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_GET_RESP))
return HPI6000_ERROR_MSG_RESP_GETRESPCMD;
hpi6000_send_dsp_interrupt(pdo);
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_GET_RESP);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK;
/* get the address and size */
if (phw->response_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
address =
hpi_read_word(pdo,
HPI_HIF_ADDR(response_buffer_address));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --timeout);
phw->response_buffer_address_on_dsp = address;
if (!timeout)
return HPI6000_ERROR_RESP_GET_ADR;
} else
address = phw->response_buffer_address_on_dsp;
/* read the length of the response back from the DSP */
timeout = TIMEOUT;
do {
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout);
if (!timeout)
length = sizeof(struct hpi_response);
/* get it */
p_data = (u32 *)phr;
if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data,
(u16)length / 4))
return HPI6000_ERROR_MSG_RESP_BLOCKREAD32;
/* set i/f back to idle */
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE))
return HPI6000_ERROR_MSG_RESP_IDLECMD;
hpi6000_send_dsp_interrupt(pdo);
error = hpi_validate_response(phm, phr);
return error;
}
/* have to set up the below defines to match stuff in the MAP file */
#define MSG_ADDRESS (HPI_HIF_BASE+0x18)
#define MSG_LENGTH 11
#define RESP_ADDRESS (HPI_HIF_BASE+0x44)
#define RESP_LENGTH 16
#define QUEUE_START (HPI_HIF_BASE+0x88)
#define QUEUE_SIZE 0x8000
static short hpi6000_send_data_check_adr(u32 address, u32 length_in_dwords)
{
/*#define CHECKING // comment this line in to enable checking */
#ifdef CHECKING
if (address < (u32)MSG_ADDRESS)
return 0;
if (address > (u32)(QUEUE_START + QUEUE_SIZE))
return 0;
if ((address + (length_in_dwords << 2)) >
(u32)(QUEUE_START + QUEUE_SIZE))
return 0;
#else
(void)address;
(void)length_in_dwords;
return 1;
#endif
}
static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 data_sent = 0;
u16 ack;
u32 length, address;
u32 *p_data = (u32 *)phm->u.d.u.data.pb_data;
u16 time_out = 8;
(void)phr;
/* round dwDataSize down to nearest 4 bytes */
while ((data_sent < (phm->u.d.u.data.data_size & ~3L))
&& --time_out) {
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT;
if (hpi6000_send_host_command(pao, dsp_index,
HPI_HIF_SEND_DATA))
return HPI6000_ERROR_SEND_DATA_CMD;
hpi6000_send_dsp_interrupt(pdo);
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_SEND_DATA);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_SEND_DATA_ACK;
do {
/* get the address and size */
address = hpi_read_word(pdo, HPI_HIF_ADDR(address));
/* DSP returns number of DWORDS */
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ));
if (!hpi6000_send_data_check_adr(address, length))
return HPI6000_ERROR_SEND_DATA_ADR;
/* send the data. break data into 512 DWORD blocks (2K bytes)
* and send using block write. 2Kbytes is the max as this is the
* memory window given to the HPI data register by the PCI2040
*/
{
u32 len = length;
u32 blk_len = 512;
while (len) {
if (len < blk_len)
blk_len = len;
if (hpi6000_dsp_block_write32(pao, dsp_index,
address, p_data, blk_len))
return HPI6000_ERROR_SEND_DATA_WRITE;
address += blk_len * 4;
p_data += blk_len;
len -= blk_len;
}
}
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE))
return HPI6000_ERROR_SEND_DATA_IDLECMD;
hpi6000_send_dsp_interrupt(pdo);
data_sent += length * 4;
}
if (!time_out)
return HPI6000_ERROR_SEND_DATA_TIMEOUT;
return 0;
}
static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 data_got = 0;
u16 ack;
u32 length, address;
u32 *p_data = (u32 *)phm->u.d.u.data.pb_data;
(void)phr; /* this parameter not used! */
/* round dwDataSize down to nearest 4 bytes */
while (data_got < (phm->u.d.u.data.data_size & ~3L)) {
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT;
if (hpi6000_send_host_command(pao, dsp_index,
HPI_HIF_GET_DATA))
return HPI6000_ERROR_GET_DATA_CMD;
hpi6000_send_dsp_interrupt(pdo);
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_GET_DATA);
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_GET_DATA_ACK;
/* get the address and size */
do {
address = hpi_read_word(pdo, HPI_HIF_ADDR(address));
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ));
/* read the data */
{
u32 len = length;
u32 blk_len = 512;
while (len) {
if (len < blk_len)
blk_len = len;
if (hpi6000_dsp_block_read32(pao, dsp_index,
address, p_data, blk_len))
return HPI6000_ERROR_GET_DATA_READ;
address += blk_len * 4;
p_data += blk_len;
len -= blk_len;
}
}
if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE))
return HPI6000_ERROR_GET_DATA_IDLECMD;
hpi6000_send_dsp_interrupt(pdo);
data_got += length * 4;
}
return 0;
}
static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo)
{
iowrite32(0x00030003, pdo->prHPI_control); /* DSPINT */
}
static short hpi6000_send_host_command(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 host_cmd)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 timeout = TIMEOUT;
/* set command */
do {
hpi_write_word(pdo, HPI_HIF_ADDR(host_cmd), host_cmd);
/* flush the FIFO */
hpi_set_address(pdo, HPI_HIF_ADDR(host_cmd));
} while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE) && --timeout);
/* reset the interrupt bit */
iowrite32(0x00040004, pdo->prHPI_control);
if (timeout)
return 0;
else
return 1;
}
/* if the PCI2040 has recorded an HPI timeout, reset the error and return 1 */
static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
u16 read_or_write)
{
u32 hPI_error;
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
/* read the error bits from the PCI2040 */
hPI_error = ioread32(phw->dw2040_HPICSR + HPI_ERROR_REPORT);
if (hPI_error) {
/* reset the error flag */
iowrite32(0L, phw->dw2040_HPICSR + HPI_ERROR_REPORT);
phw->pCI2040HPI_error_count++;
if (read_or_write == 1)
gw_pci_read_asserts++; /************* inc global */
else
gw_pci_write_asserts++;
return 1;
} else
return 0;
}
static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index,
u32 ack_value)
{
struct dsp_obj *pdo =
&(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
u32 ack = 0L;
u32 timeout;
u32 hPIC = 0L;
/* wait for host interrupt to signal ack is ready */
timeout = TIMEOUT;
while (--timeout) {
hPIC = ioread32(pdo->prHPI_control);
if (hPIC & 0x04) /* 0x04 = HINT from DSP */
break;
}
if (timeout == 0)
return HPI_HIF_ERROR_MASK;
/* wait for dwAckValue */
timeout = TIMEOUT;
while (--timeout) {
/* read the ack mailbox */
ack = hpi_read_word(pdo, HPI_HIF_ADDR(dsp_ack));
if (ack == ack_value)
break;
if ((ack & HPI_HIF_ERROR_MASK)
&& !hpi6000_check_PCI2040_error_flag(pao, H6READ))
break;
/*for (i=0;i<1000;i++) */
/* dwPause=i+1; */
}
if (ack & HPI_HIF_ERROR_MASK)
/* indicates bad read from DSP -
typically 0xffffff is read for some reason */
ack = HPI_HIF_ERROR_MASK;
if (timeout == 0)
ack = HPI_HIF_ERROR_MASK;
return (short)ack;
}
static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao,
struct hpi_message *phm)
{
const u16 dsp_index = 0;
struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u32 cache_dirty_flag;
u16 err;
hpios_dsplock_lock(pao);
timeout = TIMEOUT;
do {
cache_dirty_flag =
hpi_read_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR(control_cache_is_dirty));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout);
if (!timeout) {
err = HPI6000_ERROR_CONTROL_CACHE_PARAMS;
goto unlock;
}
if (cache_dirty_flag) {
/* read the cached controls */
u32 address;
u32 length;
timeout = TIMEOUT;
if (pdo->control_cache_address_on_dsp == 0) {
do {
address =
hpi_read_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR(control_cache_address));
length = hpi_read_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR
(control_cache_size_in_bytes));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ)
&& --timeout);
if (!timeout) {
err = HPI6000_ERROR_CONTROL_CACHE_ADDRLEN;
goto unlock;
}
pdo->control_cache_address_on_dsp = address;
pdo->control_cache_length_on_dsp = length;
} else {
address = pdo->control_cache_address_on_dsp;
length = pdo->control_cache_length_on_dsp;
}
if (hpi6000_dsp_block_read32(pao, dsp_index, address,
(u32 *)&phw->control_cache[0],
length / sizeof(u32))) {
err = HPI6000_ERROR_CONTROL_CACHE_READ;
goto unlock;
}
do {
hpi_write_word((struct dsp_obj *)pdo,
HPI_HIF_ADDR(control_cache_is_dirty), 0);
/* flush the FIFO */
hpi_set_address(pdo, HPI_HIF_ADDR(host_cmd));
} while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE)
&& --timeout);
if (!timeout) {
err = HPI6000_ERROR_CONTROL_CACHE_FLUSH;
goto unlock;
}
}
err = 0;
unlock:
hpios_dsplock_unlock(pao);
return err;
}
/** Get dsp index for multi DSP adapters only */
static u16 get_dsp_index(struct hpi_adapter_obj *pao, struct hpi_message *phm)
{
u16 ret = 0;
switch (phm->object) {
case HPI_OBJ_ISTREAM:
if (phm->obj_index < 2)
ret = 1;
break;
case HPI_OBJ_PROFILE:
ret = phm->obj_index;
break;
default:
break;
}
return ret;
}
/** Complete transaction with DSP
Send message, get response, send or get stream data if any.
*/
static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr)
{
u16 error = 0;
u16 dsp_index = 0;
u16 num_dsp = ((struct hpi_hw_obj *)pao->priv)->num_dsp;
if (num_dsp < 2)
dsp_index = 0;
else {
dsp_index = get_dsp_index(pao, phm);
/* is this checked on the DSP anyway? */
if ((phm->function == HPI_ISTREAM_GROUP_ADD)
|| (phm->function == HPI_OSTREAM_GROUP_ADD)) {
struct hpi_message hm;
u16 add_index;
hm.obj_index = phm->u.d.u.stream.stream_index;
hm.object = phm->u.d.u.stream.object_type;
add_index = get_dsp_index(pao, &hm);
if (add_index != dsp_index) {
phr->error = HPI_ERROR_NO_INTERDSP_GROUPS;
return;
}
}
}
hpios_dsplock_lock(pao);
error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr);
/* maybe an error response */
if (error) {
/* something failed in the HPI/DSP interface */
phr->error = error;
/* just the header of the response is valid */
phr->size = sizeof(struct hpi_response_header);
goto err;
}
if (phr->error != 0) /* something failed in the DSP */
goto err;
switch (phm->function) {
case HPI_OSTREAM_WRITE:
case HPI_ISTREAM_ANC_WRITE:
error = hpi6000_send_data(pao, dsp_index, phm, phr);
break;
case HPI_ISTREAM_READ:
case HPI_OSTREAM_ANC_READ:
error = hpi6000_get_data(pao, dsp_index, phm, phr);
break;
case HPI_ADAPTER_GET_ASSERT:
phr->u.a.adapter_index = 0; /* dsp 0 default */
if (num_dsp == 2) {
if (!phr->u.a.adapter_type) {
/* no assert from dsp 0, check dsp 1 */
error = hpi6000_message_response_sequence(pao,
1, phm, phr);
phr->u.a.adapter_index = 1;
}
}
}
if (error)
phr->error = error;
err:
hpios_dsplock_unlock(pao);
return;
}