iBoot/apps/EmbeddedIOP/function_sdio/iop_sdio.c

200 lines
5.4 KiB
C

/*
* 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);
}