mirror of https://github.com/inolen/redream.git
aica rtc implementation
This commit is contained in:
parent
3b1536a137
commit
49272b5de1
|
@ -1,5 +1,6 @@
|
|||
#include "hw/aica/aica.h"
|
||||
#include "core/log.h"
|
||||
#include "core/option.h"
|
||||
#include "hw/aica/aica_types.h"
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/dreamcast.h"
|
||||
|
@ -8,37 +9,59 @@
|
|||
#include "hw/scheduler.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
|
||||
DEFINE_OPTION_INT(rtc, 0, OPTION_HIDDEN);
|
||||
|
||||
#define AICA_CLOCK_FREQ INT64_C(22579200)
|
||||
#define AICA_SAMPLE_FREQ INT64_C(44100)
|
||||
#define AICA_NUM_CHANNELS 64
|
||||
#define AICA_TIMER_PERIOD 0xff
|
||||
|
||||
struct aica_channel {
|
||||
struct channel_data *data;
|
||||
};
|
||||
|
||||
struct aica {
|
||||
struct device;
|
||||
uint8_t *aica_regs;
|
||||
uint8_t *reg_ram;
|
||||
uint8_t *wave_ram;
|
||||
struct common_data *common;
|
||||
// timers
|
||||
struct timer *timers[3];
|
||||
// real-time clock
|
||||
struct timer *rtc_timer;
|
||||
int rtc_write;
|
||||
uint32_t rtc;
|
||||
// channels
|
||||
struct common_data *common_data;
|
||||
struct aica_channel channels[AICA_NUM_CHANNELS];
|
||||
};
|
||||
|
||||
/*
|
||||
* interrupts
|
||||
*/
|
||||
static void aica_raise_interrupt(struct aica *aica, int intr) {
|
||||
aica->common->SCIPD |= intr;
|
||||
aica->common_data->MCIPD |= intr;
|
||||
aica->common_data->SCIPD |= intr;
|
||||
}
|
||||
|
||||
static void aica_clear_interrupt(struct aica *aica, int intr) {
|
||||
aica->common->SCIPD &= ~intr;
|
||||
aica->common_data->MCIPD &= ~intr;
|
||||
aica->common_data->SCIPD &= ~intr;
|
||||
}
|
||||
|
||||
static void aica_update_arm(struct aica *aica) {}
|
||||
|
||||
static void aica_update_sh(struct aica *aica) {
|
||||
uint32_t enabled_intr = aica->common->MCIEB;
|
||||
uint32_t pending_intr = aica->common->MCIPD & enabled_intr;
|
||||
uint32_t enabled_intr = aica->common_data->MCIEB;
|
||||
uint32_t pending_intr = aica->common_data->MCIPD & enabled_intr;
|
||||
|
||||
if (pending_intr) {
|
||||
holly_toggle_interrupt(aica->holly, HOLLY_INTC_G2AICINT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* timers
|
||||
*/
|
||||
static void aica_timer_reschedule(struct aica *aica, int n, uint32_t period);
|
||||
|
||||
static void aica_timer_expire(struct aica *aica, int n) {
|
||||
|
@ -65,16 +88,16 @@ static void aica_timer_expire_2(void *data) {
|
|||
}
|
||||
|
||||
static uint32_t aica_timer_tctl(struct aica *aica, int n) {
|
||||
return n == 0 ? aica->common->TACTL : n == 1 ? aica->common->TBCTL
|
||||
: aica->common->TCCTL;
|
||||
return n == 0 ? aica->common_data->TACTL : n == 1 ? aica->common_data->TBCTL
|
||||
: aica->common_data->TCCTL;
|
||||
}
|
||||
|
||||
static uint32_t aica_timer_tcnt(struct aica *aica, int n) {
|
||||
struct timer *timer = aica->timers[n];
|
||||
if (!timer) {
|
||||
// if no timer has been created, return the raw value
|
||||
return n == 0 ? aica->common->TIMA : n == 1 ? aica->common->TIMB
|
||||
: aica->common->TIMC;
|
||||
return n == 0 ? aica->common_data->TIMA : n == 1 ? aica->common_data->TIMB
|
||||
: aica->common_data->TIMC;
|
||||
}
|
||||
|
||||
// else, dynamically compute the value based on the timer's remaining time
|
||||
|
@ -103,76 +126,196 @@ static void aica_timer_reschedule(struct aica *aica, int n, uint32_t period) {
|
|||
scheduler_start_timer(aica->scheduler, timer_cbs[n], aica, remaining);
|
||||
}
|
||||
|
||||
/*static uint32_t aica_channel_reg_read(struct aica *aica, uint32_t addr,
|
||||
uint32_t data_mask) {
|
||||
int ch = addr >> 7;
|
||||
static void aica_timer_shutdown(struct aica *aica) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
scheduler_cancel_timer(aica->scheduler, aica->timers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void aica_timer_init(struct aica *aica) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
aica_timer_reschedule(aica, i, AICA_TIMER_PERIOD);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rtc
|
||||
*/
|
||||
static uint32_t aica_rtc_reg_read(struct aica *aica, uint32_t addr,
|
||||
uint32_t data_mask) {
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
return aica->rtc >> 16;
|
||||
case 0x4:
|
||||
return aica->rtc & 0xffff;
|
||||
case 0x8:
|
||||
return 0;
|
||||
default:
|
||||
CHECK(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void aica_rtc_reg_write(struct aica *aica, uint32_t addr, uint32_t data,
|
||||
uint32_t data_mask) {
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
if (aica->rtc_write) {
|
||||
aica->rtc = (data << 16) | (aica->rtc & 0xffff);
|
||||
aica->rtc_write = 0;
|
||||
}
|
||||
break;
|
||||
case 0x4:
|
||||
if (aica->rtc_write) {
|
||||
aica->rtc = (aica->rtc & 0xffff0000) | (data & 0xffff);
|
||||
}
|
||||
break;
|
||||
case 0x8:
|
||||
aica->rtc_write = data & 1;
|
||||
break;
|
||||
default:
|
||||
CHECK(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void aica_rtc_timer(void *data) {
|
||||
struct aica *aica = data;
|
||||
aica->rtc++;
|
||||
aica->rtc_timer =
|
||||
scheduler_start_timer(aica->scheduler, &aica_rtc_timer, aica, NS_PER_SEC);
|
||||
}
|
||||
|
||||
static void aica_rtc_shutdown(struct aica *aica) {
|
||||
scheduler_cancel_timer(aica->scheduler, aica->rtc_timer);
|
||||
|
||||
// persist clock
|
||||
OPTION_rtc = *(int *)&aica->rtc;
|
||||
}
|
||||
|
||||
static void aica_rtc_init(struct aica *aica) {
|
||||
// seed clock from persistant options
|
||||
CHECK(sizeof(aica->rtc) <= sizeof(OPTION_rtc));
|
||||
aica->rtc = *(uint32_t *)&OPTION_rtc;
|
||||
|
||||
// increment clock every second
|
||||
aica->rtc_timer =
|
||||
scheduler_start_timer(aica->scheduler, &aica_rtc_timer, aica, NS_PER_SEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* channels
|
||||
*/
|
||||
static uint32_t aica_channel_reg_read(struct aica *aica, uint32_t addr,
|
||||
uint32_t data_mask) {
|
||||
int n = addr >> 7;
|
||||
addr &= 0x7f;
|
||||
return aica->aica_regs[(ch << 7) | addr];
|
||||
struct aica_channel *ch = &aica->channels[n];
|
||||
|
||||
return READ_DATA(&ch->data[addr]);
|
||||
}
|
||||
|
||||
static void aica_channel_reg_write(struct aica *aica, uint32_t addr,
|
||||
uint32_t data, uint32_t data_mask) {
|
||||
int ch = addr >> 7;
|
||||
int n = addr >> 7;
|
||||
addr &= 0x7f;
|
||||
struct aica_channel *ch = &aica->channels[n];
|
||||
|
||||
aica->aica_regs[(ch << 7) | addr] = data;
|
||||
WRITE_DATA(&ch->data[addr]);
|
||||
}
|
||||
|
||||
static uint32_t aica_common_reg_read(struct aica *aica, uint32_t addr, uint32_t
|
||||
data_mask) {
|
||||
return 0;
|
||||
static uint32_t aica_common_reg_read(struct aica *aica, uint32_t addr,
|
||||
uint32_t data_mask) {
|
||||
switch (addr) {
|
||||
case 0x90: /* TIMA */
|
||||
return (aica_timer_tctl(aica, 0) << 8) | aica_timer_tcnt(aica, 0);
|
||||
case 0x94: /* TIMB */
|
||||
return (aica_timer_tctl(aica, 1) << 8) | aica_timer_tcnt(aica, 1);
|
||||
case 0x98: /* TIMC */
|
||||
return (aica_timer_tctl(aica, 2) << 8) | aica_timer_tcnt(aica, 2);
|
||||
}
|
||||
return READ_DATA(&aica->reg_ram[0x2800 + addr]);
|
||||
}
|
||||
|
||||
static void aica_common_reg_write(struct aica *aica, uint32_t addr,
|
||||
uint32_t data, uint32_t data_mask) {}*/
|
||||
uint32_t data, uint32_t data_mask) {
|
||||
WRITE_DATA(&aica->reg_ram[addr]);
|
||||
|
||||
uint32_t aica_reg_read(struct aica *aica, uint32_t addr, uint32_t data_mask) {
|
||||
if (addr < 0x2000) { /* channel */
|
||||
} else if (addr >= 0x2800 && addr < 0x2d08 /* common */) {
|
||||
addr -= 0x2800;
|
||||
switch (addr) {
|
||||
case 0x90: /* TIMA */
|
||||
return (aica_timer_tctl(aica, 0) << 8) | aica_timer_tcnt(aica, 0);
|
||||
case 0x94: /* TIMB */
|
||||
return (aica_timer_tctl(aica, 1) << 8) | aica_timer_tcnt(aica, 1);
|
||||
case 0x98: /* TIMC */
|
||||
return (aica_timer_tctl(aica, 2) << 8) | aica_timer_tcnt(aica, 2);
|
||||
}
|
||||
return READ_DATA(&aica->aica_regs[0x2800 + addr]);
|
||||
switch (addr) {
|
||||
case 0x90: { /* TIMA */
|
||||
aica_timer_reschedule(aica, 0,
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 0));
|
||||
} break;
|
||||
case 0x94: { /* TIMB */
|
||||
aica_timer_reschedule(aica, 1,
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 1));
|
||||
} break;
|
||||
case 0x98: { /* TIMC */
|
||||
aica_timer_reschedule(aica, 2,
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 2));
|
||||
} break;
|
||||
case 0x9c:
|
||||
case 0x9d:
|
||||
case 0xa0:
|
||||
case 0xa1: { /* SCIEB, SCIPD */
|
||||
aica_update_arm(aica);
|
||||
} break;
|
||||
case 0xa4:
|
||||
case 0xa5: { /* SCIRE */
|
||||
aica->common_data->SCIPD &= ~aica->common_data->SCIRE;
|
||||
aica->common_data->SCIRE = 0;
|
||||
aica_update_arm(aica);
|
||||
} break;
|
||||
case 0xb4:
|
||||
case 0xb5:
|
||||
case 0xb8:
|
||||
case 0xb9: { /* MCIEB, MCIPD */
|
||||
aica_update_sh(aica);
|
||||
} break;
|
||||
case 0xbc:
|
||||
case 0xbd: { /* MCIRE */
|
||||
aica->common_data->MCIPD &= ~aica->common_data->MCIRE;
|
||||
aica->common_data->MCIRE = 0;
|
||||
aica_update_sh(aica);
|
||||
} break;
|
||||
case 0x400: { /* ARMRST */
|
||||
if (data) {
|
||||
arm_suspend(aica->arm);
|
||||
} else {
|
||||
arm_resume(aica->arm);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return READ_DATA(&aica->aica_regs[addr]);
|
||||
/*
|
||||
* memory callbacks
|
||||
*/
|
||||
uint32_t aica_reg_read(struct aica *aica, uint32_t addr, uint32_t data_mask) {
|
||||
if (addr < 0x2000) {
|
||||
return aica_channel_reg_read(aica, addr, data_mask);
|
||||
} else if (addr >= 0x2800 && addr < 0x2d08) {
|
||||
return aica_common_reg_read(aica, addr - 0x2800, data_mask);
|
||||
} else if (addr >= 0x10000 && addr < 0x1000c) {
|
||||
return aica_rtc_reg_read(aica, addr - 0x10000, data_mask);
|
||||
}
|
||||
return READ_DATA(&aica->reg_ram[addr]);
|
||||
}
|
||||
|
||||
void aica_reg_write(struct aica *aica, uint32_t addr, uint32_t data,
|
||||
uint32_t data_mask) {
|
||||
WRITE_DATA(&aica->aica_regs[addr]);
|
||||
|
||||
if (addr < 0x2000) { /* channel */
|
||||
} else if (addr >= 0x2800 && addr < 0x2d08 /* common */) {
|
||||
addr -= 0x2800;
|
||||
switch (addr) {
|
||||
case 0x90: { /* TIMA */
|
||||
aica_timer_reschedule(aica, 0,
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 0));
|
||||
} break;
|
||||
case 0x94: { /* TIMB */
|
||||
aica_timer_reschedule(aica, 1,
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 1));
|
||||
} break;
|
||||
case 0x98: { /* TIMC */
|
||||
aica_timer_reschedule(aica, 2,
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 2));
|
||||
} break;
|
||||
case 0x400: { /* ARMRST */
|
||||
if (data) {
|
||||
arm_suspend(aica->arm);
|
||||
} else {
|
||||
arm_resume(aica->arm);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
if (addr < 0x2000) {
|
||||
aica_channel_reg_write(aica, addr, data, data_mask);
|
||||
return;
|
||||
} else if (addr >= 0x2800 && addr < 0x2d08) {
|
||||
aica_common_reg_write(aica, addr - 0x2800, data, data_mask);
|
||||
return;
|
||||
} else if (addr >= 0x10000 && addr < 0x1000c) {
|
||||
aica_rtc_reg_write(aica, addr - 0x10000, data, data_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
WRITE_DATA(&aica->reg_ram[addr]);
|
||||
}
|
||||
|
||||
uint32_t aica_wave_read(struct aica *aica, uint32_t addr, uint32_t data_mask) {
|
||||
|
@ -206,14 +349,14 @@ void aica_wave_write(struct aica *aica, uint32_t addr, uint32_t data,
|
|||
WRITE_DATA(&aica->wave_ram[addr]);
|
||||
}
|
||||
|
||||
/*
|
||||
* device
|
||||
*/
|
||||
static void aica_run(struct device *dev, int64_t ns) {
|
||||
struct aica *aica = (struct aica *)dev;
|
||||
|
||||
int64_t cycles = MAX(NANO_TO_CYCLES(ns, AICA_CLOCK_FREQ), INT64_C(1));
|
||||
|
||||
while (cycles > 0) {
|
||||
cycles--;
|
||||
}
|
||||
// int64_t cycles = MAX(NANO_TO_CYCLES(ns, AICA_CLOCK_FREQ), INT64_C(1));
|
||||
// int64_t samples = NANO_TO_CYCLES(ns, AICA_SAMPLE_FREQ);
|
||||
|
||||
aica_raise_interrupt(aica, AICA_INT_SAMPLE);
|
||||
|
||||
|
@ -224,15 +367,20 @@ static void aica_run(struct device *dev, int64_t ns) {
|
|||
static bool aica_init(struct device *dev) {
|
||||
struct aica *aica = (struct aica *)dev;
|
||||
|
||||
aica->aica_regs = memory_translate(aica->memory, "aica reg ram", 0x00000000);
|
||||
aica->reg_ram = memory_translate(aica->memory, "aica reg ram", 0x00000000);
|
||||
aica->wave_ram = memory_translate(aica->memory, "aica wave ram", 0x00000000);
|
||||
aica->common = (struct common_data *)(aica->aica_regs + 0x2800);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
aica_timer_reschedule(aica, i, AICA_TIMER_PERIOD);
|
||||
// setup channel data aliases
|
||||
for (int i = 0; i < AICA_NUM_CHANNELS; i++) {
|
||||
struct aica_channel *ch = &aica->channels[i];
|
||||
ch->data = (struct channel_data *)(aica->reg_ram +
|
||||
sizeof(struct channel_data) * i);
|
||||
}
|
||||
aica->common_data = (struct common_data *)(aica->reg_ram + 0x2800);
|
||||
|
||||
aica_timer_init(aica);
|
||||
aica_rtc_init(aica);
|
||||
|
||||
// arm cpu is initially suspended
|
||||
arm_suspend(aica->arm);
|
||||
|
||||
return true;
|
||||
|
@ -248,6 +396,9 @@ struct aica *aica_create(struct dreamcast *dc) {
|
|||
}
|
||||
|
||||
void aica_destroy(struct aica *aica) {
|
||||
aica_rtc_shutdown(aica);
|
||||
aica_timer_shutdown(aica);
|
||||
|
||||
dc_destroy_execute_interface(aica->execute_if);
|
||||
dc_destroy_device((struct device *)aica);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define AICA_INT_SAMPLE 0x400
|
||||
|
||||
struct channel_data {
|
||||
// 0x0
|
||||
uint32_t SA_hi : 7;
|
||||
uint32_t PCMS : 2;
|
||||
uint32_t LPCTL : 1;
|
||||
|
@ -26,21 +27,26 @@ struct channel_data {
|
|||
uint32_t KYONEX : 1;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x4
|
||||
uint32_t SA_lo : 16;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x8
|
||||
uint32_t LSA : 16;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0xc
|
||||
uint32_t LEA : 16;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x10
|
||||
uint32_t AR : 5;
|
||||
uint32_t : 1;
|
||||
uint32_t D1R : 5;
|
||||
uint32_t D2R : 5;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x14
|
||||
uint32_t RR : 5;
|
||||
uint32_t DL : 5;
|
||||
uint32_t KRS : 4;
|
||||
|
@ -48,12 +54,14 @@ struct channel_data {
|
|||
uint32_t : 1;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x18
|
||||
uint32_t FNS : 10;
|
||||
uint32_t : 1;
|
||||
uint32_t OCT : 4;
|
||||
uint32_t : 1;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x1c
|
||||
uint32_t ALFOS : 3;
|
||||
uint32_t ALFOWS : 2;
|
||||
uint32_t PLFOS : 3;
|
||||
|
@ -62,48 +70,58 @@ struct channel_data {
|
|||
uint32_t LFORE : 1;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x20
|
||||
uint32_t ISEL : 4;
|
||||
uint32_t IMXL : 4;
|
||||
uint32_t : 8;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x24
|
||||
uint32_t DIPAN : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t DISDL : 4;
|
||||
uint32_t : 4;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x28
|
||||
uint32_t Q : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t TL : 8;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x2c
|
||||
uint32_t FLV0 : 13;
|
||||
uint32_t : 3;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x30
|
||||
uint32_t FLV1 : 13;
|
||||
uint32_t : 3;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x34
|
||||
uint32_t FLV2 : 13;
|
||||
uint32_t : 3;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x38
|
||||
uint32_t FLV3 : 13;
|
||||
uint32_t : 3;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x3c
|
||||
uint32_t FLV4 : 13;
|
||||
uint32_t : 3;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x40
|
||||
uint32_t FD1R : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t FAR : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t : 16;
|
||||
|
||||
// 0x44
|
||||
uint32_t FRR : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t FD2R : 5;
|
||||
|
|
|
@ -199,7 +199,8 @@ static bool memory_create_shmem(struct memory *memory) {
|
|||
}
|
||||
|
||||
static void memory_destroy_shmem(struct memory *memory) {
|
||||
CHECK(unmap_shared_memory(memory->shmem, memory->shmem_base, memory->shmem_size));
|
||||
CHECK(unmap_shared_memory(memory->shmem, memory->shmem_base,
|
||||
memory->shmem_size));
|
||||
destroy_shared_memory(memory->shmem);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,8 @@ static void pvr_reconfigure_spg(struct pvr *pvr) {
|
|||
}
|
||||
|
||||
LOG_INFO(
|
||||
"ReconfigureSPG: pixel_clock %d, line_clock %d, vcount %d, hcount %d, "
|
||||
"pvr_reconfigure_spg: pixel_clock %d, line_clock %d, vcount %d, hcount "
|
||||
"%d, "
|
||||
"interlace %d, vbstart %d, vbend %d",
|
||||
pixel_clock, pvr->line_clock, pvr->SPG_LOAD->vcount,
|
||||
pvr->SPG_LOAD->hcount, pvr->SPG_CONTROL->interlace,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "core/option.h"
|
||||
#include "hw/dreamcast.h"
|
||||
|
||||
DEFINE_OPTION_STRING(bios, "dc_boot.bin", "Path to BIOS");
|
||||
DEFINE_OPTION_STRING(bios, "dc_boot.bin", "Path to boot rom");
|
||||
|
||||
#define BIOS_SIZE 0x00200000
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "hw/dreamcast.h"
|
||||
#include "sys/filesystem.h"
|
||||
|
||||
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash ROM");
|
||||
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash rom");
|
||||
|
||||
// there doesn't seem to be any documentation on the flash rom used by thae
|
||||
// dreamcast. however, several people have replaced it with the MX29LV160TMC-90
|
||||
|
@ -67,7 +67,8 @@ static int flash_save_rom(struct flash *flash, const char *path) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t flash_cmd_read(struct flash *flash, uint32_t addr, uint32_t data_mask) {
|
||||
static uint32_t flash_cmd_read(struct flash *flash, uint32_t addr,
|
||||
uint32_t data_mask) {
|
||||
uint32_t *mem = (uint32_t *)&flash->rom[addr];
|
||||
return *mem & data_mask;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue