first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

+199
View File
@@ -0,0 +1,199 @@
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <AssertMacros.h>
#include <platform/soc/hwclocks.h>
#include <platform/soc/pmgr.h>
#include <platform/memmap.h>
#include <platform.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <EmbeddedIOPProtocol.h>
#include "iop_sdio_protocol.h"
#include "iop_sdio_wrapper.h"
#define cache_op_size(buf_size) (((buf_size) + (CPU_CACHELINE_SIZE-1)) & ~(CPU_CACHELINE_SIZE-1))
static struct task_event sdio_message_event;
static int sdio_channel;
static void sdio_message_wakeup(void *arg);
static bool sdio_message_process(void);
static int iop_sdio_task(void *cfg);
IOP_FUNCTION(sdio, iop_sdio_task, 1024, SDIO_CONTROL_CHANNEL);
static int
iop_sdio_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
dprintf(DEBUG_SPEW, "++ SDIO task starting\n");
check(kIOPSDIO_COMMAND_SIZE == sizeof(IOPSDIO_Command));
/* establish the host communications channel */
event_init(&sdio_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "++ opening sdio channel\n");
sdio_channel = qwi_instantiate_channel(
"sdio message",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
sdio_message_wakeup,
NULL);
for (;;) {
dprintf(DEBUG_SPEW, "++ waiting for message on sdio channel\n");
while (sdio_message_process())
{
// eat all available messages
}
event_wait(&sdio_message_event);
}
return(0);
}
static void
sdio_message_wakeup(void *arg __unused)
{
event_signal(&sdio_message_event);
}
extern unsigned int do_lookahead;
extern void iopsdio_cacheTransferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC);
static bool
sdio_message_process(void)
{
uint32_t messageAddr;
dprintf(DEBUG_SPEW, "++ handling host message\n");
do_lookahead = 0;
/* look to see if there's an item waiting for us */
if (qwi_receive_item(sdio_channel, &messageAddr) == -1)
return(false);
dprintf(DEBUG_SPEW, "++ received sdio message\n");
/* find the command structure based on the message */
union IOPSDIOMessage *message = (union IOPSDIOMessage*)mem_static_map_cached(messageAddr);
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the message size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE,
(void *)message,
cache_op_size(sizeof(*message)));
switch (message->header.opcode) {
case kIOPSDIOOpcodeInit:
{
dprintf(DEBUG_INFO, "SDHC @ 0x%X: Init\n",
message->targetSDHC.basePhysicalAddr);
message->header.status = iopsdio_init(&message->targetSDHC, &message->initCmd);
break;
}
case kIOPSDIOOpcodeFree:
{
dprintf(DEBUG_INFO, "SDHC @ 0x%X: Free\n",
message->targetSDHC.basePhysicalAddr);
message->header.status = iopsdio_free(&message->targetSDHC, &message->freeCmd);
break;
}
case kIOPSDIOOpcodeReset:
{
dprintf(DEBUG_INFO, "SDHC @ 0x%X: Reset 0x%X\n",
message->targetSDHC.basePhysicalAddr, message->resetCmd.resetFlags);
message->header.status = iopsdio_reset(&message->targetSDHC, &message->resetCmd);
break;
}
case kIOPSDIOOpcodeSetBusParam:
{
/* dprintf(DEBUG_INFO, "SDHC @ 0x%X: clk = %+d, baseClk = %u, clkRate = %u Hz, width = %u, speed = %d\n",
message->targetSDHC.basePhysicalAddr,
message->setBusParamCmd.clockMode, message->setBusParamCmd.baseClockRateHz,
message->setBusParamCmd.clockRateHz, message->setBusParamCmd.busWidth,
message->setBusParamCmd.busSpeedMode);
*/
message->header.status = iopsdio_setBusConfig(&message->targetSDHC, &message->setBusParamCmd);
break;
}
case kIOPSDIOOpcodeSendCommand:
{
/* dprintf(DEBUG_INFO, "SDHC @ 0x%X: Send SDIO Cmd: index = %u, arg = 0x%X\n",
message->targetSDHC.basePhysicalAddr,
message->sendSDIOCmd.sdioCmdIndex, message->sendSDIOCmd.sdioCmdArgument);
*/
message->header.status = iopsdio_sendSDIOCmd(&message->targetSDHC, &message->commandCmd);
break;
}
case kIOPSDIOOpcodeTransferData:
{
/* dprintf(DEBUG_INFO, "SDHC @ 0x%X: Send SDIO DMA Cmd: index = %u, arg = 0x%X\n",
message->targetSDHC.basePhysicalAddr,
message->sdioDataCmd.sdioCmdIndex, message->sdioDataCmd.sdioCmdArgument);
*/
message->header.status = iopsdio_transferSDIOData(&message->targetSDHC, &message->transferCmd);
break;
}
case kIOPSDIOOpcodePing:
dprintf(DEBUG_CRITICAL, "SDHC: Ping received, IOKit <--> IOP Connection established\n");
message->header.status = kIOPSDIOStatusSuccess;
break;
default:
dprintf(DEBUG_CRITICAL, "SDHC @ 0x%X: ERROR: unrecognised sdio opcode 0x%x\n",
message->targetSDHC.basePhysicalAddr, message->header.opcode);
message->header.status = kIOPSDIOParameterInvalid;
break;
}
dprintf(DEBUG_SPEW, "++ done processing sdio message with status 0x%08x\n", message->header.status);
platform_cache_operation(CACHE_CLEAN,
(void *)message,
sizeof(union IOPSDIOMessage));
qwi_send_item(sdio_channel, messageAddr);
{
if (do_lookahead) {
iopsdio_cacheTransferSDIOData(&message->targetSDHC);
do_lookahead = 0;
}
}
dprintf(DEBUG_SPEW, "++ signaled completion of sdio message to host\n");
return(true);
}
@@ -0,0 +1,259 @@
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#ifndef _IOP_SDIO_PROTOCOL_H_
#define _IOP_SDIO_PROTOCOL_H_
#include <sys/types.h>
#ifdef PLATFORM_VARIANT_IOP
#include <sdiocommon/sdio_types.h>
#else
#include <IOKit/sdio/sdio_types.h>
#endif
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPSDIO_MESSAGE_SIZE (512)
typedef UInt32 IOPSDIO_opcode_t;
enum IOPSDIOOpcode
{
kIOPSDIOOpcodeUnknown = 0,
kIOPSDIOOpcodePing = 1,
kIOPSDIOOpcodeInit = 2,
kIOPSDIOOpcodeFree = 3,
kIOPSDIOOpcodeReset = 4,
kIOPSDIOOpcodeSetBusParam = 5,
kIOPSDIOOpcodeSendCommand = 6,
kIOPSDIOOpcodeTransferData = 7,
};
typedef UInt32 IOPSDIO_flags_t;
enum IOPSDIOFlags
{
kIOPSDIOFlagsNone = 0,
};
typedef UInt32 IOPSDIO_status_t;
enum IOPSDIOStatus
{
// IOP Messaging Errors
/** @brief Command succeeded. */
kIOPSDIOStatusSuccess = 0,
/** @brief Status is not yet known (e.g. message has not been sent or processed). */
kIOPSDIOStatusUnknown = 1,
/** @brief One of the IOP parameters was invalid. */
kIOPSDIOParameterInvalid = 2,
// Common IOReturn Values
// If the IOP Status is in the range [0x200 0x300) then it is a standard
// IOKit IOReturn value without the "iokit_common_err( )" bits set
// SDIOReturn_t Values
// If the IOP status is in the range [0x300 0x800) it is an SDIOReturn_t
// value. This value is the same as the base value of IOSDIOReturn in the
// IOKit SDIO family, without the "iokit_family_err(sub_iokit_sdio, )"
// bits set.
};
/* this must match <drivers/dma.h>::struct dma_segment */
struct IOPSDIO_dma_segment {
u_int32_t paddr;
u_int32_t length;
};
/** @brief Standard header on all IOP SDIO Commands. */
struct IOPSDIOHeader
{
IOPSDIO_opcode_t opcode;
IOPSDIO_flags_t flags;
IOPSDIO_status_t status;
};
/** @brief Info about the target SDIO Host Controller. */
struct IOPSDIOTargetSDHC
{
/** @brief The Base physical address of the SDIO block's register file. */
UInt32 basePhysicalAddr;
/** @brief Wake event. */
UInt32 dmaCompleteEventAddr;
};
// SDIO INIT
/** @brief Bus Parameter Command. */
struct IOPSDIOInitCmd
{
/** @brief The capabilities of the SDHC. */
UInt64 sdhcCapabilities;
/** @brief The Maximum current capabilities of the SDHC. */
UInt64 sdhcMaxCurrentCapabilities;
};
// SDIO FREE
/** @brief Bus Parameter Command. */
struct IOPSDIOFreeCmd
{
// Nothing at the moment
};
// SDIO RESET
/** @brief SDHC Reset Command. */
struct IOPSDIOResetCmd
{
/** @brief Reset flags. */
UInt32 resetFlags;
};
// BUS PARAMETERS
/** @brief Bus Parameter Command. */
struct IOPSDIOSetBusParamCmd
{
/** @brief Base clock rate input to the SD Block.
* If 0, the value from the SDHC capabilities register is used instead.
*/
UInt32 baseClockRateHz;
/** @brief New SDIO clock rate in Hz. No change if 0.
* On return, this is set the value that was actually programmed into the
* SDHC block.
*/
UInt32 clockRateHz;
/** @brief New SDIO bus width (1 or 4). No change if 0. */
UInt8 busWidth;
/** @brief 1 if the SDIO clock should be on, -1 if off, 0 to leave unchanged */
int clockMode;
/** @brief 2 for high speed mode, 1 for normal speed, 0 to leave unchanged. */
int busSpeedMode;
};
// SDIO COMMAND
/** @brief Send an SDIO Command using the command line.
* Note that the status of the command will be reflected by the iopsdio.status field.
*/
struct IOPSDIOCommandCmd
{
/** @brief The SDIO Command. */
struct SDIOCommand command;
/** @brief The command response. */
struct SDIOCommandResponse response;
};
// SDIO DATA COMMAND
/** @brief Send an SDIO Command with data (including the command line)
* Note that the status of the command will be reflected by the iopsdio.status field.
*/
struct IOPSDIOTransferCmd
{
/** @brief The SDIO Command. */
struct SDIOCommand command;
/** @brief The command response. */
struct SDIOCommandResponse response;
/** @brief The SDIO transfer parameters. */
struct SDIOTransfer transfer;
/** @brief Information about the memory segments involved in the transfer. */
struct SDIOMemorySegments memory;
/** @brief Physical segments for DMA engine. Variable Size. */
struct IOPSDIO_dma_segment segment[];
};
// IOP MESSAGES
/** @brief IOP SDIO Message. */
union IOPSDIOMessage
{
struct {
/** @brief IOP Header. */
struct IOPSDIOHeader header;
/** @brief Target SDIO block. */
struct IOPSDIOTargetSDHC targetSDHC;
/** @brief Command Data. */
union {
struct IOPSDIOInitCmd initCmd;
struct IOPSDIOFreeCmd freeCmd;
struct IOPSDIOResetCmd resetCmd;
struct IOPSDIOSetBusParamCmd setBusParamCmd;
struct IOPSDIOCommandCmd commandCmd;
struct IOPSDIOTransferCmd transferCmd;
};
};
UInt8 _pad[kIOPSDIO_MESSAGE_SIZE] SDIO_RESERVED;
};
union _IOPSDIO_sgl_sizing_tool
{
struct {
/** @brief IOP Header. */
struct IOPSDIOHeader header;
/** @brief Target SDIO block. */
struct IOPSDIOTargetSDHC targetSDHC;
/** @brief Command Data. */
union {
struct IOPSDIOTransferCmd transferCmd;
/* XXX add other sgl-using commands */
};
};
};
#define kIOPSDIO_MAX_SEGMENTS ((kIOPSDIO_MESSAGE_SIZE - sizeof(union _IOPSDIO_sgl_sizing_tool)) / sizeof(IOPSDIO_dma_segment))
#endif // _IOP_SDIO_PROTOCOL_H_
@@ -0,0 +1,667 @@
/*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include "iop_sdio_wrapper.h"
#include <sys/task.h>
#include <AssertMacros.h>
#include <sdiodrv_config.h>
#include <sdiodrv_command.h>
#include <sdiodrv_transfer.h>
#include <platform.h>
#include <platform/memmap.h>
#include "iop_sdio_protocol.h"
#include <sdiocommon/sdio_cmdfields.h>
#include "debug.h"
#define cache_op_size(buf_size) (((buf_size) + (CPU_CACHELINE_SIZE-1)) & ~(CPU_CACHELINE_SIZE-1))
#define MAGIC_WRITE 0x44555544
#define MAGIC_READ 0x55555555
#define MAX_SDIO_READ_LEN (32*1024) //agreement with AppleBCMWLAN driver that there wont be any read bigger than that
#define BCOM_SDIO_CHANNEL_OFFSET 5
#define BCOM_SDIO_NEXTLEN_OFFSET 6
#define BCOM_SDIO_DATAOFFSET_OFFSET 7
#define BCOM_SDIO_GLOM_CHANNEL 3
#define BCOM_SDIO_GLOM_FRAME_DESCRIPTOR 0x80
#define MAGIC_READ_PACKET_LEN_OFFSET 1
#define MAGIC_READ_ENABLE_OFFSET 2
#define MAGIC_READ_BUFFER_LENGTH_OFFSET 3
#define LOOKAHEAD_READ_PKT_SIZE (lookahead_read_pkt_size)
struct cacheCmd {
struct IOPSDIOTransferCmd transferCmd;
struct IOPSDIO_dma_segment segment;
};
struct cacheCmd cmd;
SDIOReturn_t cachedError = kSDIOReturnSuccess;
unsigned int do_lookahead = 0;
unsigned int lookahead_read_pkt_size = 12 * 1024; //default buffer, max 12K per packet
IOPSDIO_status_t
iopsdio_init(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOInitCmd *initCmd)
{
check(targetSDHC);
check(initCmd);
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
// Create DMA completion event.
struct task_event* dmaCompleteEvent = malloc(sizeof(struct task_event));
if(!dmaCompleteEvent) {
return kSDIOReturnNoMemory;
}
event_init(dmaCompleteEvent, EVENT_FLAG_AUTO_UNSIGNAL, false);
targetSDHC->dmaCompleteEventAddr = (UInt32)dmaCompleteEvent;
#if 0
/* 7256044 */
initCmd->sdhcCapabilities = sdhc->capabilities;
initCmd->sdhcMaxCurrentCapabilities = sdhc->maxCurrentCapabilities;
#else
initCmd->sdhcCapabilities = 0;
initCmd->sdhcMaxCurrentCapabilities = 0;
#endif
return kSDIOReturnSuccess;
}
IOPSDIO_status_t
iopsdio_free(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOFreeCmd *freeCmd)
{
check(targetSDHC);
check(freeCmd);
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
// Destroy the DMA completion event
free((void*)targetSDHC->dmaCompleteEventAddr);
targetSDHC->dmaCompleteEventAddr = 0;
return kSDIOReturnSuccess;
}
IOPSDIO_status_t
iopsdio_reset(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOResetCmd *resetCmd)
{
check(targetSDHC);
check(resetCmd);
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
SDIOReturn_t retval = sdiodrv_resetSDHC(sdhc, resetCmd->resetFlags);
if(kSDIOReturnSuccess != retval) {
return retval;
}
// restore interrupt state on reset
if(resetCmd->resetFlags & kSDHCResetAll) {
sdhc_enableCommandStatus(sdhc, true);
sdhc_enableTransferStatus(sdhc, true);
}
cachedError = retval;
return retval;
}
IOPSDIO_status_t
iopsdio_setBusConfig(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOSetBusParamCmd *busParamCmd)
{
check(targetSDHC);
check(busParamCmd);
if (cachedError != kSDIOReturnSuccess)
return cachedError;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
SDIOReturn_t retval = kSDIOReturnSuccess;
if(busParamCmd->clockRateHz) {
retval = sdiodrv_setClockRate(sdhc, &busParamCmd->clockRateHz, busParamCmd->baseClockRateHz);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
if(busParamCmd->busWidth) {
retval = sdiodrv_setBusWidth(sdhc, busParamCmd->busWidth);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
if(busParamCmd->busSpeedMode) {
retval = sdiodrv_setBusSpeedMode(sdhc, busParamCmd->busSpeedMode);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
if(busParamCmd->clockMode) {
retval = sdiodrv_setClockMode(sdhc, busParamCmd->clockMode);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
return retval;
}
unsigned int nm = 0;
IOPSDIO_status_t
iopsdio_sendSDIOCmd(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOCommandCmd *commandCmd)
{
check(targetSDHC);
check(commandCmd);
if (cachedError != kSDIOReturnSuccess)
return cachedError;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
SDIOReturn_t retval = sdiodrv_sendSDIOCommand(sdhc, &commandCmd->command, &commandCmd->response);
return retval;
}
/*
* read a complete SDIO packet:
* reads are always block aligned
*
* blocks: number of blocks to read, if zero, then issue a one block read in order to get the frame header and
* obtain the packet length from this header, issue a second read in order to to get the remaining blocks
* max_blocks: max_blocks allowed
* data: virtual address if read buffer
*
*/
IOPSDIO_status_t iopsdio_read_packet(unsigned int blocks, unsigned int max_blocks, unsigned int * data, struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOTransferCmd *transferCmd)
{
IOPSDIO_status_t retval = 0;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
struct task_event *dmaCompleteEvent = (struct task_event *)targetSDHC->dmaCompleteEventAddr;
if (blocks == 0) {
transferCmd->transfer.blockCount = 1;
transferCmd->memory.dataLength = transferCmd->transfer.blockSize;
transferCmd->segment[0].length = transferCmd->transfer.blockSize;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | 1;
data = mem_static_map_cached((uint32_t)data);
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
if (transferCmd->memory.dataLength > MAX_SDIO_READ_LEN) {
//paranoia
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet1: len frame is too large %llu \n", transferCmd->memory.dataLength);
retval = kSDIOReturnBadArgument;
return retval;
}
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
if (retval)
return retval;
data = mem_static_map_cached((uint32_t)data);
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
unsigned short check = data[0]>>16;
unsigned short fflen = data[0];
if (fflen) {
if ((fflen + check) != 0xffff) {
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet error %x %d - %x blocks %u %x\n", fflen, fflen, check, blocks, data[1]);
dprintf(DEBUG_CRITICAL, " ===== %p %x %x %x\n", data, transferCmd->segment[0].paddr, data[0],*(unsigned int*)transferCmd->segment[0].paddr);
return retval;
}
unsigned short mask = transferCmd->transfer.blockSize-1;
unsigned short flen = (fflen+mask) & ~mask; //round up
unsigned int remain_blocks = flen ;
if (128 == transferCmd->transfer.blockSize) {
remain_blocks = remain_blocks/128;
} else {
remain_blocks = remain_blocks/transferCmd->transfer.blockSize;
}
if (remain_blocks == 0) {
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet: error zero blocks \n");
return kSDIOReturnBadArgument;
}
remain_blocks--;
if (remain_blocks >= max_blocks) {
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet: not enough blocks %u orig %u flen %x check %x\n", remain_blocks, max_blocks, fflen, check);
return kSDIOReturnBadArgument;
}
if (remain_blocks) {
//read remaining blocks
transferCmd->transfer.blockCount = remain_blocks;
transferCmd->memory.dataLength = transferCmd->transfer.blockSize * remain_blocks;
transferCmd->segment[0].length = transferCmd->transfer.blockSize * remain_blocks;
transferCmd->segment[0].paddr += transferCmd->transfer.blockSize;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | remain_blocks;
if (transferCmd->memory.dataLength > MAX_SDIO_READ_LEN) {
//paranoia,
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet2: len frame is too large %llu remain %u size %hu\n", transferCmd->memory.dataLength, remain_blocks, transferCmd->transfer.blockSize);
retval = kSDIOReturnBadArgument;
return retval;
}
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
transferCmd->segment[0].paddr -= transferCmd->transfer.blockSize;
}
}
} else {
transferCmd->transfer.blockCount = blocks;
transferCmd->memory.dataLength = transferCmd->transfer.blockSize * blocks;
transferCmd->segment[0].length = transferCmd->transfer.blockSize * blocks;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | blocks;
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
}
return retval;
}
void iopsdio_cacheTransferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC)
{
SDIOReturn_t retval = 0;
cachedError = kSDIOReturnSuccess;
//perform access based on the cached command
struct IOPSDIOTransferCmd *transferCmd = &cmd.transferCmd;
unsigned int * data = (unsigned int*)transferCmd->segment[0].paddr;
data = mem_static_map_cached((uint32_t)data);
unsigned int max_blocks;
if (128 == transferCmd->transfer.blockSize)
max_blocks = LOOKAHEAD_READ_PKT_SIZE/128;
else
max_blocks = LOOKAHEAD_READ_PKT_SIZE/transferCmd->transfer.blockSize;
//kick SDIO transfer
retval = iopsdio_read_packet(transferCmd->transfer.blockCount, max_blocks, data, targetSDHC, transferCmd);
if (retval) {
if ((retval < kSDIOReturnDataError) || (retval > kSDIOReturnDataTimeout)) {
//paranoia, make sure we return a Data error here, which means that the host will trigger a Reset
cachedError = kSDIOReturnDataError;
} else {
cachedError = retval;
}
} else {
cachedError = kSDIOReturnSuccess;
}
unsigned int *retp = (unsigned int*)(((unsigned char*)data)+LOOKAHEAD_READ_PKT_SIZE);
//dprintf(0, "iopsdio_cacheTransferSDIOData done %x %x\n", *retp, retval);
//make sure we tell the host processor that we're done
* retp = retval;
platform_cache_operation(CACHE_CLEAN,
(void *)retp,
(32));
}
IOPSDIO_status_t
iopsdio_transferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOTransferCmd *transferCmd)
{
check(targetSDHC);
check(transferCmd);
unsigned int orig_bcount = 0;
unsigned int nlen = 0;
if (cachedError != kSDIOReturnSuccess)
return cachedError;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
struct task_event *dmaCompleteEvent = (struct task_event *)targetSDHC->dmaCompleteEventAddr;
transferCmd->memory.segmentList = transferCmd->segment;
unsigned int orig_data = transferCmd->segment[0].paddr;
unsigned int * data = mem_static_map_cached((uint32_t)orig_data);
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
SDIOReturn_t retval = 0;
if (( transferCmd->transfer.direction == kSDIODirectionWrite) && (*data == MAGIC_WRITE) && ((transferCmd->transfer.command.argument & kSDIOCmd53BlockMode) == kSDIOCmd53BlockMode)) {
unsigned int base_addr = transferCmd->segment[0].paddr + transferCmd->transfer.blockSize;
unsigned int * l = data + 1;
unsigned int cnt = 0;
unsigned int bcount = *l ;
// dprintf(0, "iopsdio_transferSDIOData magic wwwrite got %x %x [%x %x] %u %u %u\n",l[0],l[1], transferCmd->transfer.command.argument,transferCmd->transfer.command.index, transferCmd->transfer.blockCount,transferCmd->transfer.blockSize, transferCmd->memory.segmentCount);
//first sdio block = MAGIC_WRITE/block size of pkt1/block size of pkt 2/block size of pkt3 etc...
//second sdio blocks and so on... = packets data
if (transferCmd->transfer.blockCount == 1) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData magic write error!! %p %x %x %x\n",data, l[0],l[1],l[2]);
return kSDIOReturnBadArgument;
}
while (bcount!=0) {
unsigned int transfer_len = bcount * transferCmd->transfer.blockSize;
transferCmd->memory.dataLength = transfer_len;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | bcount;
transferCmd->transfer.blockCount = bcount;
transferCmd->segment[0].length = transfer_len;
transferCmd->segment[0].paddr = base_addr;
unsigned int *p = (unsigned int*)transferCmd->segment[0].paddr;
p = mem_static_map_cached((uint32_t)p);
platform_cache_operation(CACHE_INVALIDATE,
(void *)p,
(32));
unsigned short check = p[0]>>16;
unsigned short fflen = p[0];
if ((check + fflen) != 0xffff) {
//internal error, the sender tries to send a corrupted packet
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData detected error ---> %p : %x %x %x %x \n", p, p[0], p[1], p[2], p[3]);
retval = kSDIOReturnBadArgument;
} else {
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
}
if (retval) {
*data = cnt; //tell the sender how many packets we did send successfully
platform_cache_operation(CACHE_CLEAN,
(void *)data,
(32));
unsigned int *p = (unsigned int *)base_addr;
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData ---> %x %x %x %x \n", p[0], p[1], p[2], p[3]);
dprintf(DEBUG_CRITICAL, " --> cnt %u bc %u addr %p arg %x retval %x\n", cnt, transferCmd->transfer.blockCount, (void *)transferCmd->segment[0].paddr, transferCmd->transfer.command.argument, retval);
return retval;
}
base_addr += transfer_len;//transferCmd->transfer.blockSize * bcount;
cnt++;
l++;
bcount = *l ;
}
} else if ((transferCmd->transfer.direction == kSDIODirectionRead) && (*data == MAGIC_READ) && ((transferCmd->transfer.command.argument & kSDIOCmd53BlockMode) == kSDIOCmd53BlockMode)) {
//MAGIC READ:
//buffer1 of size LOOKAHEAD_READ_PKT_SIZE + buffer2 of size LOOKAHEAD_READ_PKT_SIZE + one
//additional block used as end of transfer marker
orig_bcount = transferCmd->transfer.blockCount;
// dprintf(0, "iopsdio_transferSDIOData read got %u %u %u \n",l[0],l[1],l[2]);
unsigned int blocks = *(data+MAGIC_READ_PACKET_LEN_OFFSET);
unsigned int lk = *(data+MAGIC_READ_ENABLE_OFFSET);
if (*(data+MAGIC_READ_BUFFER_LENGTH_OFFSET)) {
lookahead_read_pkt_size = *(data+MAGIC_READ_BUFFER_LENGTH_OFFSET);
}
retval = iopsdio_read_packet(blocks, orig_bcount, data, targetSDHC, transferCmd);
if (lk == 0) {
//Host processor doesn't want the look ahead
return retval;
}
if (retval) {
goto return_clear;
}
unsigned char nextlen = *((unsigned char*)data + BCOM_SDIO_NEXTLEN_OFFSET);
unsigned char channel = *((unsigned char*)data + BCOM_SDIO_CHANNEL_OFFSET);
unsigned char offset = *((unsigned char*)data + BCOM_SDIO_DATAOFFSET_OFFSET);
// dprintf(0, "iopsdio_transferSDIOData read got %x %x %x - %u channel %u offset %u\n",data[0],data[1],data[2], nextlen,channel, offset);
unsigned short check = data[0]>>16;
unsigned short fflen = data[0];
if (fflen) {
if ((fflen + check) != 0xffff) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData cksum error %x %d - %x \n", fflen, fflen, check);
retval = kSDIOReturnDataError;
goto return_clear;
}
} else {
if (check==0) {
//fflen and check are null, hence no more data pending
goto return_clear;
}
}
nlen = 16 * nextlen;
if (nlen == 0 && channel == (BCOM_SDIO_GLOM_FRAME_DESCRIPTOR|BCOM_SDIO_GLOM_CHANNEL)) {
//reading a Glom descriptor: calculate the length of next frame
//max number of GLom frames is tunable and typically 7, a glom frame descriptor should not be larger than 64 Bytes
#define SANE_MAX_GLOM_FRAME_LEN 64
if (fflen > SANE_MAX_GLOM_FRAME_LEN) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData glom descriptor is too large %hu \n", fflen);
goto return_clear;
}
if (fflen > 32) {
//uncache the rest of the frame
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
cache_op_size(fflen));
}
unsigned short * p = (unsigned short *)((unsigned char*)data + offset);
unsigned short * end = (unsigned short *)((unsigned char*)data + fflen);
//calculate nlen
while (p < end){
nlen += *p++;
}
if (nlen > LOOKAHEAD_READ_PKT_SIZE) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData calculated glom frame is too large %u \n", nlen);
retval = kSDIOReturnDataError;
goto return_clear;
}
}
//save parameters for the next transfer
nlen += transferCmd->transfer.blockSize-1;
nlen = nlen & ~(transferCmd->transfer.blockSize-1);
if (nlen > MAX_SDIO_READ_LEN) {
//paranoia
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData magic read calculating a bad len frame large %u \n", nlen);
retval = kSDIOReturnBadArgument;
goto return_clear;
}
cmd.transferCmd.transfer.command.argument = transferCmd->transfer.command.argument;
cmd.transferCmd.transfer.command.index = transferCmd->transfer.command.index;
if (128 == transferCmd->transfer.blockSize)
cmd.transferCmd.transfer.blockCount = nlen/128;
else
cmd.transferCmd.transfer.blockCount = nlen/transferCmd->transfer.blockSize;
cmd.transferCmd.transfer.blockSize = transferCmd->transfer.blockSize;
cmd.transferCmd.transfer.direction = transferCmd->transfer.direction;
cmd.segment.length = nlen;
cmd.segment.paddr = orig_data+LOOKAHEAD_READ_PKT_SIZE;
cmd.transferCmd.memory.segmentList = &cmd.segment;
cmd.transferCmd.memory.dataLength = nlen;
cmd.transferCmd.memory.segmentCount = 1;
if (lk > 1) {
//experimental mode, read all at once while the host processor wait - used only for debug 14July2011
iopsdio_cacheTransferSDIOData(targetSDHC);
} else {
//set lookahead to tell the sdio task handler in iop_sdio.c to generate the cached loakahead read
do_lookahead = lk;
}
} else {
if (transferCmd->transfer.direction == kSDIODirectionRead) {
//if transfer is cmd53 block read, check that the requested length matches the number of blocks requested
//and check that the total length is not overly large
nlen = transferCmd->transfer.blockCount * transferCmd->transfer.blockSize;
if (nlen != transferCmd->memory.dataLength) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData len mismatch %u %llu direction %hhu\n", nlen, transferCmd->memory.dataLength, transferCmd->transfer.direction);
return kSDIOReturnBadArgument;
}
if (nlen > MAX_SDIO_READ_LEN) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData data transfer is too large %u %llu direction %hhu\n", nlen, transferCmd->memory.dataLength, transferCmd->transfer.direction);
return kSDIOReturnBadArgument;
}
}
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
if (retval) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData error %x\n",retval);
}
}
return retval;
return_clear:
{
unsigned int last_block;
if (128 == transferCmd->transfer.blockSize)
last_block = (LOOKAHEAD_READ_PKT_SIZE*2)/128;
else
last_block = (LOOKAHEAD_READ_PKT_SIZE*2)/transferCmd->transfer.blockSize;
//paranoia: check if we have enough space in the buffer, then mark it as done
if (orig_bcount >= last_block) {
unsigned int *retp = (unsigned int*)(((unsigned char*)data)+LOOKAHEAD_READ_PKT_SIZE*2);
* retp = retval;
platform_cache_operation(CACHE_CLEAN,
(void *)retp,
(32));
}
}
return retval;
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#ifndef _IOP_SDIO_WRAPPER_H
#define _IOP_SDIO_WRAPPER_H
#include "iop_sdio_protocol.h"
IOPSDIO_status_t iopsdio_init(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOInitCmd *initCmd);
IOPSDIO_status_t iopsdio_free(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOFreeCmd *freeCmd);
IOPSDIO_status_t iopsdio_reset(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOResetCmd *resetCmd);
IOPSDIO_status_t iopsdio_setBusConfig(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOSetBusParamCmd *busParamCmd);
IOPSDIO_status_t iopsdio_sendSDIOCmd(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOCommandCmd *commandCmd);
IOPSDIO_status_t iopsdio_transferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOTransferCmd *transferCmd);
#endif // _IOP_SDIO_WRAPPER_H
+25
View File
@@ -0,0 +1,25 @@
# Copyright (C) 2008 Apple Computer, Inc. All rights reserved.
#
# This document is the property of Apple Computer, Inc.
# It is considered confidential and proprietary.
#
# This document may not be reproduced or transmitted in any form,
# in whole or in part, without the express written permission of
# Apple Computer, Inc.
#
#
# SDIO driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_SDIO=1
IOP_FUNCTIONS += SDIO
ALL_OBJS += $(LOCAL_DIR)/iop_sdio.o $(LOCAL_DIR)/iop_sdio_wrapper.o
INSTALL_HEADERS += $(LOCAL_DIR)/iop_sdio_protocol.h
# XXX tune this to suit SDIO requirements
IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),8192)