Latest compatible version of Classicube from the original GitHub repository (https://github.com/ClassiCube/ClassiCube) that can be compiled on Classicube for PowerMac PPC running Mac OS X 10.4.
This commit is contained in:
373
third_party/dsiwifi/arm_iop/source/wifi_sdio.twl.c
vendored
Normal file
373
third_party/dsiwifi/arm_iop/source/wifi_sdio.twl.c
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Max Thomas
|
||||
* This file is part of DSiWifi and is distributed under the MIT license.
|
||||
* See dsiwifi_license.txt for terms of use.
|
||||
*/
|
||||
|
||||
#include "wifi_sdio.h"
|
||||
|
||||
#ifdef WIFI_SDIO_DEBUG
|
||||
#include "wifi_debug.h"
|
||||
#endif
|
||||
|
||||
#include "wifi_ndma.h"
|
||||
|
||||
#define WIFI_SDIO_NDMA
|
||||
|
||||
void wifi_sdio_controller_init(void* controller)
|
||||
{
|
||||
void* c = controller;
|
||||
if(!c) return;
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ32, 0x0800, 0x0000);
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ32, 0x1000, 0x0000);
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ32, 0x0000, 0x0402);
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_DATA16_CNT, 0x0022, 0x0002);
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_DATA16_CNT, 0x0020, 0x0000);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA32_BLK_LEN, 128);
|
||||
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA32_BLK_CNT, 0x0001);
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_RESET, 0x0003, 0x0000);
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_RESET, 0x0000, 0x0003);
|
||||
|
||||
// Disable all interrupts.
|
||||
wifi_sdio_write32(c, WIFI_SDIO_OFFS_IRQ_MASK, 0xFFFFFFFF);
|
||||
|
||||
wifi_sdio_mask16(c, 0x0FC, 0x0000, 0x00DB);
|
||||
wifi_sdio_mask16(c, 0x0FE, 0x0000, 0x00DB);
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_PORT_SEL, 0b11, 0);
|
||||
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_CLK_CNT, 0x0020);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_CARD_OPT, 0x40EE);
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ32, 0x8000, 0x0000);
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ32, 0x0000, 0x0100);
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ32, 0x0100, 0x0000);
|
||||
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_PORT_SEL, 0b11, 0);
|
||||
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA16_BLK_LEN, 128);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_STOP, 0x0100);
|
||||
}
|
||||
|
||||
void wifi_sdio_send_command(wifi_sdio_ctx* ctx, wifi_sdio_command cmd, u32 args)
|
||||
{
|
||||
if(!ctx) return;
|
||||
void* c = ctx->controller;
|
||||
if(!c) return;
|
||||
|
||||
// Safety fallback
|
||||
if (!ctx->block_size)
|
||||
ctx->block_size = 512;
|
||||
|
||||
void* buffer = ctx->buffer;
|
||||
size_t size = ctx->size;
|
||||
|
||||
ctx->status = 0;
|
||||
u16 stat0 = 0, stat1 = 0;
|
||||
u16 stat0_completion_flags = 0;
|
||||
|
||||
u16 cnt32 = 0;
|
||||
|
||||
// Are we expecting a response? We need to wait for it.
|
||||
if(cmd.response_type != wifi_sdio_resp_none)
|
||||
stat0_completion_flags |= WIFI_SDIO_STAT0_CMDRESPEND;
|
||||
|
||||
// Are we doing a data transfer? We need to wait for it to end.
|
||||
if(cmd.data_transfer)
|
||||
stat0_completion_flags |= WIFI_SDIO_STAT0_DATAEND;
|
||||
|
||||
#ifdef WIFI_SDIO_DEBUG
|
||||
if(ctx->debug)
|
||||
wifi_printlnf("CMD#: 0x%04X (%u) (%X) -> %u:%u",
|
||||
cmd.raw, cmd.cmd, stat0_completion_flags, ctx->port, ctx->address);
|
||||
#endif
|
||||
|
||||
bool buffer32 = false;
|
||||
if(buffer && ((u32)buffer & 3) == 0) buffer32 = true;
|
||||
|
||||
// Force alignment
|
||||
if (buffer && !buffer32) {
|
||||
ctx->status |= 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the SDIO controller is not busy.
|
||||
while(wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ_STAT1) & WIFI_SDIO_STAT1_CMD_BUSY);
|
||||
|
||||
// ACK all interrupts and halt
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_IRQ_STAT0, 0);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_IRQ_STAT1, 0);
|
||||
//wifi_sdio_mask16(c, WIFI_SDIO_OFFS_STOP, 1, 0);
|
||||
|
||||
// Write command arguments.
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_CMD_PARAM0, args & 0xFFFF);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_CMD_PARAM1, args >> 16);
|
||||
|
||||
// Set block len and counts
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA16_BLK_LEN, ctx->block_size);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA16_BLK_CNT, size / ctx->block_size);
|
||||
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA32_BLK_LEN, ctx->block_size);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA32_BLK_CNT, size / ctx->block_size);
|
||||
|
||||
// Data32 mode
|
||||
bool is_block = (cmd.data_length == wifi_sdio_multiple_block);
|
||||
if (is_block) {
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_DATA16_CNT, 0x0002);
|
||||
if(cmd.data_direction == wifi_sdio_data_read)
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_IRQ32, 0xC02);
|
||||
else
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_IRQ32, 0x1402);
|
||||
}
|
||||
|
||||
// Write command.
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_CMD, cmd.raw);
|
||||
|
||||
#ifdef WIFI_SDIO_NDMA
|
||||
if(cmd.data_direction == wifi_sdio_data_read && is_block && buffer)
|
||||
{
|
||||
wifi_ndma_read(buffer, size);
|
||||
return;
|
||||
}
|
||||
else if (cmd.data_direction == wifi_sdio_data_write && is_block && buffer)
|
||||
{
|
||||
wifi_ndma_write(buffer, size);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
while(true)
|
||||
{
|
||||
stat1 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ_STAT1);
|
||||
|
||||
cnt32 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ32);
|
||||
|
||||
// Ready to receive data.
|
||||
if(cnt32 & 0x100)
|
||||
{
|
||||
// Are we actually meant to receive data?
|
||||
if(cmd.data_direction == wifi_sdio_data_read && buffer)
|
||||
{
|
||||
// ACK ready state.
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ_STAT1, WIFI_SDIO_STAT1_RXRDY, 0);
|
||||
|
||||
if(size > ctx->block_size - 1)
|
||||
{
|
||||
//#ifdef WIFI_SDIO_NDMA
|
||||
//wifi_ndma_read(buffer, ctx->block_size);
|
||||
//buffer += ctx->block_size;
|
||||
//#else
|
||||
void* buffer_end = buffer + ctx->block_size;
|
||||
|
||||
while(buffer != buffer_end)
|
||||
{
|
||||
*(u32*)buffer = wifi_sdio_read32(c, WIFI_SDIO_OFFS_DATA32_FIFO);
|
||||
|
||||
//wifi_printlnf("asdf %x", *(u32*)buffer);
|
||||
|
||||
buffer += sizeof(u32);
|
||||
}
|
||||
//#endif
|
||||
size -= ctx->block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Data transmission requested.
|
||||
//if(!(cnt32 & 0x200))
|
||||
if(stat1 & WIFI_SDIO_STAT1_TXRQ)
|
||||
{
|
||||
// Are we actually meant to write data?
|
||||
if(cmd.data_direction == wifi_sdio_data_write && buffer)
|
||||
{
|
||||
// ACK request.
|
||||
wifi_sdio_mask16(c, WIFI_SDIO_OFFS_IRQ_STAT1, WIFI_SDIO_STAT1_TXRQ, 0);
|
||||
|
||||
if(size > ctx->block_size-1)
|
||||
{
|
||||
//#ifdef WIFI_SDIO_NDMA
|
||||
//wifi_ndma_write(buffer, ctx->block_size);
|
||||
//buffer += ctx->block_size;
|
||||
//#else
|
||||
void* buffer_end = buffer + ctx->block_size;
|
||||
|
||||
while(buffer != buffer_end)
|
||||
{
|
||||
u32 data = *(u32*)buffer;
|
||||
buffer += sizeof(u32);
|
||||
wifi_sdio_write32(c, WIFI_SDIO_OFFS_DATA32_FIFO, data);
|
||||
}
|
||||
//#endif
|
||||
size -= ctx->block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Has an error been asserted? Exit if so.
|
||||
if(stat1 & WIFI_SDIO_MASK_ERR)
|
||||
{
|
||||
#ifdef WIFI_SDIO_DEBUG
|
||||
if(ctx->debug)
|
||||
wifi_printlnf("ERR#: %04X 0000", stat1 & WIFI_SDIO_MASK_ERR);
|
||||
#endif
|
||||
// Error flag.
|
||||
ctx->status |= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
bool end_cond = !(stat1 & WIFI_SDIO_STAT1_CMD_BUSY);
|
||||
if (is_block) {
|
||||
stat0 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ_STAT0);
|
||||
end_cond = (stat0 & WIFI_SDIO_STAT0_CMDRESPEND && !size);
|
||||
}
|
||||
|
||||
// Not busy...
|
||||
if(end_cond)
|
||||
{
|
||||
stat0 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ_STAT0);
|
||||
|
||||
// Set response end flag.
|
||||
if(stat0 & WIFI_SDIO_STAT0_CMDRESPEND)
|
||||
ctx->status |= 1;
|
||||
|
||||
// Set data end flag.
|
||||
if(stat0 & WIFI_SDIO_STAT0_DATAEND)
|
||||
ctx->status |= 2;
|
||||
|
||||
// If done (all completion criteria), exit.
|
||||
if((stat0 & stat0_completion_flags) == stat0_completion_flags)
|
||||
break;
|
||||
}
|
||||
|
||||
if(ctx->break_early && !size)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stat0 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ_STAT0);
|
||||
ctx->stat1 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_IRQ_STAT1);
|
||||
|
||||
ctx->err0 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_ERR_DETAIL0);
|
||||
ctx->err1 = wifi_sdio_read16(c, WIFI_SDIO_OFFS_ERR_DETAIL1);
|
||||
|
||||
// ACK all interrupts.
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_IRQ_STAT0, 0);
|
||||
wifi_sdio_write16(c, WIFI_SDIO_OFFS_IRQ_STAT1, 0);
|
||||
|
||||
// If the command has a response, pull it in to sdmmc_ctx.
|
||||
if(cmd.response_type != wifi_sdio_resp_none)
|
||||
{
|
||||
ctx->resp[0] = wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP0) | (u32)(wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP1) << 16);
|
||||
ctx->resp[1] = wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP2) | (u32)(wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP3) << 16);
|
||||
ctx->resp[2] = wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP4) | (u32)(wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP5) << 16);
|
||||
ctx->resp[3] = wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP6) | (u32)(wifi_sdio_read16(c, WIFI_SDIO_OFFS_RESP7) << 16);
|
||||
}
|
||||
|
||||
#ifdef WIFI_SDIO_DEBUG
|
||||
if(ctx->debug)
|
||||
{
|
||||
wifi_printlnf("STAT: %04X %04X (%X) INFO: %04X %04X", ctx->stat1, ctx->stat0,
|
||||
ctx->status, ctx->err1, ctx->err0);
|
||||
|
||||
if(cmd.response_type != wifi_sdio_resp_none)
|
||||
{
|
||||
if(cmd.response_type == wifi_sdio_resp_136bit) {
|
||||
wifi_printlnf("RESP: %08lX %08lX %08lX %08lX",
|
||||
ctx->resp[0], ctx->resp[1], ctx->resp[2], ctx->resp[3]);
|
||||
} else {
|
||||
wifi_printlnf("RESP: %08lX", ctx->resp[0]);
|
||||
}
|
||||
}
|
||||
|
||||
wifi_printlnf("");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void wifi_sdio_switch_device(wifi_sdio_ctx* ctx)
|
||||
{
|
||||
if(!ctx) return;
|
||||
|
||||
// Reconfigure the bus to talk to the new device.
|
||||
wifi_sdio_mask16(ctx->controller, WIFI_SDIO_OFFS_PORT_SEL, 0b11, ctx->port);
|
||||
wifi_sdio_setclk(ctx->controller, ctx->clk_cnt);
|
||||
|
||||
// WIFI_SDIO_CARD_OPT bit 15: bus width (0 = 4-bit, 1 = 1-bit).
|
||||
if(ctx->bus_width == 4)
|
||||
wifi_sdio_mask16(ctx->controller, WIFI_SDIO_OFFS_CARD_OPT, BIT(15), 0);
|
||||
else
|
||||
wifi_sdio_mask16(ctx->controller, WIFI_SDIO_OFFS_CARD_OPT, 0, BIT(15));
|
||||
}
|
||||
|
||||
u16 wifi_sdio_read16(void* controller, u32 reg)
|
||||
{
|
||||
return *(vu16*)(controller + reg);
|
||||
}
|
||||
|
||||
u32 wifi_sdio_read32(void* controller, u32 reg)
|
||||
{
|
||||
return *(vu32*)(controller + reg);
|
||||
}
|
||||
|
||||
void wifi_sdio_write16(void* controller, u32 reg, u16 val)
|
||||
{
|
||||
*(vu16*)(controller + reg) = val;
|
||||
}
|
||||
|
||||
void wifi_sdio_write32(void* controller, u32 reg, u32 val)
|
||||
{
|
||||
*(vu32*)(controller + reg) = val;
|
||||
}
|
||||
|
||||
void wifi_sdio_mask16(void* controller, u32 reg, u16 clear, u16 set)
|
||||
{
|
||||
u16 val = wifi_sdio_read16(controller, reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
wifi_sdio_write16(controller, reg, val);
|
||||
}
|
||||
|
||||
void wifi_sdio_mask32(void* controller, u32 reg, u32 clear, u32 set)
|
||||
{
|
||||
u32 val = wifi_sdio_read32(controller, reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
wifi_sdio_write32(controller, reg, val);
|
||||
}
|
||||
|
||||
void wifi_sdio_setclk(void* controller, u32 data)
|
||||
{
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CLK_CNT, 0x100, 0);
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CLK_CNT, 0x2FF, data & 0x2FF);
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CLK_CNT, 0x0, 0x100);
|
||||
}
|
||||
|
||||
void wifi_sdio_stop(void* controller)
|
||||
{
|
||||
wifi_sdio_write16(controller, WIFI_SDIO_OFFS_STOP, 0x100);
|
||||
}
|
||||
|
||||
void wifi_sdio_enable_cardirq(void* controller, bool en)
|
||||
{
|
||||
if (en)
|
||||
{
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CARDIRQ_CTL, 0, 0x0001);
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CARDIRQ_MASK, 0x0003, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CARDIRQ_CTL, 1, 0);
|
||||
wifi_sdio_mask16(controller, WIFI_SDIO_OFFS_CARDIRQ_MASK, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
u16 wifi_sdio_get_cardirq_stat(void* controller)
|
||||
{
|
||||
return wifi_sdio_read16(controller, WIFI_SDIO_OFFS_CARDIRQ_STAT);
|
||||
}
|
||||
Reference in New Issue
Block a user