- WIP: OMAP35x SD/MMC controller driver
- WIP: Pagecache - Added sleep() and usleep() functions
This commit is contained in:
6
Makefile
6
Makefile
@@ -5,10 +5,10 @@ OBJCOPY=arm-none-eabi-objcopy
|
||||
OBJDUMP=arm-none-eabi-objdump
|
||||
|
||||
#CFLAGS=-ffreestanding -march=armv7-a -mcpu=cortex-a8 -mfloat-abi=softfp -mfpu=neon --std=gnu99 -ggdb -Wall -Wextra -pedantic -Wno-unused-parameter -Og -flto
|
||||
CXXFLAGS=-ffreestanding -march=armv7-a -mcpu=cortex-a8 -mfloat-abi=softfp -mfpu=neon --std=c++11 -ggdb -Wall -Wextra -pedantic -Wno-unused-parameter -fno-rtti -fstrict-enums -Wabi -Og -flto
|
||||
CXXFLAGS=-ffreestanding -march=armv7-a -mcpu=cortex-a8 -mfloat-abi=softfp -mfpu=neon -mno-unaligned-access --std=c++11 -ggdb -Wall -Wextra -pedantic -Wno-unused-parameter -fno-rtti -fstrict-enums -Wabi -Og -flto
|
||||
LDFLAGS=-static
|
||||
C_SRCS=
|
||||
CXX_SRCS=cortexa8.cc drv_omap35x_gpt.cc drv_omap35x_i2c.cc drv_tps65950.cc main.cc mm.cc newlib_syscall.cc omap35x.cc omap35x_intc.cc omap35x_prcm.cc phys_mm.cc scheduler.cc syscall.cc uart.cc
|
||||
CXX_SRCS=cortexa8.cc drv_omap35x_gpt.cc drv_omap35x_i2c.cc drv_omap35x_sdmmc.cc drv_tps65950.cc main.cc mm.cc newlib_syscall.cc omap35x.cc omap35x_intc.cc omap35x_prcm.cc pagecache.cc phys_mm.cc scheduler.cc sleep.cc syscall.cc uart.cc
|
||||
S_SRCS=cortexa8_asm.s
|
||||
|
||||
OBJS=$(addprefix objs/,$(C_SRCS:.c=.o)) $(addprefix objs/,$(CXX_SRCS:.cc=.o)) $(addprefix objs/,$(S_SRCS:.s=.o))
|
||||
@@ -29,7 +29,7 @@ beagle-nand.bin: fw.img
|
||||
./bb_nandflash_ecc $@ 0x0 0xe80000 || true
|
||||
|
||||
qemu: beagle-nand.bin
|
||||
qemu-system-arm -M beagle -m 256M -mtdblock beagle-nand.bin -nographic -s
|
||||
qemu-system-arm -M beagle -m 256M -mtdblock beagle-nand.bin -sd sd.bin -nographic -s
|
||||
|
||||
objs/%.o: %.c
|
||||
$(CXX) $(CXXFLAGS) -c -MMD -MP -o $@ $<
|
||||
|
||||
25
cortexa8.cc
25
cortexa8.cc
@@ -78,6 +78,13 @@ void cortexa8::disable_dcache() noexcept {
|
||||
: [reg] "=r"(reg));
|
||||
}
|
||||
|
||||
void cortexa8::errata() noexcept {
|
||||
// Errata 458693: Disable PLD and enable L1NEON
|
||||
__asm__ __volatile__ ("mrc 15, 0, r0, c1, c0, 1; orr r0, r0, #0x220; mcr 15, 0, r0, c1, c0, 1"
|
||||
: : : "r0");
|
||||
|
||||
}
|
||||
|
||||
struct ptentry_section_t {
|
||||
unsigned :1;
|
||||
unsigned one:1;
|
||||
@@ -372,15 +379,15 @@ void cortexa8::unmap_pages(uintptr_t virt, unsigned count) {
|
||||
// Clear L2 pagetable entries
|
||||
for(unsigned l1 = virt_l1;l1 <= virt_end_l1;++l1) {
|
||||
assert(ttable1_virt[l1] != nullptr);
|
||||
for(unsigned l2 = (l1==virt_l1)?virt_l2:0;l2 <= (l1==virt_end_l1)?virt_end_l2:255;++l2) {
|
||||
for(unsigned l2 = ((l1==virt_l1)?virt_l2:0);l2 <= ((l1==virt_end_l1)?virt_end_l2:255);++l2) {
|
||||
ttable1_virt[l1][l2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush L2 pagetables from L1D$
|
||||
for(unsigned l1 = virt_l1;l1 <= virt_end_l1;++l1) {
|
||||
for(unsigned cl = (l1==virt_l1)?virt_l2/16:0;
|
||||
cl <= (l1==virt_end_l1)?(virt_end_l2/16+((virt_end_l2%16==0)?0:1)):15;
|
||||
for(unsigned cl = ((l1==virt_l1)?virt_l2/16:0);
|
||||
cl <= ((l1==virt_end_l1)?(virt_end_l2/16+((virt_end_l2%16==0)?0:1)):15);
|
||||
++cl) {
|
||||
__asm__ __volatile__ ("mcr 15, 0, %[val], c7, c11, 1"
|
||||
: : [val] "r"(ttable1_virt[l1]+cl*16));
|
||||
@@ -429,9 +436,9 @@ void _cortexa8_excp_data_abt() {
|
||||
__asm__ ("mov %[lr], lr; mrc 15, 0, %[dfsr], c5, c0, 0; mrc 15, 0, %[dfar], c6, c0, 0"
|
||||
: [lr] "=r"(lr), [dfsr] "=r"(dfsr), [dfar] "=r"(dfar));
|
||||
printf("ERROR: Data abort\n");
|
||||
printf("PC: %.8lx Fault Address: %.8lx Fault code: %.lx\n",
|
||||
lr-8, dfar, dfsr&0x4);
|
||||
while(1) {}
|
||||
printf("PC: %.8lx Fault Address: %.8lx Fault code: %lx\n",
|
||||
lr-4, dfar, ((dfsr>>7)&0x20) | ((dfsr>>6)&0x10) | (dfsr&0xf));
|
||||
while(1) { __asm__ __volatile__ ("wfi"); }
|
||||
}
|
||||
|
||||
void _cortexa8_excp_pf_abt() {
|
||||
@@ -439,9 +446,9 @@ void _cortexa8_excp_pf_abt() {
|
||||
__asm__ ("mov %[lr], lr; mrc 15, 0, %[ifsr], c5, c0, 1; mrc 15, 0, %[ifar], c6, c0, 2"
|
||||
: [lr] "=r"(lr), [ifsr] "=r"(ifsr), [ifar] "=r"(ifar));
|
||||
printf("ERROR: Prefetch abort\n");
|
||||
printf("PC: %.8lx Fault Address: %.8lx Fault code: %.lx\n",
|
||||
lr-4, ifar, ifsr&0x4);
|
||||
while(1) {}
|
||||
printf("PC: %.8lx Fault Address: %.8lx Fault code: %lx\n",
|
||||
lr-4, ifar, ((ifsr>>7)&0x20) | ((ifsr>>6)&0x10) | (ifsr&0xf));
|
||||
while(1) { __asm__ __volatile__ ("wfi"); }
|
||||
}
|
||||
|
||||
void _cortexa8_excp_undef() {
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace cortexa8 {
|
||||
void disable_icache() noexcept;
|
||||
void disable_dcache() noexcept;
|
||||
|
||||
void errata() noexcept;
|
||||
|
||||
void init_mmu() noexcept;
|
||||
|
||||
void init_handlers() noexcept;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
class OMAP35x_GPT_impl {
|
||||
public:
|
||||
OMAP35x_GPT_impl(uintptr_t base, int irq) : base_{base}, irq_{irq} {
|
||||
OMAP35x_GPT_impl(uintptr_t base, int irq) : base_{base}, irq_{irq}, ticks_(0) {
|
||||
}
|
||||
|
||||
~OMAP35x_GPT_impl() {
|
||||
@@ -46,8 +46,26 @@ public:
|
||||
global::intc->enable_int(irq_);
|
||||
}
|
||||
|
||||
void ustimer() {
|
||||
r_tiocp_cfg() = 0x215; // Clockactiviy = 2, emufree = 0, idlemode = 2 (smartidle), wakeup = ena, autoidle = 1
|
||||
|
||||
r_tldr() = 0;
|
||||
r_tcrr() = 0;
|
||||
r_tclr() = 0x3; // autoreload = 1, start = 1
|
||||
}
|
||||
|
||||
uint32_t getTicks() const {
|
||||
return ticks_;
|
||||
}
|
||||
|
||||
uint32_t getCounter() {
|
||||
return r_tcrr();
|
||||
}
|
||||
|
||||
private:
|
||||
void irqhandler() {
|
||||
++ticks_;
|
||||
|
||||
if(handler_)
|
||||
handler_();
|
||||
|
||||
@@ -78,6 +96,7 @@ private:
|
||||
MMIO_alloc base_;
|
||||
int irq_;
|
||||
int_handler_t handler_;
|
||||
volatile uint32_t ticks_;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,3 +109,15 @@ OMAP35x_GPT::~OMAP35x_GPT() {
|
||||
void OMAP35x_GPT::ticktimer(int_handler_t handler) {
|
||||
impl_->ticktimer(handler);
|
||||
}
|
||||
|
||||
uint32_t OMAP35x_GPT::getTicks() const {
|
||||
return impl_->getTicks();
|
||||
}
|
||||
|
||||
void OMAP35x_GPT::ustimer() {
|
||||
impl_->ustimer();
|
||||
}
|
||||
|
||||
uint32_t OMAP35x_GPT::getCounter() {
|
||||
return impl_->getCounter();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "omap35x_intc.hh"
|
||||
|
||||
#define USTIMER_PER_US 26
|
||||
#define TICKTIMER_MS 10
|
||||
|
||||
class OMAP35x_GPT_impl;
|
||||
|
||||
class OMAP35x_GPT {
|
||||
@@ -18,6 +21,14 @@ public:
|
||||
// The GPT must be GPTIMER1, GPTIMER2 or GPTIMER10 because the 1ms-Tick functionality is needed
|
||||
void ticktimer(int_handler_t handler);
|
||||
|
||||
uint32_t getTicks() const;
|
||||
|
||||
// Configure the GPT as the system microsecond timer
|
||||
// The GPT counts up at USTIMER_PER_US counts/us
|
||||
void ustimer();
|
||||
|
||||
uint32_t getCounter();
|
||||
|
||||
private:
|
||||
std::unique_ptr<OMAP35x_GPT_impl> impl_;
|
||||
};
|
||||
|
||||
618
drv_omap35x_sdmmc.cc
Normal file
618
drv_omap35x_sdmmc.cc
Normal file
@@ -0,0 +1,618 @@
|
||||
#include <cstdint>
|
||||
#include <errno.h>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#include "exceptions.hh"
|
||||
#include "sleep.hh"
|
||||
#include "globals.hh"
|
||||
#include "drv_omap35x_gpt.hh"
|
||||
#include "omap35x_intc.hh"
|
||||
#include "omap35x_prcm.hh"
|
||||
#include "drv_omap35x_sdmmc.hh"
|
||||
|
||||
class commanderror : public ex::error {
|
||||
public:
|
||||
commanderror(uint32_t stat) : ex::error(EIO), stat(stat) {
|
||||
}
|
||||
|
||||
uint32_t stat;
|
||||
};
|
||||
|
||||
class dataerror : public ex::error {
|
||||
public:
|
||||
dataerror(uint32_t stat) : ex::error(EIO), stat(stat) {
|
||||
}
|
||||
|
||||
uint32_t stat;
|
||||
};
|
||||
|
||||
#define ACMD(x) ((((x)) | 0x100))
|
||||
|
||||
typedef union {
|
||||
uint32_t data[4];
|
||||
struct {
|
||||
unsigned :8;
|
||||
unsigned mdt_m:4;
|
||||
unsigned mdt_y:8;
|
||||
unsigned :4;
|
||||
unsigned psn:32;
|
||||
unsigned prv:8; // BCD coded
|
||||
char pnm[5];
|
||||
char oid[2];
|
||||
unsigned mid:8;
|
||||
} __attribute__((packed)) bits;
|
||||
} CID_reg;
|
||||
|
||||
typedef union {
|
||||
uint32_t data[4];
|
||||
struct {
|
||||
unsigned :10;
|
||||
unsigned file_format:2;
|
||||
unsigned tmp_write_protect:1;
|
||||
unsigned perm_write_protect:1;
|
||||
unsigned copy:1;
|
||||
unsigned file_format_grp:1;
|
||||
unsigned :5;
|
||||
unsigned write_bl_partial:1;
|
||||
unsigned write_bl_len:4;
|
||||
unsigned r2w_factor:3;
|
||||
unsigned :2;
|
||||
unsigned wp_grp_enable:1;
|
||||
unsigned wp_grp_size:7;
|
||||
unsigned sector_size:7;
|
||||
unsigned erase_blk_en:1;
|
||||
unsigned c_size_mult:3;
|
||||
unsigned vdd_w_curr_max:3;
|
||||
unsigned vdd_w_curr_min:3;
|
||||
unsigned vdd_r_curr_max:3;
|
||||
unsigned vdd_r_curr_min:3;
|
||||
unsigned c_size:12;
|
||||
unsigned :2;
|
||||
unsigned dsr_imp:1;
|
||||
unsigned read_blk_misalign:1;
|
||||
unsigned write_blk_misalign:1;
|
||||
unsigned read_bl_partial:1;
|
||||
unsigned read_bl_len:4;
|
||||
unsigned ccc:12;
|
||||
unsigned tran_speed:8;
|
||||
unsigned nsac:8;
|
||||
unsigned taac:8;
|
||||
unsigned :6;
|
||||
unsigned csd_structure:2;
|
||||
} __attribute__((packed)) v1;
|
||||
struct {
|
||||
unsigned :12;
|
||||
unsigned tmp_write_protect:1;
|
||||
unsigned perm_write_protect:1;
|
||||
unsigned copy:1;
|
||||
unsigned :32;
|
||||
unsigned :1;
|
||||
unsigned c_size:22;
|
||||
unsigned :6;
|
||||
unsigned dsr_imp:1;
|
||||
unsigned :7;
|
||||
unsigned ccc:12;
|
||||
unsigned :30;
|
||||
unsigned csd_structure:2;
|
||||
} __attribute__((packed)) v2;
|
||||
} CSD_reg;
|
||||
|
||||
#define BCDDEC(x) (((((x))&0xf) + (((((x))>>4)&0xf)*10)))
|
||||
|
||||
OMAP35x_SDMMC::OMAP35x_SDMMC(uintptr_t base, unsigned irq, unsigned drq_rx, unsigned drq_tx)
|
||||
: base_{base}, irq_(irq), drq_rx_(drq_rx), drq_tx_(drq_tx), rca_(0), intSem_(0) {
|
||||
printf("OMAP35x SD/MMC controller\n");
|
||||
// Reset the controller
|
||||
r_sysconfig() |= (1<<1);
|
||||
while(!(r_sysstatus() & (1<<0)));
|
||||
|
||||
r_sysctl() |= (1<<24); // Software reset all
|
||||
while(r_sysctl() & (1<<24));
|
||||
|
||||
// Set capabilities
|
||||
uint32_t capa = (1<<26);
|
||||
if(base_.get_phys() == 0x4809c000) { // MMC 1
|
||||
printf("MMCHS1, activating 3.0V support\n");
|
||||
capa |= (1<<25);
|
||||
}
|
||||
r_capa() |= capa;
|
||||
|
||||
// Install interrupt handler
|
||||
global::intc->register_handler(irq_, std::bind(&OMAP35x_SDMMC::int_handler, this), 0);
|
||||
global::intc->enable_int(irq_);
|
||||
|
||||
// Configure
|
||||
r_hctl() = (r_hctl() & ~(0x7<<9)) | (0x6<<9); // 3.0V mode
|
||||
r_hctl() |= (1<<8); // power on
|
||||
while(!(r_hctl() & (1<<8)));
|
||||
|
||||
// TODO: padconf
|
||||
|
||||
changeClock(0x3ff);
|
||||
|
||||
r_ie() = (1<<28) | (1<<22) | (1<<21) | (1<<20) |(1<<19) | (1<<18) | (1<<17) | (1<<16) | (1<<5) | (1<<1) | (1<<0);
|
||||
|
||||
// Card detect
|
||||
r_con() |= (1<<1); // Send initialization stream
|
||||
r_cmd() = 0x0; // Dummy command
|
||||
|
||||
usleep(1000);
|
||||
|
||||
r_stat() |= (1<<0);
|
||||
r_con() &= ~(1<<1); // End initialization stream
|
||||
r_stat() = 0xffffffff;
|
||||
|
||||
// Set clock to ~400 kHz
|
||||
changeClock(240);
|
||||
|
||||
cmd_reset();
|
||||
|
||||
r_ise() = (1<<28) | (1<<22) | (1<<21) // | (1<<20)
|
||||
|(1<<19) | (1<<18) | (1<<17) | (1<<16) // | (1<<5) | (1<<1)
|
||||
| (1<<0);
|
||||
|
||||
printf("Probing SD/MMC card...\n");
|
||||
try {
|
||||
// Send CMD0
|
||||
command(0, 0);
|
||||
|
||||
// Here we could test for SDIO (send CMD5), but we don't support SDIO (yet)
|
||||
bool sdv2 = false;
|
||||
uint32_t resp;
|
||||
|
||||
try {
|
||||
resp = command_r1(8, 0x000001a5);
|
||||
if((resp&0xfff) != 0x1a5) {
|
||||
printf("CMD8 unexpected response: %.8lx\n", resp);
|
||||
throw ex::error{EIO};
|
||||
} else
|
||||
sdv2 = true;
|
||||
} catch(commanderror &ex) {
|
||||
printf("CMD8 failed: %.8lx\n", ex.stat);
|
||||
} catch(ex::timeout &ex) {
|
||||
printf("CMD8 timed out\n");
|
||||
cmd_reset();
|
||||
}
|
||||
|
||||
bool sd = false;
|
||||
uint32_t startTicks = global::ticktimer->getTicks(), endTicks;
|
||||
uint32_t arg = (sdv2?0x50000000:0);
|
||||
|
||||
while((endTicks = global::ticktimer->getTicks()) < (startTicks+1000/TICKTIMER_MS)) { // Try for 1s
|
||||
try {
|
||||
resp = command_r1(ACMD(41), arg);
|
||||
arg |= (0x1ff<<15); // Set OCR bits after first ACMD41
|
||||
} catch (ex::timeout &ex) {
|
||||
break; // On command timeout we don't have to continue trying
|
||||
}
|
||||
if(resp & (1<<31)) {
|
||||
printf("ACMD41 resp: %.8lx after %lu ms\n", resp, (endTicks-startTicks)*TICKTIMER_MS);
|
||||
sd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!sd) { // Try MMC init
|
||||
assert(!sdv2);
|
||||
arg = 0;
|
||||
r_con() |= (1<<0); // Open drain
|
||||
startTicks = global::ticktimer->getTicks();
|
||||
while((endTicks = global::ticktimer->getTicks()) < startTicks+1000/TICKTIMER_MS) {
|
||||
try {
|
||||
resp = command_r1(1, 0);
|
||||
arg |= (0x1ff<<15); // Set OCR bits after first CMD1
|
||||
} catch (ex::timeout &ex) {
|
||||
// On command timeout we don't have to continue trying
|
||||
// Unknown/Unsupported card type
|
||||
throw ex::error{EIO};
|
||||
}
|
||||
if(resp & (1<<31)) {
|
||||
printf("CMD1 resp: %.8lx after %lu ms\n", resp, (endTicks-startTicks)*TICKTIMER_MS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<uint32_t, 4> resp4 = command_r2(2, 0);
|
||||
CID_reg cid;
|
||||
for(int i = 0;i <4;++i)
|
||||
cid.data[i] = resp4[i];
|
||||
printf(" %c%c %c%c%c%c%c Rev %hhu Serial# %.8x Date %u/%u\n",
|
||||
cid.bits.oid[1], cid.bits.oid[0],
|
||||
cid.bits.pnm[4], cid.bits.pnm[3], cid.bits.pnm[2], cid.bits.pnm[1], cid.bits.pnm[0],
|
||||
cid.bits.prv, cid.bits.psn, cid.bits.mdt_m, 2000+cid.bits.mdt_y);
|
||||
|
||||
resp = command_r1(3, 0);
|
||||
|
||||
resp &= 0xffff0000;
|
||||
rca_ = resp>>16;
|
||||
|
||||
r_con() &= ~(1<<0); // No more open drain
|
||||
|
||||
resp4 = command_r2(9, rca_<<16);
|
||||
printf("CSD: %.8lx %.8lx %.8lx %.8lx\n",
|
||||
resp4[3], resp4[2], resp4[1], resp4[0]);
|
||||
|
||||
CSD_reg csd;
|
||||
for(int i = 0;i <4;++i)
|
||||
csd.data[i] = resp4[i];
|
||||
|
||||
if((csd.v1.csd_structure == 1) && sdv2) {
|
||||
blockSize_ = 512;
|
||||
sectorSize_ = 64*1024;
|
||||
|
||||
cardBlocks_ = (csd.v2.c_size+1)*1024;
|
||||
} else {
|
||||
assert(csd.v1.csd_structure == 0);
|
||||
|
||||
blockSize_ = _pow2(csd.v1.read_bl_len);
|
||||
|
||||
sectorSize_ = blockSize_*(csd.v1.sector_size+1);
|
||||
|
||||
cardBlocks_ = (csd.v1.c_size+1)*_pow2(csd.v1.c_size_mult+2);
|
||||
}
|
||||
|
||||
if(!sd) {
|
||||
changeClock(5); // 19.2 MHz
|
||||
writeTimeout_ = 11; // ~500 ms
|
||||
readTimeout_ = 8; // ~100 ms
|
||||
} else {
|
||||
// TODO: Check card for High-Speed (50 MHz) support
|
||||
changeClock(4); // 24 MHz
|
||||
writeTimeout_ = 11; // ~ 500 ms
|
||||
readTimeout_ = 9; // ~ 100 ms
|
||||
}
|
||||
|
||||
if(sd) {
|
||||
if(sdv2) {
|
||||
if(csd.v1.csd_structure == 1)
|
||||
if(cardBlocks_ >= 67108864u) // 32GiB
|
||||
cardType_ = CardType::sdxc;
|
||||
else
|
||||
cardType_ = CardType::sdhc;
|
||||
else
|
||||
cardType_ = CardType::sdv2;
|
||||
} else
|
||||
cardType_ = CardType::sdv1;
|
||||
} else
|
||||
cardType_ = CardType::mmc;
|
||||
|
||||
printf("Detected %s card, %u KiB (%u byte Blocks, %u byte Sectors)\n",
|
||||
cardTypeToString(), cardBlocks_*blockSize_/1024, blockSize_, sectorSize_);
|
||||
|
||||
resp = command_r1b(7, rca_<<16);
|
||||
printf("%.8lx\n", resp);
|
||||
|
||||
if(cardType_ != CardType::mmc) {
|
||||
resp = command_r1(ACMD(6), 0x2); // Set 4 byte bus on card
|
||||
|
||||
r_hctl() |= (1<<1); // Set 4 byte bus on controller
|
||||
}
|
||||
|
||||
resp = command_r1(16, 512);
|
||||
printf("%.8lx\n", resp);
|
||||
|
||||
try {
|
||||
test_data(0);
|
||||
} catch(dataerror &ex) {
|
||||
printf("Data error: %.8lx\n", ex.stat);
|
||||
}
|
||||
} catch(commanderror &ex) {
|
||||
printf("Unexpected command error: %.8lx\n", ex.stat);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
OMAP35x_SDMMC::~OMAP35x_SDMMC() {
|
||||
global::intc->disable_int(irq_);
|
||||
}
|
||||
|
||||
unsigned OMAP35x_SDMMC::getBlockSize() const {
|
||||
return blockSize_;
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::write(unsigned pos, char const* data, unsigned count) {
|
||||
throw ex::error{ENOTSUP};
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::read(unsigned pos, char *data, unsigned count) {
|
||||
throw ex::error{ENOTSUP};
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::command(unsigned cmd, uint32_t arg) {
|
||||
assert(intSem_.getCount() == 0);
|
||||
|
||||
if(cmd&0x100) {
|
||||
acmd();
|
||||
cmd &= 0xff;
|
||||
}
|
||||
assert(cmd <= 63);
|
||||
|
||||
while(r_pstate() & (1<<0)); // Wait for command line free
|
||||
|
||||
r_con() &= ~(1<<6) & ~(1<<3);
|
||||
r_csre() = 0x0;
|
||||
|
||||
r_blk() &= 0x0000f800; // No data transfer
|
||||
|
||||
r_stat() = 0xffffffff;
|
||||
|
||||
r_arg() = arg;
|
||||
|
||||
uint32_t creg = (cmd<<24);
|
||||
if(cmd == 12)
|
||||
creg |= (0x3<<22);
|
||||
creg |= // (1<<19) |
|
||||
(0<<16);
|
||||
|
||||
r_cmd() = creg;
|
||||
|
||||
intSem_.acquire();
|
||||
|
||||
if(intStat_ & (1<<16)) {
|
||||
cmd_reset();
|
||||
throw ex::timeout{};
|
||||
} else if(intStat_ & (1<<15)) {
|
||||
throw commanderror{intStat_};
|
||||
}
|
||||
|
||||
assert(intStat_ & (1<<0));
|
||||
}
|
||||
|
||||
uint32_t OMAP35x_SDMMC::command_r1(unsigned cmd, uint32_t arg) {
|
||||
assert(intSem_.getCount() == 0);
|
||||
|
||||
if(cmd&0x100) {
|
||||
acmd();
|
||||
cmd &= 0xff;
|
||||
}
|
||||
assert(cmd <= 63);
|
||||
|
||||
while(r_pstate() & (1<<0)); // Wait for command line free
|
||||
|
||||
r_con() &= ~(1<<6) & ~(1<<3);
|
||||
r_csre() = 0x0;
|
||||
|
||||
r_blk() &= 0x0000f800; // No data transfer
|
||||
|
||||
r_stat() = 0xffffffff;
|
||||
|
||||
r_arg() = arg;
|
||||
|
||||
uint32_t creg = (cmd<<24);
|
||||
if(cmd == 12)
|
||||
creg |= (0x3<<22);
|
||||
if(!(cmd == 41)) // These commands do not respond with CRC, otherwise
|
||||
creg |= (1<<19); // CRC check enable
|
||||
if(!(cmd == 41)) // These commands do not respond with command index, otherwise
|
||||
creg |= (1<<20); // Index check enable
|
||||
creg |= (2<<16);
|
||||
|
||||
r_cmd() = creg;
|
||||
|
||||
intSem_.acquire();
|
||||
|
||||
if(intStat_ & (1<<16)) {
|
||||
cmd_reset();
|
||||
throw ex::timeout{};
|
||||
} else if(intStat_ & (1<<15)) {
|
||||
throw commanderror{intStat_};
|
||||
}
|
||||
|
||||
assert(intStat_ & (1<<0));
|
||||
|
||||
return r_rsp10();
|
||||
}
|
||||
|
||||
uint32_t OMAP35x_SDMMC::command_r1b(unsigned cmd, uint32_t arg) {
|
||||
assert(intSem_.getCount() == 0);
|
||||
|
||||
if(cmd&0x100) {
|
||||
acmd();
|
||||
cmd &= 0xff;
|
||||
}
|
||||
assert(cmd <= 63);
|
||||
|
||||
while(r_pstate() & (1<<0)); // Wait for command line free
|
||||
|
||||
r_sysctl() = (r_sysctl() & ~(0xf<<16)) | (readTimeout_<<16); // Use read timeout
|
||||
|
||||
r_con() &= ~(1<<6) & ~(1<<3);
|
||||
r_csre() = 0x0;
|
||||
|
||||
r_blk() &= 0x0000f800; // No data transfer
|
||||
|
||||
r_stat() = 0xffffffff;
|
||||
|
||||
r_arg() = arg;
|
||||
|
||||
uint32_t creg = (cmd<<24);
|
||||
if(cmd == 12)
|
||||
creg |= (0x3<<22);
|
||||
creg |= (1<<19) | (1<<20); // CRC check, index check enable
|
||||
creg |= // (1<<19) |
|
||||
(3<<16);
|
||||
|
||||
r_cmd() = creg;
|
||||
|
||||
intSem_.acquire();
|
||||
|
||||
if(intStat_ & (1<<16)) {
|
||||
cmd_reset();
|
||||
throw ex::timeout{};
|
||||
} else if(intStat_ & (1<<15)) {
|
||||
throw commanderror{intStat_};
|
||||
}
|
||||
|
||||
assert(intStat_ & (1<<0));
|
||||
|
||||
return r_rsp10();
|
||||
}
|
||||
|
||||
std::array<uint32_t, 4> OMAP35x_SDMMC::command_r2(unsigned cmd, uint32_t arg) {
|
||||
assert(intSem_.getCount() == 0);
|
||||
|
||||
if(cmd&0x100) {
|
||||
acmd();
|
||||
cmd &= 0xff;
|
||||
}
|
||||
assert(cmd <= 63);
|
||||
|
||||
while(r_pstate() & (1<<0)); // Wait for command line free
|
||||
|
||||
r_con() &= ~(1<<6) & ~(1<<3);
|
||||
r_csre() = 0x0;
|
||||
|
||||
r_blk() &= 0x0000f800; // No data transfer
|
||||
|
||||
r_stat() = 0xffffffff;
|
||||
|
||||
r_arg() = arg;
|
||||
|
||||
uint32_t creg = (cmd<<24);
|
||||
if(cmd == 12)
|
||||
creg |= (0x3<<22);
|
||||
creg |= (1<<19); // CRC check enable
|
||||
creg |= (1<<16);
|
||||
|
||||
r_cmd() = creg;
|
||||
|
||||
intSem_.acquire();
|
||||
|
||||
if(intStat_ & (1<<16)) {
|
||||
cmd_reset();
|
||||
throw ex::timeout{};
|
||||
} else if(intStat_ & (1<<15)) {
|
||||
throw commanderror{intStat_};
|
||||
}
|
||||
|
||||
assert(intStat_ & (1<<0));
|
||||
|
||||
return std::array<uint32_t, 4>{r_rsp10(), r_rsp32(), r_rsp54(), r_rsp76()};
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::int_handler() {
|
||||
intStat_ = r_stat();
|
||||
r_stat() = intStat_; // Read and clear status bits
|
||||
|
||||
intSem_.release();
|
||||
|
||||
global::prcm->clear_wake_core(24);
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::acmd() {
|
||||
uint32_t resp = command_r1(55, rca_<<16);
|
||||
if(!(resp & (1<<5))) { // Not in ACMD mode
|
||||
// WTF?
|
||||
printf("Error: CMD55 response without APP_CMD set (%.8lx)\n", resp);
|
||||
throw ex::error{EIO};
|
||||
}
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::cmd_reset() {
|
||||
r_sysctl() |= (1<<25);
|
||||
while(r_sysctl() & (1<<25));
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::dat_reset() {
|
||||
r_sysctl() |= (1<<26);
|
||||
while(r_sysctl() & (1<<26));
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::changeClock(unsigned cdiv) {
|
||||
assert(cdiv <= 0x3ff);
|
||||
|
||||
r_sysctl() &= ~(1<<2) & ~(1<<0); // Clock disable
|
||||
|
||||
r_sysctl() &= ~(0x3ff<<6); // clear clock divider
|
||||
r_sysctl() |= (cdiv<<6) | (1<<0); // Set new divider and start internal clock
|
||||
while(!(r_sysctl() & (1<<1))); // Wait until clock stable
|
||||
|
||||
r_sysctl() |= (1<<2); // Clock reenable
|
||||
}
|
||||
|
||||
char const* OMAP35x_SDMMC::cardTypeToString() const {
|
||||
switch(cardType_) {
|
||||
case CardType::sdxc:
|
||||
return "SDxc";
|
||||
case CardType::sdhc:
|
||||
return "SDhc";
|
||||
case CardType::sdv2:
|
||||
return "SDsc v2";
|
||||
case CardType::sdv1:
|
||||
return "SDsc v1";
|
||||
case CardType::mmc:
|
||||
return "MMC";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void OMAP35x_SDMMC::test_data(unsigned block) {
|
||||
assert(intSem_.getCount() == 0);
|
||||
|
||||
std::vector<uint32_t> buffer(128, 0);
|
||||
|
||||
while(r_pstate() & (1<<0)); // Wait for command line free
|
||||
while(r_pstate() & (1<<1)); // Wait for data line free
|
||||
|
||||
r_sysctl() = (r_sysctl() & ~(0xf<<16)) | (readTimeout_<<16); // Use read timeout
|
||||
|
||||
r_con() &= ~(1<<6) & ~(1<<3);
|
||||
r_csre() = 0x0;
|
||||
|
||||
r_blk() &= 0x0000f800;
|
||||
r_blk() |= 0x00010000 | blockSize_;
|
||||
|
||||
r_stat() = 0xffffffff;
|
||||
|
||||
if((cardType_ == CardType::sdxc) || (cardType_ == CardType::sdhc))
|
||||
r_arg() = block;
|
||||
else
|
||||
r_arg() = block*blockSize_;
|
||||
|
||||
uint32_t creg = (17<<24); // CMD17 read single block
|
||||
creg |= (1<<21) | (1<<20) | (1<<19) | (2<<16) | (1<<4);
|
||||
|
||||
r_cmd() = creg;
|
||||
|
||||
intSem_.acquire();
|
||||
|
||||
if(intStat_ & (1<<16)) {
|
||||
cmd_reset();
|
||||
throw ex::timeout{};
|
||||
} else if(intStat_ & (1<<15)) {
|
||||
throw commanderror{intStat_};
|
||||
}
|
||||
|
||||
assert(intStat_ & (1<<0));
|
||||
|
||||
// TODO: DMA for data transfers
|
||||
unsigned pos = 0;
|
||||
while(true) {
|
||||
uint32_t stat = r_stat() | intStat_; // Make sure no status bits get 'lost'
|
||||
intStat_ = 0;
|
||||
if(stat & (1<<15)) {
|
||||
dat_reset();
|
||||
throw dataerror{stat};
|
||||
} else if(stat & (1<<1)) {
|
||||
break;
|
||||
} else if(stat & (1<<5)) {
|
||||
assert(pos < 128);
|
||||
buffer[pos++] = r_data();
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0;i < 32;++i) {
|
||||
for(int j = 0;j < 4;++j) {
|
||||
printf("%.2lx %.2lx %.2lx %.2lx ",
|
||||
buffer[i*4+j]&0xff,
|
||||
(buffer[i*4+j]>>8)&0xff,
|
||||
(buffer[i*4+j]>>16)&0xff,
|
||||
(buffer[i*4+j]>>24)&0xff);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
}
|
||||
90
drv_omap35x_sdmmc.hh
Normal file
90
drv_omap35x_sdmmc.hh
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef _DRV_OMAP35X_SDMMC_HH_
|
||||
#define _DRV_OMAP35X_SDMMC_HH_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "interfaces.hh"
|
||||
#include "exceptions.hh"
|
||||
#include "mmio.hh"
|
||||
#include "util.hh"
|
||||
#include "scheduler.hh"
|
||||
|
||||
class OMAP35x_SDMMC : public IBlockDevice {
|
||||
public:
|
||||
OMAP35x_SDMMC(uintptr_t base, unsigned irq, unsigned drq_rx, unsigned drq_tx);
|
||||
|
||||
~OMAP35x_SDMMC();
|
||||
|
||||
// Get the (logical) block size of the device
|
||||
virtual unsigned getBlockSize() const;
|
||||
|
||||
// Write getBlockSize()*'count' bytes of data from 'data' to the device starting at block 'pos'
|
||||
/* Throw an ex::error on error, containing the errno describing the error. The contents of the
|
||||
block device from 'pos' to 'pos'+'count' are undefined after an exception */
|
||||
virtual void write(unsigned pos, char const* data, unsigned count);
|
||||
// Read getBlockSize()*'count' bytes of data from the device starting at block 'pos' to 'data'
|
||||
/* Throw an ex::error on error, containing the errno describing the error. The contents of 'data'
|
||||
are undefined after an exception */
|
||||
virtual void read(unsigned pos, char *data, unsigned count);
|
||||
|
||||
private:
|
||||
void command(unsigned cmd, uint32_t arg);
|
||||
uint32_t command_r1(unsigned cmd, uint32_t arg);
|
||||
uint32_t command_r1b(unsigned cmd, uint32_t arg);
|
||||
std::array<uint32_t, 4> command_r2(unsigned cmd, uint32_t arg);
|
||||
|
||||
void int_handler();
|
||||
|
||||
void test_data(unsigned block);
|
||||
|
||||
void acmd();
|
||||
void cmd_reset();
|
||||
void dat_reset();
|
||||
void changeClock(unsigned cdiv);
|
||||
|
||||
enum class CardType {
|
||||
sdxc,
|
||||
sdhc,
|
||||
sdv2,
|
||||
sdv1,
|
||||
mmc
|
||||
};
|
||||
|
||||
char const* cardTypeToString() const;
|
||||
|
||||
MMIO_alloc base_;
|
||||
unsigned irq_, drq_rx_, drq_tx_;
|
||||
CardType cardType_;
|
||||
unsigned blockSize_, sectorSize_, cardBlocks_;
|
||||
unsigned rca_;
|
||||
unsigned readTimeout_, writeTimeout_;
|
||||
Scheduler::Semaphore intSem_;
|
||||
uint32_t intStat_;
|
||||
|
||||
uint32_t volatile& r_sysconfig() { return _reg32(base_.get_virt(), 0x10); }
|
||||
uint32_t volatile& r_sysstatus() { return _reg32(base_.get_virt(), 0x14); }
|
||||
uint32_t volatile& r_csre() { return _reg32(base_.get_virt(), 0x24); }
|
||||
uint32_t volatile& r_systest() { return _reg32(base_.get_virt(), 0x28); }
|
||||
uint32_t volatile& r_con() { return _reg32(base_.get_virt(), 0x2c); }
|
||||
uint32_t volatile& r_pwcnt() { return _reg32(base_.get_virt(), 0x30); }
|
||||
uint32_t volatile& r_blk() { return _reg32(base_.get_virt(), 0x104); }
|
||||
uint32_t volatile& r_arg() { return _reg32(base_.get_virt(), 0x108); }
|
||||
uint32_t volatile& r_cmd() { return _reg32(base_.get_virt(), 0x10c); }
|
||||
uint32_t volatile& r_rsp10() { return _reg32(base_.get_virt(), 0x110); }
|
||||
uint32_t volatile& r_rsp32() { return _reg32(base_.get_virt(), 0x114); }
|
||||
uint32_t volatile& r_rsp54() { return _reg32(base_.get_virt(), 0x118); }
|
||||
uint32_t volatile& r_rsp76() { return _reg32(base_.get_virt(), 0x11c); }
|
||||
uint32_t volatile& r_data() { return _reg32(base_.get_virt(), 0x120); }
|
||||
uint32_t volatile& r_pstate() { return _reg32(base_.get_virt(), 0x124); }
|
||||
uint32_t volatile& r_hctl() { return _reg32(base_.get_virt(), 0x128); }
|
||||
uint32_t volatile& r_sysctl() { return _reg32(base_.get_virt(), 0x12c); }
|
||||
uint32_t volatile& r_stat() { return _reg32(base_.get_virt(), 0x130); }
|
||||
uint32_t volatile& r_ie() { return _reg32(base_.get_virt(), 0x134); }
|
||||
uint32_t volatile& r_ise() { return _reg32(base_.get_virt(), 0x138); }
|
||||
uint32_t volatile& r_ac12() { return _reg32(base_.get_virt(), 0x13c); }
|
||||
uint32_t volatile& r_capa() { return _reg32(base_.get_virt(), 0x140); }
|
||||
uint32_t volatile& r_cur_capa() { return _reg32(base_.get_virt(), 0x148); }
|
||||
uint32_t volatile& r_rev() { return _reg32(base_.get_virt(), 0x1fc); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,15 +1,36 @@
|
||||
#ifndef _EXCEPTIONS_HH_
|
||||
#define _EXCEPTIONS_HH_
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace ex {
|
||||
// An unexcpected fatal error occured
|
||||
class error {
|
||||
public:
|
||||
error(int eno = 0) : eno_(eno) {
|
||||
}
|
||||
|
||||
int getErrno() const {
|
||||
return eno_;
|
||||
}
|
||||
|
||||
private:
|
||||
int eno_;
|
||||
};
|
||||
|
||||
// A memory allocation, or virtual address space allocation, failed
|
||||
class bad_alloc{};
|
||||
class bad_alloc : public error {
|
||||
public:
|
||||
bad_alloc() : error(ENOMEM) {
|
||||
}
|
||||
};
|
||||
|
||||
// An operation timed out
|
||||
class timeout{};
|
||||
|
||||
// An unexcpected fatal error occured
|
||||
class error{};
|
||||
class timeout : public error {
|
||||
public:
|
||||
timeout() : error(EAGAIN) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -190,7 +190,7 @@ SECTIONS
|
||||
__stack_int = .;
|
||||
. = . + 0x4000; /* 16KiB syscall stack */
|
||||
__stack_syscall = .;
|
||||
. = . + 0x8000; /* 32KiB kernel startup stack */
|
||||
. = . + 0x10000; /* 64KiB kernel startup stack */
|
||||
__stack = .;
|
||||
|
||||
__end__ = . ;
|
||||
@@ -222,8 +222,11 @@ SECTIONS
|
||||
/* Virtual address space for kernel scratchpad */
|
||||
__scratch_start = 0xc0000000;
|
||||
/* Map:
|
||||
0xc0000000 - 0xc0800000 Kernel L2 pagetables
|
||||
0xc0000000 - 0xc07fffff Kernel L2 pagetables
|
||||
0xc0800000 - 0xc0ffffff Kernel pagecache
|
||||
*/
|
||||
__pc_start = 0xc0800000;
|
||||
__pc_end = 0xc1000000;
|
||||
__scratch_end = 0xe0000000;
|
||||
/* Virtual address space for MMIO */
|
||||
__io_start = 0xe0000000;
|
||||
|
||||
@@ -5,15 +5,20 @@
|
||||
|
||||
class OMAP35x_prcm;
|
||||
class OMAP35x_intc;
|
||||
class OMAP35x_GPT;
|
||||
|
||||
class Scheduler;
|
||||
class PageCache;
|
||||
|
||||
namespace global {
|
||||
extern OMAP35x_prcm *prcm;
|
||||
extern OMAP35x_intc *intc;
|
||||
|
||||
extern OMAP35x_GPT *ticktimer, *ustimer;
|
||||
|
||||
extern ICharacterDevice *console;
|
||||
extern Scheduler *scheduler;
|
||||
extern PageCache *pagecache;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,21 @@ public:
|
||||
virtual int read(char *buf, int const& len) = 0;
|
||||
};
|
||||
|
||||
class IBlockDevice {
|
||||
public:
|
||||
// Get the (logical) block size of the device
|
||||
virtual unsigned getBlockSize() const = 0;
|
||||
|
||||
// Write getBlockSize()*'count' bytes of data from 'data' to the device starting at block 'pos'
|
||||
/* Throw an Error on error, containing the errno describing the error. The contents of the block
|
||||
device from 'pos' to 'pos'+'count' are undefined after an exception */
|
||||
virtual void write(unsigned pos, char const* data, unsigned count) = 0;
|
||||
// Read getBlockSize()*'count' bytes of data from the device starting at block 'pos' to 'data'
|
||||
/* Throw an Error on error, containing the errno describing the error. The contents of 'data'
|
||||
are undefined after an exception */
|
||||
virtual void read(unsigned pos, char *data, unsigned count) = 0;
|
||||
};
|
||||
|
||||
class II2C {
|
||||
public:
|
||||
virtual void reg_write(uint8_t slaveid, uint8_t reg, uint8_t value) = 0;
|
||||
|
||||
36
main.cc
36
main.cc
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "drv_omap35x_gpt.hh"
|
||||
#include "drv_omap35x_i2c.hh"
|
||||
#include "drv_omap35x_sdmmc.hh"
|
||||
#include "drv_tps65950.hh"
|
||||
#include "omap35x.hh"
|
||||
#include "omap35x_intc.hh"
|
||||
@@ -16,14 +17,18 @@
|
||||
#include "scheduler.hh"
|
||||
#include "cortexa8.hh"
|
||||
#include "uart.hh"
|
||||
#include "pagecache.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
namespace global {
|
||||
OMAP35x_prcm* prcm = nullptr;
|
||||
OMAP35x_intc* intc = nullptr;
|
||||
OMAP35x_GPT* ticktimer = nullptr;
|
||||
OMAP35x_GPT* ustimer = nullptr;
|
||||
|
||||
ICharacterDevice* console = nullptr;
|
||||
Scheduler* scheduler = nullptr;
|
||||
PageCache *pagecache = nullptr;
|
||||
}
|
||||
|
||||
void sbrk_stats();
|
||||
@@ -45,6 +50,9 @@ int main(int argc, char* argv[]) {
|
||||
// Initialize memory
|
||||
cortexa8::enable_dcache();
|
||||
cortexa8::enable_icache();
|
||||
|
||||
cortexa8::errata();
|
||||
|
||||
cortexa8::init_mmu();
|
||||
|
||||
// Enable early console
|
||||
@@ -81,9 +89,12 @@ int main(int argc, char* argv[]) {
|
||||
if(chipInfo.running_on_qemu())
|
||||
printf("QEMU detected, avoiding bugs...\n");
|
||||
|
||||
OMAP35x_GPT gpt2{0x49032000, 38};
|
||||
global::ticktimer = new OMAP35x_GPT{0x49032000, 38};
|
||||
|
||||
gpt2.ticktimer(&tickfunc);
|
||||
global::ticktimer->ticktimer(&tickfunc);
|
||||
|
||||
global::ustimer = new OMAP35x_GPT{0x49034000, 39};
|
||||
global::ustimer->ustimer();
|
||||
|
||||
// Configure TPS65950 (power control etc.)
|
||||
OMAP35x_I2C i2c1{0x48070000};
|
||||
@@ -101,6 +112,26 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
global::console = new UART{0x49020000, 74};
|
||||
|
||||
global::pagecache = new PageCache{};
|
||||
|
||||
OMAP35x_SDMMC *sdmmc1;
|
||||
try {
|
||||
sdmmc1 = new OMAP35x_SDMMC{0x4809c000, 83, 61, 60};
|
||||
} catch(ex::error &ex) {
|
||||
printf("SD initialization failed: %s (%d)\n", strerror(ex.getErrno()), ex.getErrno());
|
||||
}
|
||||
|
||||
try {
|
||||
auto test = global::pagecache->readAllocate(*sdmmc1, 0, 1);
|
||||
for(int i = 0;i < 32;++i) {
|
||||
for(int j = 0;j < 16;++j)
|
||||
printf("%.2hhx ", test.data()[i*16+j]);
|
||||
printf("\n");
|
||||
}
|
||||
} catch(ex::error &ex) {
|
||||
printf("SD read failed: %s (%d)\n", strerror(ex.getErrno()), ex.getErrno());
|
||||
}
|
||||
|
||||
while(1) {
|
||||
char buf[256];
|
||||
if(fgets(buf, 256, stdin))
|
||||
@@ -124,4 +155,3 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
24
mm.cc
24
mm.cc
@@ -1,12 +1,13 @@
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include "cortexa8.hh"
|
||||
#include "phys_mm.hh"
|
||||
#include "util.hh"
|
||||
#include "mm.hh"
|
||||
|
||||
extern uint32_t __scratch_start, __scratch_end, __io_start, __io_end, __heap_start, __heap_end;
|
||||
extern uint32_t __scratch_start, __scratch_end, __io_start, __io_end, __heap_start, __heap_end, __pc_start, __pc_end;
|
||||
|
||||
static const uintptr_t scratch_start = (uintptr_t)&__scratch_start;
|
||||
static const uintptr_t scratch_end = (uintptr_t)&__scratch_end;
|
||||
@@ -14,8 +15,11 @@ static const uintptr_t io_start = (uintptr_t)&__io_start;
|
||||
static const uintptr_t io_end = (uintptr_t)&__io_end;
|
||||
static const uintptr_t heap_start = (uintptr_t)&__heap_start;
|
||||
static const uintptr_t heap_end = (uintptr_t)&__heap_end;
|
||||
static const uintptr_t pc_start = (uintptr_t)&__pc_start;
|
||||
static const uintptr_t pc_end = (uintptr_t)&__pc_end;
|
||||
|
||||
static uintptr_t heap_top, io_top;
|
||||
|
||||
static uintptr_t heap_top, io_top, pc_top;
|
||||
|
||||
void mm::init() {
|
||||
heap_top = phys_mm::get_end_of_kernel_alloc();
|
||||
@@ -28,6 +32,7 @@ void mm::init() {
|
||||
cortexa8::map_pages(heap_start_align, heap_start_align, (heap_top-heap_start)/4096);
|
||||
|
||||
io_top = io_start;
|
||||
pc_top = pc_start;
|
||||
}
|
||||
|
||||
uintptr_t mm::virtalloc_io(unsigned pages) {
|
||||
@@ -58,3 +63,18 @@ uintptr_t mm::grow_heap(unsigned pages) {
|
||||
uintptr_t mm::get_heap_end() {
|
||||
return heap_top;
|
||||
}
|
||||
|
||||
|
||||
uintptr_t mm::virtalloc_pc(unsigned pages) {
|
||||
if(pc_top+0x1000*pages >= pc_end)
|
||||
throw bad_alloc{};
|
||||
|
||||
uintptr_t ret = pc_top;
|
||||
pc_top += 0x1000*pages;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mm::virtfree_pc(uintptr_t base) {
|
||||
printf("NYI: virtfree_pc\n");
|
||||
}
|
||||
|
||||
4
mm.hh
4
mm.hh
@@ -13,6 +13,10 @@ namespace mm {
|
||||
// Allocate 'pages' pages of virtual address space in the I/O region
|
||||
uintptr_t virtalloc_io(unsigned pages);
|
||||
|
||||
// Allocate in pagecache region
|
||||
uintptr_t virtalloc_pc(unsigned pages);
|
||||
void virtfree_pc(uintptr_t base);
|
||||
|
||||
// Grow the kernel heap by 'pages' pages
|
||||
// Allocate and map the desired amount of memory
|
||||
// Return the new heap end
|
||||
|
||||
@@ -53,6 +53,7 @@ private:
|
||||
|
||||
template<class T>
|
||||
class ScopedLock {
|
||||
public:
|
||||
ScopedLock(T& mutex) : mutex_(mutex), done_(false) {
|
||||
mutex_.lock();
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@ public:
|
||||
r_mpugrpsel_per() |= (1<<3); // GPT2 wakes up MPU
|
||||
r_wken_per() |= (1<<3); // GPT2 wake up enable
|
||||
|
||||
// Prepare GPTIMER3
|
||||
r_fclken_per() |= (1<<4); // Enable GPT3 fclk
|
||||
r_iclken_per() |= (1<<4); // Enable GPT3 iclk
|
||||
r_autoidle_per() |= (1<<4); // Enable GPT3 iclk autoidle
|
||||
r_clksel_per() |= (1<<1); // GPT3 fclk = SYS_CLK
|
||||
r_mpugrpsel_per() &= ~(1<<4); // GPT3 does not wake up MPU
|
||||
r_wken_per() &= ~(1<<4); // GPT3 wake up disable
|
||||
|
||||
// Prepare UART3
|
||||
r_autoidle_per() |= (1<<11); // Enable UART3 iclk autoidle
|
||||
r_mpugrpsel_per() |= (1<<11); // UART3 wakes up MPU
|
||||
@@ -76,6 +84,13 @@ public:
|
||||
r_iclken1_core() |= (1<<15);
|
||||
r_autoidle1_core() |= (1<<15);
|
||||
r_wken1_core() &= ~(1<15);
|
||||
|
||||
// Prepare MMC1
|
||||
r_fclken1_core() |= (1<<24);
|
||||
r_iclken1_core() |= (1<<24);
|
||||
r_autoidle1_core() |= (1<<24);
|
||||
r_wken1_core() |= (1<<24);
|
||||
r_mpugrpsel1_core() |= (1<<24);
|
||||
}
|
||||
|
||||
void clear_wake_per(int n) {
|
||||
@@ -83,6 +98,14 @@ public:
|
||||
r_wkst_per() = (1<<n);
|
||||
}
|
||||
|
||||
void clear_wake_core(int n) {
|
||||
assert(n >= 0 && n <= 63);
|
||||
if(n&32)
|
||||
r_wkst3_core() = (1<<(n&0x1f));
|
||||
else
|
||||
r_wkst1_core() = (1<<n);
|
||||
}
|
||||
|
||||
void set_cpu_opp(int opp) {
|
||||
assert(opp >= 1 && opp <= 6);
|
||||
|
||||
@@ -319,6 +342,10 @@ void OMAP35x_prcm::clear_wake_per(int n) {
|
||||
impl_->clear_wake_per(n);
|
||||
}
|
||||
|
||||
void OMAP35x_prcm::clear_wake_core(int n) {
|
||||
impl_->clear_wake_core(n);
|
||||
}
|
||||
|
||||
void OMAP35x_prcm::set_cpu_opp(int opp) {
|
||||
impl_->set_cpu_opp(opp);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
void enable_peripherals();
|
||||
|
||||
void clear_wake_per(int n);
|
||||
void clear_wake_core(int n);
|
||||
|
||||
void set_cpu_opp(int opp);
|
||||
void set_core_opp(int opp, int config);
|
||||
|
||||
166
pagecache.cc
Normal file
166
pagecache.cc
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include "mm.hh"
|
||||
#include "phys_mm.hh"
|
||||
#include "pagecache.hh"
|
||||
|
||||
PageCache::PageCache(int maxPages) : maxPages_(maxPages), curPages_(0) {
|
||||
}
|
||||
|
||||
PageCache::~PageCache() {
|
||||
for(auto& devEntry : allocs_) { // For each known device
|
||||
for(auto& allocEntry : devEntry.second) {
|
||||
freeAlloc(allocEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PageCache::Handle PageCache::readAllocate(IBlockDevice& dev, unsigned pos, unsigned count) {
|
||||
ScopedLock<Mutex> lock(mutex_);
|
||||
|
||||
_allocInfo *alloc = searchAlloc(&dev, pos, count);
|
||||
if(!alloc) {
|
||||
_allocInfo newAlloc;
|
||||
buildAlloc(newAlloc, count);
|
||||
|
||||
newAlloc.dev = &dev;
|
||||
newAlloc.start = pos;
|
||||
|
||||
try {
|
||||
dev.read(newAlloc.start, newAlloc.virtMem, newAlloc.len);
|
||||
allocs_[&dev].push_back(newAlloc);
|
||||
alloc = &allocs_[&dev].back();
|
||||
} catch(...) {
|
||||
freeAlloc(newAlloc);
|
||||
throw;
|
||||
}
|
||||
curPages_ += count;
|
||||
}
|
||||
|
||||
return Handle(alloc, (pos - alloc->start));
|
||||
}
|
||||
|
||||
PageCache::Handle PageCache::writeAllocate(IBlockDevice& dev, unsigned pos, unsigned count) {
|
||||
ScopedLock<Mutex> lock(mutex_);
|
||||
|
||||
_allocInfo *alloc = searchAlloc(&dev, pos, count);
|
||||
if(!alloc) {
|
||||
_allocInfo newAlloc;
|
||||
buildAlloc(newAlloc, count);
|
||||
|
||||
newAlloc.dev = &dev;
|
||||
newAlloc.start = pos;
|
||||
|
||||
try {
|
||||
allocs_[&dev].push_back(newAlloc);
|
||||
alloc = &allocs_[&dev].back();
|
||||
} catch(...) {
|
||||
freeAlloc(newAlloc);
|
||||
throw;
|
||||
}
|
||||
curPages_ += count;
|
||||
}
|
||||
|
||||
return Handle(alloc, (pos - alloc->start));
|
||||
}
|
||||
|
||||
void PageCache::eject(int lengthHint) {
|
||||
ScopedLock<Mutex> lock(mutex_);
|
||||
_eject(lengthHint);
|
||||
}
|
||||
|
||||
|
||||
void PageCache::freeAlloc(_allocInfo& alloc) {
|
||||
assert(!alloc.refs);
|
||||
if(alloc.dirty && alloc.dev)
|
||||
alloc.dev->write(alloc.start, alloc.virtMem, alloc.len);
|
||||
|
||||
if(alloc.virtMem) {
|
||||
cortexa8::unmap_pages((uintptr_t)alloc.virtMem, alloc.len);
|
||||
mm::virtfree_pc((uintptr_t)alloc.virtMem);
|
||||
}
|
||||
if(alloc.physMem)
|
||||
phys_mm::free(alloc.physMem);
|
||||
}
|
||||
|
||||
void PageCache::buildAlloc(_allocInfo& alloc, unsigned len) {
|
||||
if((maxPages_ > 0) && (curPages_ + len > (unsigned)maxPages_)) {
|
||||
_eject(curPages_+len-maxPages_);
|
||||
if(curPages_ + len > (unsigned)maxPages_)
|
||||
throw ex::bad_alloc{};
|
||||
}
|
||||
|
||||
alloc.dirty = false;
|
||||
alloc.len = len;
|
||||
alloc.dev = nullptr;
|
||||
alloc.physMem = 0;
|
||||
alloc.virtMem = nullptr;
|
||||
alloc.refs = 0;
|
||||
|
||||
bool retry = true;
|
||||
while(true) {
|
||||
try {
|
||||
if(!alloc.physMem)
|
||||
alloc.physMem = phys_mm::alloc(len);
|
||||
if(!alloc.virtMem)
|
||||
alloc.virtMem = (char*)mm::virtalloc_pc(len);
|
||||
cortexa8::map_pages((uintptr_t)alloc.virtMem, alloc.physMem, len);
|
||||
break;
|
||||
} catch(ex::bad_alloc &ex) {
|
||||
if(retry) {
|
||||
_eject(len);
|
||||
retry = false;
|
||||
} else {
|
||||
freeAlloc(alloc);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PageCache::_allocInfo* PageCache::searchAlloc(IBlockDevice* dev, unsigned start, unsigned len) {
|
||||
auto it = allocs_.find(dev);
|
||||
if(it == allocs_.end())
|
||||
return nullptr;
|
||||
|
||||
for(auto& allocEntry : it->second) {
|
||||
if((allocEntry.start <= start) &&
|
||||
((allocEntry.start + allocEntry.len) >= (start + len)))
|
||||
return &allocEntry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PageCache::_eject(int lengthHint) {
|
||||
printf("NYI: PageCache::_eject\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
PageCache::Handle::Handle(_allocInfo* alloc, int ofs) : alloc_(alloc), ofs_(ofs) {
|
||||
++alloc_->refs;
|
||||
}
|
||||
|
||||
PageCache::Handle::Handle(Handle const& copy) : alloc_(copy.alloc_), ofs_(copy.ofs_) {
|
||||
++alloc_->refs;
|
||||
}
|
||||
|
||||
PageCache::Handle& PageCache::Handle::operator=(Handle const& copy) {
|
||||
if(alloc_)
|
||||
--alloc_->refs;
|
||||
|
||||
alloc_ = copy.alloc_;
|
||||
ofs_ = copy.ofs_;
|
||||
|
||||
if(alloc_)
|
||||
++alloc_->refs;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PageCache::Handle::~Handle() {
|
||||
--alloc_->refs;
|
||||
}
|
||||
80
pagecache.hh
Normal file
80
pagecache.hh
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef _PAGECACHE_HH_
|
||||
#define _PAGECACHE_HH_
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include "interfaces.hh"
|
||||
#include "mtprim.hh"
|
||||
|
||||
class PageCache {
|
||||
private:
|
||||
struct _allocInfo;
|
||||
|
||||
public:
|
||||
class Handle {
|
||||
public:
|
||||
Handle() : alloc_(nullptr) {
|
||||
}
|
||||
Handle(Handle const& copy);
|
||||
~Handle();
|
||||
|
||||
Handle& operator=(Handle const& copy);
|
||||
|
||||
char *data() {
|
||||
return alloc_->virtMem + ofs_*4096;
|
||||
}
|
||||
|
||||
void setDirty();
|
||||
void flush();
|
||||
|
||||
private:
|
||||
Handle(_allocInfo* alloc, int ofs);
|
||||
|
||||
_allocInfo *alloc_;
|
||||
int ofs_;
|
||||
|
||||
friend class PageCache;
|
||||
};
|
||||
|
||||
PageCache(int maxPages = -1);
|
||||
~PageCache();
|
||||
|
||||
// Allocate memory in the cache and fill it with data from dev
|
||||
// If the data already exists in the cache a Handle to that entry may be returned
|
||||
Handle readAllocate(IBlockDevice& dev, unsigned pos, unsigned count);
|
||||
|
||||
// Allocate memory in the cache without reading from device
|
||||
// This prevents useless reads if you're going to overwrite the entire range anyways
|
||||
// If the data already exists in the cache a Handle to that entry may be returned
|
||||
Handle writeAllocate(IBlockDevice& dev, unsigned pos, unsigned count);
|
||||
|
||||
// Flush all dirty cached pages to their block devices
|
||||
void sync();
|
||||
|
||||
// Eject pages from cache. If lengthHint is > 0, try to free at least that many pages
|
||||
void eject(int lengthHint);
|
||||
|
||||
private:
|
||||
int maxPages_, curPages_;
|
||||
Mutex mutex_;
|
||||
|
||||
struct _allocInfo {
|
||||
IBlockDevice *dev;
|
||||
unsigned start;
|
||||
unsigned len;
|
||||
uintptr_t physMem;
|
||||
char *virtMem;
|
||||
bool dirty;
|
||||
int refs;
|
||||
};
|
||||
|
||||
std::map<IBlockDevice*, std::list<_allocInfo> > allocs_;
|
||||
|
||||
void freeAlloc(_allocInfo& alloc);
|
||||
void buildAlloc(_allocInfo& alloc, unsigned len);
|
||||
_allocInfo* searchAlloc(IBlockDevice* dev, unsigned start, unsigned len);
|
||||
void _eject(int lengthHint);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -164,6 +164,10 @@ public:
|
||||
return (count_ < 0);
|
||||
}
|
||||
|
||||
int getCount() const {
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
int count_;
|
||||
|
||||
|
||||
35
sleep.cc
Normal file
35
sleep.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <cassert>
|
||||
#include <errno.h>
|
||||
|
||||
#include "globals.hh"
|
||||
#include "exceptions.hh"
|
||||
#include "drv_omap35x_gpt.hh"
|
||||
#include "cortexa8.hh"
|
||||
#include "sleep.hh"
|
||||
|
||||
#define MAX_USLEEP (4294967295/USTIMER_PER_US)
|
||||
|
||||
// "busy" sleep for at least 'us' microseconds with microsecond granularity
|
||||
void usleep(uint32_t us) {
|
||||
if(us > MAX_USLEEP)
|
||||
sleep(us/1000);
|
||||
|
||||
uint32_t start = global::ustimer->getCounter();
|
||||
uint32_t end = start + us*USTIMER_PER_US;
|
||||
|
||||
bool overflow = (end<start);
|
||||
while(true) {
|
||||
uint32_t now = global::ustimer->getCounter();
|
||||
if((now >= end) && (!overflow || (now < start)))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep with yield, at least 10 ms, 10 ms granularity
|
||||
void sleep(unsigned ms) {
|
||||
// TODO: Proper implementation
|
||||
uint32_t start = global::ticktimer->getTicks();
|
||||
uint32_t end = start + ms/TICKTIMER_MS;
|
||||
|
||||
while(end > global::ticktimer->getTicks()) cortexa8::yield_svc();
|
||||
}
|
||||
Reference in New Issue
Block a user